Skip to content

Commit e2e64a6

Browse files
committed
windows support
1 parent 29a8f53 commit e2e64a6

File tree

6 files changed

+142
-47
lines changed

6 files changed

+142
-47
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ HOST_LIBS=-lm -ldl -lpthread
247247
LIBS=-lm -lpthread
248248
ifndef CONFIG_WIN32
249249
LIBS+=-ldl
250+
else
251+
LIBS+=-lws2_32
250252
endif
251253
LIBS+=$(EXTRA_LIBS)
252254

examples/http_client.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ function must(result) {
1010
}
1111

1212
const sockfd = must(os.socket(os.AF_INET, os.SOCK_STREAM));
13-
await os.connect(sockfd, os.getaddrinfo("bellard.org",'80')[0]);
14-
const httpReq = ["GET / HTTP/1.0", "", ""].join('\r\n')
15-
must(await os.send(sockfd, Uint8Array.from(httpReq, c => c.charCodeAt(0)).buffer) > 0);
13+
const addr = os.getaddrinfo("bellard.org",'80').find(a => a.family == os.AF_INET);
14+
await os.connect(sockfd, addr);
15+
const httpReq = Uint8Array.from("GET / HTTP/1.0\r\n\r\n", c => c.charCodeAt(0))
16+
must(await os.send(sockfd, httpReq.buffer) > 0);
1617
const chunk = new Uint8Array(512);
1718
const recvd = await os.recv(sockfd, chunk.buffer);
1819
console.log([...chunk.slice(0,recvd)].map(c => String.fromCharCode(c)).join(''));

examples/http_server.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function sendLines(fd, lines) {
4040
}
4141
//USAGE: qjs http_server.js [PORT=8080 [HOST=localhost]]
4242
const [port = "8080", host = "localhost"] = scriptArgs.slice(1);
43-
const [ai] = os.getaddrinfo(host, port);
43+
const ai = os.getaddrinfo(host, port).find(a => a.family == os.AF_INET);
4444
//if (!ai.length) throw `Unable to getaddrinfo(${host}, ${port})`;
4545
const sock_srv = must(os.socket(os.AF_INET, os.SOCK_STREAM));
4646
must(os.setsockopt(sock_srv, os.SO_REUSEADDR, new Uint32Array([1]).buffer));
@@ -49,7 +49,7 @@ must(os.listen(sock_srv));
4949
//os.signal(os.SIGINT, ()=>os.close(sock_srv)); // don't work
5050
console.log(`Listening on http://${host}:${port} (${ai.addr}:${ai.port}) ...`);
5151
const openCmd = { linux: "xdg-open", darwin: "open", win32: "start" }[os.platform];
52-
if (openCmd) os.exec([openCmd, `http://${host}:${port}`]);
52+
if (openCmd && os.exec) os.exec([openCmd, `http://${host}:${port}`]);
5353
while (true) { // TODO: break on SIG*
5454
const [sock_cli] = await os.accept(sock_srv);
5555

qjs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
#elif defined(__FreeBSD__)
4040
#include <malloc_np.h>
4141
#endif
42+
#if defined(_WIN32)
43+
#include <winsock2.h>
44+
#include <ws2tcpip.h>
45+
#include <windows.h>
46+
#endif
4247

4348
#include "cutils.h"
4449
#include "quickjs-libc.h"

quickjs-libc.c

Lines changed: 111 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@
3838
#include <sys/stat.h>
3939
#include <dirent.h>
4040
#if defined(_WIN32)
41+
#include <winsock2.h>
42+
#include <ws2tcpip.h>
4143
#include <windows.h>
4244
#include <conio.h>
4345
#include <utime.h>
44-
#include <winsock2.h>
4546
#else
4647
#include <dlfcn.h>
4748
#include <termios.h>
@@ -113,6 +114,9 @@ typedef struct {
113114
struct sockaddr_storage sockaddr; // for sendto()
114115
JSValue resolve;
115116
JSValue reject;
117+
#ifdef _WIN32
118+
WSAEVENT event; // so os_pool can wait on it
119+
#endif
116120
} JSOSSockHandler;
117121

118122
typedef struct {
@@ -249,7 +253,7 @@ static int JS_ToSockaddrStruct(JSContext *ctx, JSValue addr,
249253
static JSValue JS_ToSockaddrObj(JSContext *ctx, struct sockaddr_storage *sockaddr)
250254
{
251255
JSValue obj, prop;
252-
char ip_str[INET_ADDRSTRLEN];
256+
char ip_str[INET6_ADDRSTRLEN]; // max(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)
253257
const char *ip_ptr;
254258
struct sockaddr_in *sockaddr4 = (struct sockaddr_in *)sockaddr;
255259
struct sockaddr_in6 *sockaddr6 = (struct sockaddr_in6 *)sockaddr;
@@ -268,7 +272,9 @@ static JSValue JS_ToSockaddrObj(JSContext *ctx, struct sockaddr_storage *sockadd
268272

269273
void* sin_addr = sockaddr->ss_family == AF_INET ? (void*)&sockaddr4->sin_addr :
270274
sockaddr->ss_family == AF_INET6 ? (void*)&sockaddr6->sin6_addr : NULL;
271-
ip_ptr = inet_ntop(AF_INET, sin_addr, ip_str, sizeof(ip_str));
275+
size_t sin_len = sockaddr->ss_family == AF_INET ? INET_ADDRSTRLEN:
276+
sockaddr->ss_family == AF_INET6 ? INET6_ADDRSTRLEN: 0 ;
277+
ip_ptr = inet_ntop(sockaddr->ss_family, sin_addr, ip_str, sin_len);
272278
prop = ip_ptr ? JS_NewString(ctx, ip_ptr) : JS_NULL;
273279
JS_DefinePropertyValueStr(ctx, obj, "addr", prop, JS_PROP_C_W_E);
274280
return obj;
@@ -1039,6 +1045,18 @@ static ssize_t js_get_errno(ssize_t ret)
10391045
return ret;
10401046
}
10411047

1048+
static ssize_t js_get_sockerrno(ssize_t ret)
1049+
{
1050+
#if defined(_WIN32)
1051+
if (ret == -1 || ret == INVALID_SOCKET)
1052+
ret = -WSAGetLastError();
1053+
#else
1054+
if (ret == -1)
1055+
ret = -errno;
1056+
#endif
1057+
return ret;
1058+
}
1059+
10421060
static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,
10431061
int argc, JSValueConst *argv)
10441062
{
@@ -1785,11 +1803,6 @@ static int js_std_init(JSContext *ctx, JSModuleDef *m)
17851803
{
17861804
JSValue proto;
17871805

1788-
#if defined(_WIN32)
1789-
WSADATA wsa_data;
1790-
WSAStartup(0x0202, &wsa_data);
1791-
#endif
1792-
17931806
/* FILE class */
17941807
/* the class ID is created once */
17951808
JS_NewClassID(&js_std_file_class_id);
@@ -2517,28 +2530,41 @@ static int handle_posted_message(JSContext *ctx, JSWorkerMessageHandler *port)
25172530

25182531
static void handle_socket_message(JSContext *ctx, JSOSSockHandler *sh)
25192532
{
2533+
#ifdef _WIN32
2534+
WSANETWORKEVENTS netEvents;
2535+
WSAEnumNetworkEvents(sh->sockfd, sh->event, &netEvents);
2536+
#endif
2537+
25202538
int err = 0;
25212539
struct sockaddr_storage sockaddr;
25222540
socklen_t addr_len = sizeof(sockaddr);
2523-
socklen_t len = sizeof(err);
2541+
25242542
if (sh->magic == MAGIC_SOCKET_RECV) {
2525-
err = js_get_errno(recv(sh->sockfd, sh->buffer, sh->length, 0));
2543+
err = js_get_sockerrno(recv(sh->sockfd, (char*) sh->buffer, sh->length, 0));
25262544
} else if (sh->magic == MAGIC_SOCKET_SEND) {
2527-
err = js_get_errno(send(sh->sockfd, sh->buffer, sh->length, 0));
2545+
err = js_get_sockerrno(send(sh->sockfd, (char*) sh->buffer, sh->length, 0));
25282546
} else if (sh->magic == MAGIC_SOCKET_RECVFROM) {
2529-
err = js_get_errno(recvfrom(sh->sockfd, sh->buffer, sh->length, 0, (struct sockaddr *)&sockaddr, &addr_len));
2547+
err = js_get_sockerrno(recvfrom(sh->sockfd, (char*) sh->buffer, sh->length, 0, (struct sockaddr *)&sockaddr, &addr_len));
25302548
} else if (sh->magic == MAGIC_SOCKET_SENDTO) {
2531-
err = js_get_errno(sendto(sh->sockfd, sh->buffer, sh->length, 0, (const struct sockaddr *)&sh->sockaddr, addr_len));
2549+
err = js_get_sockerrno(sendto(sh->sockfd, (char*) sh->buffer, sh->length, 0, (const struct sockaddr *)&sh->sockaddr, addr_len));
25322550
} else if (sh->magic == MAGIC_SOCKET_CONNECT) {
2533-
err = getsockopt(sh->sockfd, SOL_SOCKET, SO_ERROR, &err, &len) ? -errno : -err;
2551+
#ifdef _WIN32
2552+
err = 0;
2553+
#else
2554+
socklen_t len = sizeof(err);
2555+
err = getsockopt(sh->sockfd, SOL_SOCKET, SO_ERROR, (char*) &err, &len) ? -errno : -err;
2556+
#endif
25342557
} else if (sh->magic == MAGIC_SOCKET_ACCEPT) {
2535-
err = js_get_errno(accept(sh->sockfd, (struct sockaddr *)&sockaddr, &addr_len));
2558+
err = js_get_sockerrno(accept(sh->sockfd, (struct sockaddr *)&sockaddr, &addr_len));
25362559
}
25372560

2538-
2561+
#ifdef _WIN32
2562+
if (err == -WSAEWOULDBLOCK)
2563+
return;
2564+
#else
25392565
if (err == -EAGAIN || err == -EWOULDBLOCK)
25402566
return;
2541-
2567+
#endif
25422568
JSValue promiseval = JS_UNDEFINED;
25432569
if (sh->magic == MAGIC_SOCKET_ACCEPT) {
25442570
promiseval = JS_NewArray(ctx);
@@ -2589,8 +2615,10 @@ static int js_os_poll(JSContext *ctx)
25892615

25902616
/* XXX: handle signals if useful */
25912617

2592-
if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&
2593-
list_empty(&ts->port_list)) {
2618+
if (list_empty(&ts->os_rw_handlers) &&
2619+
list_empty(&ts->os_timers) &&
2620+
list_empty(&ts->port_list) &&
2621+
list_empty(&ts->os_sock_handlers)) {
25942622
return -1; /* no more events */
25952623
}
25962624

@@ -2620,13 +2648,21 @@ static int js_os_poll(JSContext *ctx)
26202648
count = 0;
26212649
list_for_each(el, &ts->os_rw_handlers) {
26222650
rh = list_entry(el, JSOSRWHandler, link);
2623-
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
2651+
if (rh->fd == 0 && !JS_IsNull(rh->r_func)) {
26242652
handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin
26252653
if (count == (int)countof(handles))
26262654
break;
26272655
}
26282656
}
26292657

2658+
list_for_each(el, &ts->os_sock_handlers) {
2659+
JSOSSockHandler *sh = list_entry(el, JSOSSockHandler, link);
2660+
//TODO: socket readability don't seems to be a winsock event => trigger manually
2661+
handles[count++] = sh->event;
2662+
if (count == (int)countof(handles))
2663+
break;
2664+
}
2665+
26302666
list_for_each(el, &ts->port_list) {
26312667
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
26322668
if (JS_IsNull(port->on_message_func))
@@ -2642,16 +2678,26 @@ static int js_os_poll(JSContext *ctx)
26422678
timeout = min_delay;
26432679
ret = WaitForMultipleObjects(count, handles, FALSE, timeout);
26442680

2681+
// why iterate on every list instead of just handles[ret] ?
26452682
if (ret < count) {
26462683
list_for_each(el, &ts->os_rw_handlers) {
26472684
rh = list_entry(el, JSOSRWHandler, link);
2648-
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
2649-
call_handler(ctx, rh->rw_func[0]);
2685+
if (rh->fd == 0 && !JS_IsNull(rh->r_func)) {
2686+
call_handler(ctx, rh->r_func);
26502687
/* must stop because the list may have been modified */
26512688
goto done;
26522689
}
26532690
}
26542691

2692+
list_for_each(el, &ts->os_sock_handlers) {
2693+
JSOSSockHandler *sh = list_entry(el, JSOSSockHandler, link);
2694+
if (sh->event == handles[ret]) {
2695+
handle_socket_message(ctx, sh);
2696+
WSAResetEvent(sh->event); // WSACloseEvent(sh->event);
2697+
goto done;
2698+
}
2699+
}
2700+
26552701
list_for_each(el, &ts->port_list) {
26562702
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
26572703
if (!JS_IsNull(port->on_message_func)) {
@@ -3613,7 +3659,7 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
36133659
static JSValue js_os_socket(JSContext *ctx, JSValueConst this_val,
36143660
int argc, JSValueConst *argv)
36153661
{
3616-
int domain, type, protocol = 0, ret;
3662+
int domain, type, protocol = 0;
36173663

36183664
if (JS_ToInt32(ctx, &domain, argv[0]))
36193665
return JS_EXCEPTION;
@@ -3622,11 +3668,19 @@ static JSValue js_os_socket(JSContext *ctx, JSValueConst this_val,
36223668
if (argc >= 3 && JS_ToInt32(ctx, &protocol, argv[2]))
36233669
return JS_EXCEPTION;
36243670

3625-
ret = js_get_errno(socket(domain, type, protocol));
3626-
3627-
if (ret >= 0 && !(type & SOCK_NONBLOCK)) // JS flag `os.SOCK_BLOCKING`
3628-
fcntl(ret, F_SETFL, fcntl(ret, F_GETFL, 0) | O_NONBLOCK);
3629-
return JS_NewInt32(ctx, ret);
3671+
int socketfd = socket(domain, type, protocol);
3672+
int ret = js_get_sockerrno(socketfd);
3673+
if (ret < 0)
3674+
return JS_NewInt32(ctx, ret);
3675+
#if defined(_WIN32)
3676+
u_long mode = 1;
3677+
ret = ioctlsocket(ret, FIONBIO, &mode);
3678+
#else
3679+
ret = fcntl(ret, F_SETFL, fcntl(ret, F_GETFL, 0) | O_NONBLOCK);
3680+
#endif
3681+
if (ret < 0)
3682+
return JS_NewInt32(ctx, ret);
3683+
return JS_NewInt32(ctx, socketfd);
36303684
}
36313685

36323686
static JSValue js_os_get_setsockopt(JSContext *ctx, JSValueConst this_val,
@@ -3647,9 +3701,9 @@ static JSValue js_os_get_setsockopt(JSContext *ctx, JSValueConst this_val,
36473701
optlen = buflen;
36483702

36493703
if (magic == 0)
3650-
ret = js_get_errno(getsockopt(sock, SOL_SOCKET, optname, optval, &optlen));
3704+
ret = js_get_sockerrno(getsockopt(sock, SOL_SOCKET, optname, (char*)optval, &optlen));
36513705
else
3652-
ret = js_get_errno(setsockopt(sock, SOL_SOCKET, optname, &optval, optlen));
3706+
ret = js_get_sockerrno(setsockopt(sock, SOL_SOCKET, optname, (char*)optval, optlen));
36533707
return JS_NewInt32(ctx, ret);
36543708
}
36553709

@@ -3670,7 +3724,7 @@ static JSValue js_os_getaddrinfo(JSContext *ctx, JSValueConst this_val,
36703724
if (!service)
36713725
goto fail;
36723726

3673-
ret = js_get_errno(getaddrinfo(node, service, NULL, &ai));
3727+
ret = js_get_sockerrno(getaddrinfo(node, service, NULL, &ai));
36743728
if (ret)
36753729
goto fail;
36763730

@@ -3703,7 +3757,7 @@ static JSValue js_os_bind(JSContext *ctx, JSValueConst this_val,
37033757
return JS_EXCEPTION;
37043758
socklen_t addr_len = sockaddr.ss_family == AF_INET ? sizeof(struct sockaddr_in) :
37053759
sockaddr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0;
3706-
ret = js_get_errno(bind(sockfd, (struct sockaddr *)&sockaddr, addr_len));
3760+
ret = js_get_sockerrno(bind(sockfd, (struct sockaddr *)&sockaddr, addr_len));
37073761
return JS_NewInt32(ctx, ret);
37083762
}
37093763

@@ -3717,7 +3771,7 @@ static JSValue js_os_listen(JSContext *ctx, JSValueConst this_val,
37173771
if (argc >= 2 && JS_ToInt32(ctx, &backlog, argv[1]))
37183772
return JS_EXCEPTION;
37193773

3720-
ret = js_get_errno(listen(sockfd, backlog));
3774+
ret = js_get_sockerrno(listen(sockfd, backlog));
37213775
return JS_NewInt32(ctx, ret);
37223776
}
37233777

@@ -3737,10 +3791,14 @@ static JSValue js_os_connect_accept(JSContext *ctx, JSValueConst this_val,
37373791
}
37383792
socklen_t addr_len = sockaddr.ss_family == AF_INET ? sizeof(struct sockaddr_in) :
37393793
sockaddr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0;
3740-
3741-
sockret = js_get_errno(connect(sockfd, (struct sockaddr *)&sockaddr, addr_len));
3742-
if (sockret != 0 && sockret != -EINPROGRESS) {
3743-
JS_ThrowTypeError(ctx, "connect failed");
3794+
sockret = js_get_sockerrno(connect(sockfd, (struct sockaddr *)&sockaddr, addr_len));
3795+
#if defined(_WIN32)
3796+
if (sockret != -WSAEWOULDBLOCK)
3797+
#else
3798+
if (sockret != -EINPROGRESS)
3799+
#endif
3800+
{
3801+
JS_ThrowTypeError(ctx, "connect failed (%i)", sockret);
37443802
return JS_EXCEPTION;
37453803
}
37463804
}
@@ -3757,10 +3815,13 @@ static JSValue js_os_connect_accept(JSContext *ctx, JSValueConst this_val,
37573815
sh->magic = magic;
37583816
sh->resolve = JS_DupValue(ctx, resolving_funcs[0]);
37593817
sh->reject = JS_DupValue(ctx, resolving_funcs[1]);
3818+
#if defined(_WIN32)
3819+
sh->event = WSACreateEvent();
3820+
WSAEventSelect(sh->sockfd, sh->event, magic == MAGIC_SOCKET_CONNECT ? FD_CONNECT : FD_ACCEPT);
3821+
#endif
37603822
list_add_tail(&sh->link, &ts->os_sock_handlers);
37613823
JS_FreeValue(ctx, resolving_funcs[0]);
37623824
JS_FreeValue(ctx, resolving_funcs[1]);
3763-
37643825
return promise;
37653826
}
37663827

@@ -3804,7 +3865,11 @@ static JSValue js_os_recv_send(JSContext *ctx, JSValueConst this_val,
38043865
}
38053866
sh->length = length;
38063867
}
3807-
3868+
#if defined(_WIN32)
3869+
sh->event = WSACreateEvent();
3870+
int flags = (magic == MAGIC_SOCKET_SENDTO || magic == MAGIC_SOCKET_SEND) ? FD_WRITE : FD_READ;
3871+
WSAEventSelect(sh->sockfd, sh->event, flags);
3872+
#endif
38083873
sh->bufval = JS_DupValue(ctx, argv[bufArgvIdx]);
38093874
sh->resolve = JS_DupValue(ctx, resolving_funcs[0]);
38103875
sh->reject = JS_DupValue(ctx, resolving_funcs[1]);
@@ -3831,7 +3896,7 @@ static JSValue js_os_shutdown(JSContext *ctx, JSValueConst this_val,
38313896
if (JS_ToInt32(ctx, &how, argv[1]))
38323897
return JS_EXCEPTION;
38333898

3834-
ret = js_get_errno(shutdown(sockfd, how));
3899+
ret = js_get_sockerrno(shutdown(sockfd, how));
38353900
return JS_NewInt32(ctx, ret);
38363901
}
38373902

@@ -4389,14 +4454,18 @@ static const JSCFunctionListEntry js_os_funcs[] = {
43894454
OS_FLAG(SOCK_STREAM),
43904455
OS_FLAG(SOCK_DGRAM),
43914456
OS_FLAG(SOCK_RAW),
4392-
// SOCK_NONBLOCK is set by default so reuse it value for our imaginary nemsis
4393-
JS_PROP_INT32_DEF("SOCK_BLOCK", SOCK_NONBLOCK, JS_PROP_CONFIGURABLE ),
43944457
OS_FLAG(SO_REUSEADDR),
43954458
OS_FLAG(SO_RCVBUF),
43964459
OS_FLAG(SO_ERROR),
4460+
#if defined(_WIN32)
4461+
JS_PROP_INT32_DEF("SHUT_RD", SD_RECEIVE, JS_PROP_CONFIGURABLE),
4462+
JS_PROP_INT32_DEF("SHUT_WR", SD_SEND, JS_PROP_CONFIGURABLE),
4463+
JS_PROP_INT32_DEF("SHUT_RDWR", SD_BOTH, JS_PROP_CONFIGURABLE),
4464+
#else
43974465
OS_FLAG(SHUT_RD),
43984466
OS_FLAG(SHUT_WR),
43994467
OS_FLAG(SHUT_RDWR),
4468+
#endif
44004469
};
44014470

44024471
static int js_os_init(JSContext *ctx, JSModuleDef *m)

0 commit comments

Comments
 (0)