]> git.plutz.net Git - quickjs_net/commitdiff
improved timeout handling
authorPaul Hänsch <paul@plutz.net>
Sun, 15 Mar 2026 23:53:28 +0000 (00:53 +0100)
committerPaul Hänsch <paul@plutz.net>
Sun, 15 Mar 2026 23:53:28 +0000 (00:53 +0100)
socket.c

index 31ad4cb60d8b64824e6d700e24ad343ceedea692..cca14f4888a2e7dbde5a24804c0e8ec965a3e355 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -134,59 +134,58 @@ typedef struct {
   struct sockaddr_storage peer;
 } SocketData;
 
-static JSValue sock_set_timeout( JSContext *ctx, JSValueConst this, JSValueConst val ) {
-  SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
-  struct timeval t; double delay;
-
-  if (!JS_IsNumber(val)
-    || JS_ToFloat64(ctx, &delay, val) || delay < 0) {
-    t.tv_sec = 0; t.tv_usec = 0;
-    delay = -1;
-  } else {
-    t.tv_sec = (int) delay;
-    t.tv_usec = (int) ((delay - t.tv_sec) * 1000000);
-  }
-
-  if ( setsockopt(data->fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t))
-    || setsockopt(data->fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t))
-  ) return libc_error(ctx);
-
-  data->timeout = delay;
-
-  if ( delay == 0
-    && fcntl(data->fd, F_SETFL, O_NONBLOCK)
-  ) return libc_error(ctx);
-  else if ( delay != 0
-    && fcntl(data->fd, F_SETFL, 0)
-  ) return libc_error(ctx);
+static int sock_set_timeout(SocketData *so, double timeout) {
+  struct timeval t = { .tv_sec = 0, .tv_usec = 0 }; int flags = 0;
+
+  if ( timeout == 0 ) flags = O_NONBLOCK;
+  else if ( timeout > 0 ) {
+    t.tv_sec = (int) timeout;
+    t.tv_usec = (int) ((timeout - t.tv_sec) * 1000000);
+  } else timeout = -1;
+
+  if ( !setsockopt(so->fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t))
+    && !setsockopt(so->fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t))
+    && !fcntl(so->fd, F_SETFL, flags)
+  ) {
+    so->timeout = timeout;
+    return 0;
+  } else return 1;
+}
 
+static JSValue js_sock_set_timeout(
+  JSContext *ctx, JSValueConst this, JSValueConst val
+) {
+  double timeout;
+  SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
+  if (!JS_IsNumber(val) || JS_ToFloat64(ctx, &timeout, val)) timeout = -1;
 
-  return JS_UNDEFINED;
+  if (! sock_set_timeout(data, timeout) ) return JS_UNDEFINED;
+  else return libc_error(ctx);
 }
-static JSValue sock_get_timeout( JSContext *ctx, JSValueConst this ) {
+static JSValue js_sock_get_timeout( JSContext *ctx, JSValueConst this ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   if ( data->timeout == -1 ) return JS_UNDEFINED;
   else return JS_NewFloat64(ctx, data->timeout);
 }
 
-static JSValue sock_get_fd( JSContext *ctx, JSValueConst this ) {
+static JSValue js_sock_get_fd( JSContext *ctx, JSValueConst this ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   return JS_NewInt32(ctx, data->fd);
 }
-static JSValue sock_get_family( JSContext *ctx, JSValueConst this ) {
+static JSValue js_sock_get_family( JSContext *ctx, JSValueConst this ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   return JS_NewInt32(ctx, data->bind.ss_family);
 }
-static JSValue sock_get_type( JSContext *ctx, JSValueConst this ) {
+static JSValue js_sock_get_type( JSContext *ctx, JSValueConst this ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   return JS_NewInt32(ctx, data->type);
 }
-static JSValue sock_get_connected( JSContext *ctx, JSValueConst this ) {
+static JSValue js_sock_get_connected( JSContext *ctx, JSValueConst this ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   return JS_NewBool(ctx, data->connected);
 }
 
-static JSValue sock_accept(
+static JSValue js_sock_accept(
   JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
 ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
@@ -202,9 +201,7 @@ static JSValue sock_accept(
     memcpy(&(newdata->bind), &(data->bind), sizeof(data->bind));
     newdata->connected = 1;
     JS_SetOpaque(new, newdata);
-    if (data->timeout >= 0)
-      (void) sock_set_timeout(ctx, new, JS_NewFloat64(ctx, data->timeout));
-    else newdata->timeout = -1;
+    (void) sock_set_timeout(newdata, data->timeout);
     return new;
 
   } else {
@@ -215,7 +212,7 @@ static JSValue sock_accept(
     else return libc_error(ctx);
   }
 }
-static JSValue sock_send(
+static JSValue js_sock_send(
   JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
 ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
@@ -243,8 +240,6 @@ static JSValue sock_send(
   // destination should not be given for SOCK_STREAM sockets
   } else to = NULL;
 
-  if (data->timeout == 0) flags |= MSG_DONTWAIT;
-
   // Read Argument either as ArrayBuffer or String
   if ( (buf = JS_GetArrayBuffer(ctx, &len, argv[0])) ) {
     len = sendto(data->fd, buf, len, flags, to, to ? sizeof(*to) : 0);
@@ -268,11 +263,11 @@ static void js_array_buffer_free(JSRuntime *rt, void *junk, void *buf) {
   js_free_rt(rt, buf);
 }
 
-static JSValue sock_recv(
+static JSValue js_sock_recv(
   JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int str
 ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
-  uint8_t *buf; int len; JSValue jbuf; int flags = 0;
+  uint8_t *buf; int len; JSValue jbuf;
   socklen_t ps = sizeof(data->peer);
 
   // check current queue size if no valid length was given
@@ -282,10 +277,9 @@ static JSValue sock_recv(
   ) return libc_error(ctx);
 
   if (len <= 0) len = 65535;  // if no data is queued, wait for next packet
-  if (data->timeout == 0) flags |= MSG_DONTWAIT;
 
   if ( ( buf = js_malloc(ctx, len) )
-    && ((len = recvfrom(data->fd, buf, len, flags,
+    && ((len = recvfrom(data->fd, buf, len, 0,
          (struct sockaddr *) &(data->peer), &ps)) >= 0)
   ) {
     // resize/clear buffer in case of short read,
@@ -311,7 +305,7 @@ static JSValue sock_recv(
   }
 }
 
-static JSValue sock_get_addr( JSContext *ctx, JSValueConst this, int magic ) {
+static JSValue js_sock_get_addr( JSContext *ctx, JSValueConst this, int magic ) {
   SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
   struct sockaddr_storage * addr = (magic&2) ? &(data->peer) : &(data->bind);
   char host[40];
@@ -338,7 +332,7 @@ static JSValue sock_get_addr( JSContext *ctx, JSValueConst this, int magic ) {
   } else return JS_UNDEFINED;
 }
 
-static JSValue sock_close(
+static JSValue js_sock_close(
   JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
 ) {
   SocketData *data = JS_GetOpaque2(ctx, this, socket_cid);
@@ -357,20 +351,20 @@ static void sock_destroy(JSRuntime *rt, JSValue this){
 };
 
 static const JSCFunctionListEntry socket_ptype[] = {
-  JS_CFUNC_DEF("accept", 0, sock_accept),
-  JS_CFUNC_DEF("send",   1, sock_send),
-  JS_CFUNC_DEF("close",  0, sock_close),
-  JS_CFUNC_MAGIC_DEF("recv",       0, sock_recv, 0),
-  JS_CFUNC_MAGIC_DEF("recvString", 0, sock_recv, 1),
-  JS_CGETSET_MAGIC_DEF("localName", sock_get_addr, NULL, 0),
-  JS_CGETSET_MAGIC_DEF("localPort", sock_get_addr, NULL, 1),
-  JS_CGETSET_MAGIC_DEF("peerName",  sock_get_addr, NULL, 2),
-  JS_CGETSET_MAGIC_DEF("peerPort",  sock_get_addr, NULL, 3),
-  JS_CGETSET_DEF("timeout", sock_get_timeout, sock_set_timeout),
-  JS_CGETSET_DEF("fd",      sock_get_fd, NULL),
-  JS_CGETSET_DEF("family",  sock_get_family, NULL),
-  JS_CGETSET_DEF("type",    sock_get_type, NULL),
-  JS_CGETSET_DEF("connected",    sock_get_connected, NULL),
+  JS_CFUNC_DEF("accept", 0, js_sock_accept),
+  JS_CFUNC_DEF("send",   1, js_sock_send),
+  JS_CFUNC_DEF("close",  0, js_sock_close),
+  JS_CFUNC_MAGIC_DEF("recv",       0, js_sock_recv, 0),
+  JS_CFUNC_MAGIC_DEF("recvString", 0, js_sock_recv, 1),
+  JS_CGETSET_MAGIC_DEF("localName", js_sock_get_addr, NULL, 0),
+  JS_CGETSET_MAGIC_DEF("localPort", js_sock_get_addr, NULL, 1),
+  JS_CGETSET_MAGIC_DEF("peerName",  js_sock_get_addr, NULL, 2),
+  JS_CGETSET_MAGIC_DEF("peerPort",  js_sock_get_addr, NULL, 3),
+  JS_CGETSET_DEF("timeout", js_sock_get_timeout, js_sock_set_timeout),
+  JS_CGETSET_DEF("fd",      js_sock_get_fd, NULL),
+  JS_CGETSET_DEF("family",  js_sock_get_family, NULL),
+  JS_CGETSET_DEF("type",    js_sock_get_type, NULL),
+  JS_CGETSET_DEF("connected",    js_sock_get_connected, NULL),
 };
 
 static JSClassDef socket_class = {
@@ -397,10 +391,10 @@ static int net_ip_listen(SocketData *so, int type) {
 
   if ( (so->fd = socket(so->bind.ss_family, type, 0)) >= 0
     && !bind(so->fd, (struct sockaddr *) &(so->bind), sizeof(so->bind))
+    && !sock_set_timeout(so, net_timeout)
     && (type == SOCK_DGRAM || !listen(so->fd, 1))
   ){
     so->type = type;
-    so->timeout = -1;
     return 0;
   } else {
     if (so->fd >= 0) close(so->fd);
@@ -442,11 +436,11 @@ static int net_ip_connect(SocketData *so, int type) {
   if (!so) return 1;
 
   if ( (so->fd = socket(so->peer.ss_family, type, 0)) >= 0
+    && !sock_set_timeout(so, net_timeout)
     && !connect(so->fd, (struct sockaddr *) &(so->peer), ps)
     && !getsockname(so->fd, (struct sockaddr *) &(so->bind), &ps)
   ){
     so->type = type;
-    so->timeout = -1;
     so->connected = 1;
     return 0;
   } else {
@@ -486,20 +480,26 @@ static JSValue js_net_ip_connect(
 
 static int net_unix_bind(SocketData *so, const char *path, size_t plen, int con) {
   struct sockaddr_un * addr;
-  if (!so) return 1;
+  if (!so || plen >= sizeof(addr->sun_path)) return 1;
 
   addr = (struct sockaddr_un *) (con ? &(so->peer) : &(so->bind));
   addr->sun_family = AF_UNIX;
-  if (plen < sizeof(addr->sun_path)) strncpy(addr->sun_path, path, plen);
+  strncpy(addr->sun_path, path, plen);
+  so->type = SOCK_STREAM;
 
-  if ( (so->fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0
-    && !( con ? connect(so->fd,(struct sockaddr *) addr ,sizeof(*addr))
-            :      bind(so->fd,(struct sockaddr *) addr ,sizeof(*addr))
-    ) && (con || !listen(so->fd, 1))
+  if ( con
+    && (so->fd = socket(AF_UNIX, so->type, 0)) >= 0
+    && !sock_set_timeout(so, net_timeout)
+    && !connect(so->fd,(struct sockaddr *) addr ,sizeof(*addr))
+  ) {
+    so->connected = 1;
+    return 0;
+  } else if (!con
+    && (so->fd = socket(AF_UNIX, so->type, 0)) >= 0
+    && !bind(so->fd,(struct sockaddr *) addr ,sizeof(*addr))
+    && !sock_set_timeout(so, net_timeout)
+    && !listen(so->fd, 1)
   ) {
-    so->type = SOCK_STREAM;
-    so->connected = con;
-    so->timeout = -1;
     return 0;
   } else {
     if (so->fd >= 0) close(so->fd);
@@ -556,16 +556,16 @@ static const JSCFunctionListEntry net_funcs[] = {
   JS_PROP_INT32_DEF("AF_INET6",    AF_INET6,    JS_PROP_CONFIGURABLE),
   JS_PROP_INT32_DEF("SOCK_STREAM", SOCK_STREAM, JS_PROP_CONFIGURABLE),
   JS_PROP_INT32_DEF("SOCK_DGRAM",  SOCK_DGRAM,  JS_PROP_CONFIGURABLE),
-  JS_CFUNC_MAGIC_DEF("udpListen",   2, js_net_ip_listen, 0),
-  JS_CFUNC_MAGIC_DEF("tcpListen",   2, js_net_ip_listen, 1),
-  JS_CFUNC_MAGIC_DEF("udp6Listen",  2, js_net_ip_listen, 2),
-  JS_CFUNC_MAGIC_DEF("tcp6Listen",  2, js_net_ip_listen, 3),
+  JS_CFUNC_MAGIC_DEF("udpListen",   2, js_net_ip_listen,  0),
+  JS_CFUNC_MAGIC_DEF("tcpListen",   2, js_net_ip_listen,  1),
+  JS_CFUNC_MAGIC_DEF("udp6Listen",  2, js_net_ip_listen,  2),
+  JS_CFUNC_MAGIC_DEF("tcp6Listen",  2, js_net_ip_listen,  3),
   JS_CFUNC_MAGIC_DEF("udpConnect",  2, js_net_ip_connect, 0),
   JS_CFUNC_MAGIC_DEF("tcpConnect",  2, js_net_ip_connect, 1),
   JS_CFUNC_MAGIC_DEF("udp6Connect", 2, js_net_ip_connect, 2),
   JS_CFUNC_MAGIC_DEF("tcp6Connect", 2, js_net_ip_connect, 3),
-  JS_CFUNC_MAGIC_DEF("unixListen",  1, js_net_unix_bind, 0),
-  JS_CFUNC_MAGIC_DEF("unixConnect", 1, js_net_unix_bind, 1),
+  JS_CFUNC_MAGIC_DEF("unixListen",  1, js_net_unix_bind,  0),
+  JS_CFUNC_MAGIC_DEF("unixConnect", 1, js_net_unix_bind,  1),
 
   // XXX: How does one use a get/set property in a module provided object?
   // JS_CGETSET_DEF("timeout", net_get_timeout, net_set_timeout),
@@ -573,10 +573,6 @@ static const JSCFunctionListEntry net_funcs[] = {
   JS_CFUNC_DEF("getConnectTimeout", 1, net_get_timeout),
 };
 
-// static const JSCFunctionListEntry net_obj[] = {
-//   JS_OBJECT_DEF("net", net_funcs, countof(net_funcs), JS_PROP_CONFIGURABLE)
-// };
-
 static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) {
   JSValue proto = JS_NewObject(ctx);
 
@@ -596,7 +592,6 @@ JSModuleDef *js_init_module(JSContext *ctx, const char *name) {
 
   if (mod) {
     JS_AddModuleExportList(ctx, mod, net_funcs, countof(net_funcs));
-    // JS_SetPropertyFunctionList(ctx, global, net_obj, countof(net_obj));
     if ( !JS_IsUndefined(os) )
       JS_SetPropertyFunctionList(ctx, os, os_socket_funcs,
                                   countof(os_socket_funcs));