From: Paul Hänsch Date: Thu, 19 Mar 2026 22:47:14 +0000 (+0100) Subject: refactoring: use constructor for socket object X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=611e36fec1d0dadc02172a24e64820e5a13be677;p=quickjs_net refactoring: use constructor for socket object --- diff --git a/socket.c b/socket.c index d4bdff6..84cc20b 100644 --- a/socket.c +++ b/socket.c @@ -21,7 +21,6 @@ static JSValue js_os_fork( } static JSClassID socket_cid; -static double net_timeout = -1; typedef struct { int fd; @@ -65,19 +64,52 @@ static int net_addrinfo( } static int net_ip_listen(SocketData *so, int type) { + int o = 1; if (!so) return 1; - if ( (so->fd = socket(so->bind.ss_family, type, 0)) >= 0 + if ( !setsockopt(so->fd, SOL_SOCKET, SO_REUSEADDR, &o, sizeof(int)) && !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; return 0; - } else { - if (so->fd >= 0) close(so->fd); - return 1; - } + } else return 1; +} + +static int net_ip_connect(SocketData *so, int type) { + socklen_t ps = sizeof(struct sockaddr_storage); + if (!so) return 1; + + if ( !connect(so->fd, (struct sockaddr *) &(so->peer), ps) + && !getsockname(so->fd, (struct sockaddr *) &(so->bind), &ps) + ){ + so->type = type; + so->connected = 1; + return 0; + } else return 1; +} + +static int net_unix_bind(SocketData *so, const char *path, size_t plen, int con) { + struct sockaddr_un * addr; + if (!so || plen >= sizeof(addr->sun_path)) return 1; + + addr = (struct sockaddr_un *) (con ? &(so->peer) : &(so->bind)); + addr->sun_family = AF_UNIX; + strncpy(addr->sun_path, path, plen); + so->type = SOCK_STREAM; + + if ( con + && !connect(so->fd,(struct sockaddr *) addr ,sizeof(*addr)) + ) { + so->bind.ss_family = AF_UNIX; + so->connected = 1; + return 0; + } else if (!con + && !bind(so->fd,(struct sockaddr *) addr ,sizeof(*addr)) + && !listen(so->fd, 1) + ) { + return 0; + } else return 1; } static JSValue js_sock_set_timeout( @@ -291,7 +323,8 @@ static JSValue js_sock_close( static void sock_destroy(JSRuntime *rt, JSValue this){ SocketData *data = JS_GetOpaque(this, socket_cid); - // close(data->fd); // do not do what a c programmer wouldn't do + // XXX: do not do what a c programmer wouldn't do + // close(data->fd); js_free_rt(rt, data); }; @@ -345,178 +378,135 @@ static JSValue js_sock_async( return promise; } -static const JSCFunctionListEntry socket_ptype[] = { - JS_CFUNC_DEF("accept", 0, js_sock_accept), - JS_CFUNC_MAGIC_DEF("acceptAsync", 0, js_sock_async, 1), - 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_CFUNC_MAGIC_DEF("recvAsync", 0, js_sock_async, 2), - JS_CFUNC_MAGIC_DEF("recvStringAsync", 0, js_sock_async, 3), - 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 = { - "Socket", .finalizer = sock_destroy -}; - -static JSValue js_net_ip_listen( - JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic +static JSValue js_new_socket( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv ) { - int family = (magic&2) ? AF_INET6 : AF_INET; - int type = (magic&1) ? SOCK_STREAM : SOCK_DGRAM; - const char *host, *port; int gai_err = 0; SocketData *data = js_mallocz(ctx, sizeof(*data)); JSValue new = JS_NewObjectClass(ctx, socket_cid); - - host = JS_ToCString(ctx, argv[0]); - port = JS_ToCString(ctx, argv[1]); - - if ( data && (data->fd = -1) - && !(gai_err = net_addrinfo(family, type, host, port, &(data->bind))) - && !net_ip_listen(data, type) + const char * type; + + type = JS_ToCString(ctx, argv[0]); + + if (data) { + if ( !strcmp(type, "unix") ) { + data->bind.ss_family = AF_UNIX; data->type = SOCK_STREAM; + } else if ( !strcmp(type, "tcp") ) { + data->bind.ss_family = AF_INET; data->type = SOCK_STREAM; + } else if ( !strcmp(type, "tcp6") ) { + data->bind.ss_family = AF_INET6; data->type = SOCK_STREAM; + } else if ( !strcmp(type, "udp") ) { + data->bind.ss_family = AF_INET; data->type = SOCK_DGRAM; + } else if ( !strcmp(type, "udp6") ) { + data->bind.ss_family = AF_INET6; data->type = SOCK_DGRAM; + } else { + js_free(ctx, data); + data = NULL; + } + } + if ( data + && (data->fd = socket(data->bind.ss_family, data->type, 0)) >= 0 ) { JS_SetOpaque(new, data); } else { JS_FreeValue(ctx, new); - if (!data) new = JS_EXCEPTION; - else if (!gai_err) new = libc_error(ctx); - else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); - js_free(ctx, data); + new = data ? libc_error(ctx) : JS_EXCEPTION; } - JS_FreeCString(ctx, host); JS_FreeCString(ctx, port); + JS_FreeCString(ctx, type); return new; } -static int net_ip_connect(SocketData *so, int type) { - socklen_t ps = sizeof(struct sockaddr_storage); - 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->connected = 1; - return 0; - } else { - if (so->fd >= 0) close(so->fd); - return 1; - } -} - -static JSValue js_net_ip_connect( - JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic +static JSValue js_sock_listen( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv ) { - int family = (magic&2) ? AF_INET6 : AF_INET; - int type = (magic&1) ? SOCK_STREAM : SOCK_DGRAM; - const char *host, *port; int gai_err = 0; - SocketData *data = js_mallocz(ctx, sizeof(*data)); - JSValue new = JS_NewObjectClass(ctx, socket_cid); + SocketData *data = JS_GetOpaque2(ctx, this, socket_cid); + const char *host = NULL, *port = NULL; int gai_err = 0; + const size_t l_sun_path = sizeof( ((struct sockaddr_un){}).sun_path ); + size_t plen; JSValue ret = JS_UNDEFINED; - host = JS_ToCString(ctx, argv[0]); + host = JS_ToCStringLen(ctx, &plen, argv[0]); port = JS_ToCString(ctx, argv[1]); - if ( data && (data->fd = -1) - && !(gai_err = net_addrinfo(family, type, host, port, &(data->peer))) - && !(net_ip_connect(data, type)) - ) { - JS_SetOpaque(new, data); - } else { - JS_FreeValue(ctx, new); - if (!data) new = JS_EXCEPTION; - else if (!gai_err) new = libc_error(ctx); - else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); - js_free(ctx, data); + if ( data->bind.ss_family == AF_UNIX) { + if ( plen >= l_sun_path ) + ret = JS_ThrowRangeError( + ctx, "pathname too long (>= %lu bytes)", l_sun_path + ); + else if ( net_unix_bind(data, host, plen, 0) ) + ret = libc_error(ctx); + + } else { // ip connection + if ( (gai_err = net_addrinfo( + data->bind.ss_family, data->type, host, port, &(data->bind) + )) ) + ret = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); + else if ( net_ip_listen(data, data->type) ) + ret = libc_error(ctx); } JS_FreeCString(ctx, host); JS_FreeCString(ctx, port); - return new; + if (JS_IsUndefined(ret) ) return JS_DupValue(ctx, this); + else return ret; } -static int net_unix_bind(SocketData *so, const char *path, size_t plen, int con) { - struct sockaddr_un * addr; - if (!so || plen >= sizeof(addr->sun_path)) return 1; - - addr = (struct sockaddr_un *) (con ? &(so->peer) : &(so->bind)); - addr->sun_family = AF_UNIX; - strncpy(addr->sun_path, path, plen); - so->type = SOCK_STREAM; - - 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->bind.ss_family = AF_UNIX; - 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) - ) { - return 0; - } else { - if (so->fd >= 0) close(so->fd); - return 1; - } -} - -static JSValue js_net_unix_bind( - JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int c +static JSValue js_sock_connect( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv ) { - SocketData *data = js_mallocz(ctx, sizeof(*data)); - JSValue new = JS_NewObjectClass(ctx, socket_cid); - const char *path; size_t plen; + SocketData *data = JS_GetOpaque2(ctx, this, socket_cid); + const char *host, *port; int gai_err = 0; const size_t l_sun_path = sizeof( ((struct sockaddr_un){}).sun_path ); + size_t plen; JSValue ret = JS_UNDEFINED; - path = JS_ToCStringLen(ctx, &plen, argv[0]); + host = JS_ToCStringLen(ctx, &plen, argv[0]); + port = JS_ToCString(ctx, argv[1]); - if ( data && (plen < l_sun_path ) - && !net_unix_bind(data, path, plen, c) - ) { - JS_SetOpaque(new, data); - } else { - JS_FreeValue(ctx, new); - if (!data) { - new = JS_EXCEPTION; - } else if (plen < l_sun_path) { - new = libc_error(ctx); - } else new = JS_ThrowRangeError( - ctx, "pathname too long (>= %lu bytes)", l_sun_path - ); - js_free(ctx, data); + if ( data->bind.ss_family == AF_UNIX) { + if ( plen >= l_sun_path ) + ret = JS_ThrowRangeError( + ctx, "pathname too long (>= %lu bytes)", l_sun_path + ); + else if ( net_unix_bind(data, host, plen, 1) ) + ret = libc_error(ctx); + + } else { // ip connection + if ( (gai_err = net_addrinfo( + data->bind.ss_family, data->type, host, port, &(data->peer) + )) ) + ret = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); + else if ( net_ip_connect(data, data->type) ) + ret = libc_error(ctx); } - JS_FreeCString(ctx, path); + JS_FreeCString(ctx, host); JS_FreeCString(ctx, port); - return new; + if (JS_IsUndefined(ret) ) return JS_DupValue(ctx, this); + else return ret; } -static JSValue net_set_timeout( - JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv -) { - if ( ! JS_ToFloat64(ctx, &net_timeout, argv[0])) return argv[0]; - else return JS_EXCEPTION; -} -static JSValue net_get_timeout( - JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv -) { - if (net_timeout < 0) return JS_UNDEFINED; - return JS_NewFloat64(ctx, net_timeout); -} +static const JSCFunctionListEntry socket_ptype[] = { + 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_DEF("listen", 1, js_sock_listen), + JS_CFUNC_DEF("connect", 1, js_sock_connect), + 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), + JS_CFUNC_MAGIC_DEF("acceptAsync", 0, js_sock_async, 1), + JS_CFUNC_MAGIC_DEF("recvAsync", 0, js_sock_async, 2), + JS_CFUNC_MAGIC_DEF("recvStringAsync", 0, js_sock_async, 3), +}; + +static JSClassDef socket_class = { + "Socket", .finalizer = sock_destroy +}; static const JSCFunctionListEntry net_funcs[] = { JS_PROP_INT32_DEF("AF_UNIX", AF_UNIX, JS_PROP_CONFIGURABLE), @@ -524,34 +514,25 @@ 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("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), - - // XXX: How does one use a get/set property in a module provided object? - // JS_CGETSET_DEF("timeout", net_get_timeout, net_set_timeout), - JS_CFUNC_DEF("setTimeout", 1, net_set_timeout), - JS_CFUNC_DEF("getTimeout", 1, net_get_timeout), JS_CFUNC_DEF("fork", 0, js_os_fork), }; static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) { JSValue proto = JS_NewObject(ctx); + JSValue socket = JS_NewCFunction2( + ctx, js_new_socket, "Socket", 1, JS_CFUNC_constructor, 0 + ); JS_NewClassID(&socket_cid); JS_NewClass(JS_GetRuntime(ctx), socket_cid, &socket_class); JS_SetPropertyFunctionList(ctx, proto, socket_ptype, countof(socket_ptype)); + JS_SetConstructor(ctx, socket, proto); JS_SetClassProto(ctx, socket_cid, proto); - return JS_SetModuleExportList(ctx, mod, net_funcs, countof(net_funcs)); + JS_SetModuleExportList(ctx, mod, net_funcs, countof(net_funcs)); + JS_SetModuleExport(ctx, mod, "Socket", socket ); + return 0; } JSModuleDef *js_init_module(JSContext *ctx, const char *name) { @@ -559,6 +540,7 @@ JSModuleDef *js_init_module(JSContext *ctx, const char *name) { if (mod) { JS_AddModuleExportList(ctx, mod, net_funcs, countof(net_funcs)); + JS_AddModuleExport(ctx, mod, "Socket"); } return mod;