}
static JSClassID socket_cid;
-static double net_timeout = -1;
typedef struct {
int fd;
}
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(
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);
};
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),
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) {
if (mod) {
JS_AddModuleExportList(ctx, mod, net_funcs, countof(net_funcs));
+ JS_AddModuleExport(ctx, mod, "Socket");
}
return mod;