uWebSockets.js icon indicating copy to clipboard operation
uWebSockets.js copied to clipboard

add binding for getRemotePort

Open Shachar-Brightdata opened this issue 9 months ago • 4 comments

Hi, I've a patch on uWebSocket.js and uWebSocket that exposes the us_socket_remote_port api and the adds binding for it with getRemotePort is this something you'll be willing to merge on the main branch, I thought i'ts better to ask before making pull requests the repositories.

I'm in the process of updating from an older version of uWebsocket.js and in my use case uses the remote port to generate a unique id for the connection, something that was possible in older versions.

Shachar-Brightdata avatar Mar 27 '25 09:03 Shachar-Brightdata

Attached changes diff for your review:

uWebSockets.diff:

index 5b6c647..3c991f9 100644
--- a/src/AsyncSocket.h
+++ b/src/AsyncSocket.h
@@ -230,6 +230,12 @@ protected:
         return addressAsText(getRemoteAddress());
     }
 
+    /* Returns the remote port number or -1 on failure */
+    unsigned int getRemotePort() {
+        int port = us_socket_remote_port(SSL, (us_socket_t *) this);
+        return (unsigned int) port;
+    }
+
     /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible.
      * Returns pair of bytes written (anywhere) and whether or not this call resulted in the polling for
      * writable (or we are in a state that implies polling for writable). */
diff --git a/src/HttpResponse.h b/src/HttpResponse.h
index e73da46..b90a90c 100644
--- a/src/HttpResponse.h
+++ b/src/HttpResponse.h
@@ -229,6 +229,10 @@ public:
     std::string_view getProxiedRemoteAddressAsText() {
         return Super::addressAsText(getProxiedRemoteAddress());
     }
+
+    unsigned int getProxiedRemotePort() {
+        return getHttpResponseData()->proxyParser.getSourcePort();
+    }
 #endif
 
     /* Manually upgrade to WebSocket. Typically called in upgrade handler. Immediately calls open handler.
@@ -355,6 +359,7 @@ public:
     /* See AsyncSocket */
     using Super::getRemoteAddress;
     using Super::getRemoteAddressAsText;
+    using Super::getRemotePort;
     using Super::getNativeHandle;
 
     /* Throttle reads and writes */
diff --git a/src/ProxyParser.h b/src/ProxyParser.h
index 95ee3d1..30acef1 100644
--- a/src/ProxyParser.h
+++ b/src/ProxyParser.h
@@ -91,6 +91,22 @@ public:
         }
     }
 
+    unsigned int getSourcePort() {
+
+        // UNSPEC family and protocol
+        if (family == 0) {
+            return {};
+        }
+
+        if ((family & 0xf0) >> 4 == 1) {
+            /* Family 1 is INET4 */
+            return addr.ipv4_addr.src_port;
+        } else {
+            /* Family 2 is INET6 */
+            return addr.ipv6_addr.src_port;
+        }
+    }
+
     /* Returns [done, consumed] where done = false on failure */
     std::pair<bool, unsigned int> parse(std::string_view data) {
 
diff --git a/src/WebSocket.h b/src/WebSocket.h
index 199e2f4..0aea72d 100644
--- a/src/WebSocket.h
+++ b/src/WebSocket.h
@@ -58,6 +51,7 @@ public:
     using Super::getBufferedAmount;
     using Super::getRemoteAddress;
     using Super::getRemoteAddressAsText;
+    using Super::getRemotePort;
     using Super::getNativeHandle;
 
     /* WebSocket close cannot be an alias to AsyncSocket::close since

uWebSockets.js.diff:

index 5199f60..4a9b72a 100644
--- a/docs/index.d.ts
+++ b/docs/index.d.ts
@@ -104,6 +104,9 @@ export interface WebSocket<UserData> {
     /** Returns the remote IP address as text. See RecognizedString. */
     getRemoteAddressAsText() : ArrayBuffer;
 
+    /** Returns the remote port number. */
+    getRemotePort() : number;
+
     /** Returns the UserData object. */
     getUserData() : UserData;
 }
@@ -172,12 +175,18 @@ export interface HttpResponse {
     /** Returns the remote IP address as text. */
     getRemoteAddressAsText() : ArrayBuffer;
 
+    /** Returns the remote port number. */
+    getRemotePort() : number;
+
     /** Returns the remote IP address in binary format (4 or 16 bytes), as reported by the PROXY Protocol v2 compatible proxy. */
     getProxiedRemoteAddress() : ArrayBuffer;
 
     /** Returns the remote IP address as text, as reported by the PROXY Protocol v2 compatible proxy. */
     getProxiedRemoteAddressAsText() : ArrayBuffer;
 
+    /** Returns the remote port number, as reported by the PROXY Protocol v2 compatible proxy. */
+    getProxiedRemotePort() : number;
+
     /** Corking a response is a performance improvement in both CPU and network, as you ready the IO system for writing multiple chunks at once.
      * By default, you're corked in the immediately executing top portion of the route handler. In all other cases, such as when returning from
      * await, or when being called back from an async database request or anything that isn't directly executing in the route handler, you'll want
diff --git a/src/HttpResponseWrapper.h b/src/HttpResponseWrapper.h
index 340f5be..23ea452 100644
--- a/src/HttpResponseWrapper.h
+++ b/src/HttpResponseWrapper.h
@@ -160,6 +160,18 @@ struct HttpResponseWrapper {
         }
     }
 
+    /* Takes nothing, returns integer */
+    template <int SSL>
+    static void res_getRemotePort(const FunctionCallbackInfo<Value> &args) {
+        Isolate *isolate = args.GetIsolate();
+        auto *res = getHttpResponse<SSL>(args);
+        if (res) {
+            unsigned int port = res->getRemotePort();
+
+            args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, port));
+        }
+    }
+
     /* Takes nothing, returns arraybuffer */
     template <int SSL>
     static void res_getProxiedRemoteAddress(const FunctionCallbackInfo<Value> &args) {
@@ -184,6 +196,18 @@ struct HttpResponseWrapper {
         }
     }
 
+    /* Takes nothing, returns number */
+    template <int SSL>
+    static void res_getProxiedRemotePort(const FunctionCallbackInfo<Value> &args) {
+        Isolate *isolate = args.GetIsolate();
+        auto *res = getHttpResponse<SSL>(args);
+        if (res) {
+            unsigned int port = res->getProxiedRemotePort();
+
+            args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, port));
+        }
+    }
+
     template <int PROTOCOL>
     static void res_getX509Certificate(const FunctionCallbackInfo<Value> &args) {
         Isolate *isolate = args.GetIsolate();
@@ -472,8 +496,10 @@ struct HttpResponseWrapper {
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "collect", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "upgrade", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_upgrade<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddressAsText<SSL>));
+                resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemotePort<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddress<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddressAsText<SSL>));
+                resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemotePort<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "pause", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_pause<SSL>));
                 resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_resume<SSL>));
             }
diff --git a/src/WebSocketWrapper.h b/src/WebSocketWrapper.h
index f476bd7..47d4c97 100644
--- a/src/WebSocketWrapper.h
+++ b/src/WebSocketWrapper.h
@@ -159,6 +159,17 @@ struct WebSocketWrapper {
         }
     }
 
+    /* Takes nothing, returns integer */
+    template <bool SSL>
+    static void uWS_WebSocket_getRemotePort(const FunctionCallbackInfo<Value> &args) {
+        Isolate *isolate = args.GetIsolate();
+        auto *ws = getWebSocket<SSL>(args);
+        if (ws) {
+            unsigned int port = ws->getRemotePort();
+            args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, port));
+        }
+    }
+
     /* Takes nothing, returns integer */
     template <bool SSL>
     static void uWS_WebSocket_getBufferedAmount(const FunctionCallbackInfo<Value> &args) {
@@ -335,6 +346,7 @@ struct WebSocketWrapper {
         wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_cork<SSL>));
         wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping<SSL>));
         wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddressAsText<SSL>));
+        wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemotePort<SSL>));
         wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "isSubscribed", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_isSubscribed<SSL>));
 
         /* This one does not exist in C++ */

Shachar-Brightdata avatar Apr 07 '25 10:04 Shachar-Brightdata

It looks fine but

return getHttpResponseData()->proxyParser.getSourcePort();

needs to be within macros for the proxy support.

Also why do you have a diff for getSourcePort?

uNetworkingAB avatar Apr 08 '25 00:04 uNetworkingAB

return getHttpResponseData()->proxyParser.getSourcePort(); needs to be within macros for the proxy support.

The function getProxiedRemotePort is within the UWS_WITH_PROXY define scope

Also why do you have a diff for getSourcePort?

Called from getProxiedRemotePort to expose proxy port

tbf, the proxy part isn't required for what I'm doing, but I thought that if I'm already adding remote port in one place it should be in the others as well

Shachar-Brightdata avatar Apr 10 '25 07:04 Shachar-Brightdata

See uWebSockets PR: uNetworking/uWebSockets#1854

See uWebSockets.js PR: #1162

Shachar-Brightdata avatar Apr 14 '25 07:04 Shachar-Brightdata