typedef struct {
int fd;
int type;
+ double timeout;
struct sockaddr_storage bind;
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_IsNull(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;
+
+ return JS_UNDEFINED;
+}
+static JSValue 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_accept(
JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
) {
socklen_t ps = sizeof(data->bind);
if ( newdata
- && (newdata->fd = accept(
- data->fd, (struct sockaddr *) &(newdata->peer), &ps
- )) >= 0
+ && ( newdata->fd =
+ accept( data->fd, (struct sockaddr *) &(newdata->peer), &ps)
+ ) >= 0
) {
memcpy(&(newdata->bind), &(data->bind), sizeof(data->bind));
JS_SetOpaque(new, newdata);
+ if (data->timeout >= 0)
+ (void) sock_set_timeout(ctx, new, JS_NewFloat64(ctx, data->timeout));
+ else newdata->timeout = -1;
+ return new;
+
} else {
- new = libc_error(ctx);
js_free(ctx, newdata);
JS_FreeValue(ctx, new);
+ if ( errno == EWOULDBLOCK || errno == EAGAIN )
+ return JS_UNDEFINED;
+ else return libc_error(ctx);
}
-
- return new;
}
static JSValue sock_send(
JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
) {
SocketData * data = JS_GetOpaque2(ctx, this, socket_cid);
- uint8_t *buf; size_t len;
+ // XXX: how portable is MSG_NOSIGNAL ? (POSIX.1-2008)
+ uint8_t *buf; size_t len; int flags = MSG_NOSIGNAL;
const char *host, *port; int gai_err;
struct addrinfo hints = { .ai_family = data->bind.ss_family, .ai_socktype = data->type };
struct addrinfo * dest; struct sockaddr * to;
// 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])) ) {
- // XXX: how portable is MSG_NOSIGNAL ? (POSIX.1-2008)
- len = sendto(data->fd, buf, len, MSG_NOSIGNAL, to, to ? sizeof(*to) : 0);
+ len = sendto(data->fd, buf, len, flags, to, to ? sizeof(*to) : 0);
} else if ( (buf = (uint8_t*) JS_ToCStringLen(ctx, &len, argv[0])) ) {
- len = sendto(data->fd, buf, len, MSG_NOSIGNAL, to, to ? sizeof(*to) : 0);
+ len = sendto(data->fd, buf, len, flags, to, to ? sizeof(*to) : 0);
JS_FreeCString(ctx, (char *) buf);
} else return JS_ThrowTypeError(ctx,
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;
+ uint8_t *buf; int len; JSValue jbuf; int flags = 0;
socklen_t ps = sizeof(data->peer);
- // see how much data is queued up if no length argument was given
+ // check current queue size if no valid length was given
// XXX: how portable is ioctl/FIONREAD ?
if ( (JS_ToInt32(ctx, &len, argv[0]) || len <= 0)
&& ioctl(data->fd, FIONREAD, &len)
) return libc_error(ctx);
- // return immediately if 0 data was requested (and queue is empty)
- if (len == 0 && str)
- return JS_NewStringLen(ctx, NULL, 0);
- else if (len == 0)
- return JS_NewArrayBufferCopy(ctx, NULL, 0);
+ if (len <= 0) len = 65535; // if no data is queued, wait for next packet
+ if (data->timeout == 0) flags |= MSG_DONTWAIT;
- else if ( len > 0
- && (buf = js_malloc(ctx, len))
- && ((len = recvfrom(data->fd, buf, len, 0,
+ if ( ( buf = js_malloc(ctx, len) )
+ && ((len = recvfrom(data->fd, buf, len, flags,
(struct sockaddr *) &(data->peer), &ps)) >= 0)
) {
- // make sure the buffer is resized after a short read
- // avoid realloc(p, 0) as recommended by glibc
+ // resize/clear buffer in case of short read,
if (len) js_realloc(ctx, buf, len);
else { js_free(ctx, buf); buf = NULL; }
// XXX: what does the opaque field in NewArryBuffer do? Is it important?
- if (str) jbuf = JS_NewStringLen(ctx, (char*) buf, len);
- else jbuf = JS_NewArrayBuffer(ctx, buf, len, js_array_buffer_free, NULL, 0);
- return jbuf;
-
+ if (str) {
+ jbuf = JS_NewStringLen(ctx, (char*) buf, len);
+ js_free(ctx, buf);
+ return jbuf;
+ } else
+ return JS_NewArrayBuffer(ctx, buf, len, js_array_buffer_free, NULL, 0);
} else {
js_free(ctx, buf);
- return libc_error(ctx);
+ if ( errno == EWOULDBLOCK || errno == EAGAIN )
+ return JS_UNDEFINED;
+ else return libc_error(ctx);
}
}
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", ) // TODO: send / receive timeout
+ JS_CGETSET_DEF("timeout", sock_get_timeout, sock_set_timeout),
// JS_CFUNC_DEF("reconnect", ) // TODO
};
&& ( hints.ai_socktype == SOCK_DGRAM || !listen(data->fd, 1))
) {
data->type = self->ai_socktype;
+ data->timeout = -1;
memcpy(&(data->bind), self->ai_addr, sizeof(struct sockaddr));
JS_SetOpaque(new, data);
} else {
&& !ai_connect(data->fd, peer)
) {
data->type = peer->ai_socktype;
+ data->timeout = -1;
memcpy(&(data->peer), peer->ai_addr, sizeof(struct sockaddr));
JS_SetOpaque(new, data);
} else {
) && (c || !listen(data->fd, 1))
) {
data->type = SOCK_STREAM;
+ data->timeout = -1;
JS_SetOpaque(new, data);
} else {
if (data && data->fd >= 0) close(data->fd);
JS_CFUNC_MAGIC_DEF("tcp6Connect", 2, ip_connect, 3),
JS_CFUNC_MAGIC_DEF("unixListen", 1, unix_bind, 0),
JS_CFUNC_MAGIC_DEF("unixConnect", 1, unix_bind, 1),
- // JS_CGETSET_DEF("timeout", ) // TODO: connect timeout
};
// static const JSCFunctionListEntry net_obj[] = {