MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <stdlib.h>
6#include <string.h>
7
8#ifdef _WIN32
9#include <winsock2.h>
10#include <ws2tcpip.h>
11#else
12#include <arpa/inet.h>
13#endif
14
15#include "ant.h"
16#include "ptr.h"
17#include "errors.h"
18#include "internal.h"
19
20#include "gc/roots.h"
21#include "gc/modules.h"
22#include "net/listener.h"
23#include "silver/engine.h"
24
25#include "modules/buffer.h"
26#include "modules/events.h"
27#include "modules/net.h"
28#include "modules/symbol.h"
29
30typedef struct net_server_s net_server_t;
31typedef struct net_socket_s net_socket_t;
32
33typedef struct net_listen_args_s net_listen_args_t;
34typedef struct net_write_args_s net_write_args_t;
35
36struct net_socket_s {
37 ant_t *js;
38 ant_value_t obj;
39 ant_conn_t *conn;
40 net_server_t *server;
41 ant_value_t encoding;
42 struct net_socket_s *next_active;
43 struct net_socket_s *next_in_server;
44 bool allow_half_open;
45 bool destroyed;
46 bool had_error;
47};
48
49struct net_server_s {
50 ant_t *js;
51 ant_value_t obj;
52 ant_listener_t listener;
53 net_socket_t *sockets;
54 struct net_server_s *next_active;
55 char *host;
56 char *path;
57 int port;
58 int backlog;
59 int max_connections;
60 bool listening;
61 bool closing;
62 bool allow_half_open;
63 bool pause_on_connect;
64 bool no_delay;
65 bool keep_alive;
66 unsigned int keep_alive_initial_delay_secs;
67};
68
69struct net_listen_args_s {
70 const char *host;
71 const char *path;
72 int port;
73 int backlog;
74 ant_value_t callback;
75 ant_value_t error;
76};
77
78struct net_write_args_s {
79 const uint8_t *bytes;
80 size_t len;
81 ant_value_t callback;
82 ant_value_t error;
83};
84
85static ant_value_t g_net_server_proto = 0;
86static ant_value_t g_net_socket_proto = 0;
87static ant_value_t g_net_server_ctor = 0;
88static ant_value_t g_net_socket_ctor = 0;
89
90static net_server_t *g_active_servers = NULL;
91static net_socket_t *g_active_sockets = NULL;
92
93static bool g_default_auto_select_family = true;
94static double g_default_auto_select_family_attempt_timeout = 250.0;
95
96enum {
97 NET_SERVER_NATIVE_TAG = 0x4e455453u, // NETS
98 NET_SOCKET_NATIVE_TAG = 0x4e45544bu, // NETK
99};
100
101static ant_value_t net_not_implemented(ant_t *js, const char *what);
102
103static net_server_t *net_server_data(ant_value_t value) {
104 if (!js_check_native_tag(value, NET_SERVER_NATIVE_TAG)) return NULL;
105 return (net_server_t *)js_get_native_ptr(value);
106}
107
108static net_socket_t *net_socket_data(ant_value_t value) {
109 if (!js_check_native_tag(value, NET_SOCKET_NATIVE_TAG)) return NULL;
110 return (net_socket_t *)js_get_native_ptr(value);
111}
112
113static void net_add_active_server(net_server_t *server) {
114 server->next_active = g_active_servers;
115 g_active_servers = server;
116}
117
118static void net_remove_active_server(net_server_t *server) {
119 net_server_t **it = NULL;
120 for (it = &g_active_servers; *it; it = &(*it)->next_active) {
121 if (*it == server) {
122 *it = server->next_active;
123 server->next_active = NULL;
124 return;
125 }}
126}
127
128static void net_add_active_socket(net_socket_t *socket) {
129 socket->next_active = g_active_sockets;
130 g_active_sockets = socket;
131}
132
133static void net_remove_active_socket(net_socket_t *socket) {
134 net_socket_t **it = NULL;
135 for (it = &g_active_sockets; *it; it = &(*it)->next_active) {
136 if (*it == socket) {
137 *it = socket->next_active;
138 socket->next_active = NULL;
139 return;
140 }}
141}
142
143static ant_value_t net_call_value(
144 ant_t *js,
145 ant_value_t fn,
146 ant_value_t this_val,
147 ant_value_t *args,
148 int nargs
149) {
150 ant_value_t saved_this = js->this_val;
151 ant_value_t result = js_mkundef();
152
153 js->this_val = this_val;
154 if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs);
155 else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false);
156 js->this_val = saved_this;
157 return result;
158}
159
160static bool net_emit(ant_t *js, ant_value_t target, const char *event, ant_value_t *args, int nargs) {
161 return eventemitter_emit_args(js, target, event, args, nargs);
162}
163
164static bool net_add_listener(
165 ant_t *js,
166 ant_value_t target,
167 const char *event,
168 ant_value_t listener,
169 bool once
170) {
171 return eventemitter_add_listener(js, target, event, listener, once);
172}
173
174static net_server_t *net_require_server(ant_t *js, ant_value_t this_val) {
175 net_server_t *server = net_server_data(this_val);
176 if (!server) {
177 js->thrown_exists = true;
178 js->thrown_value = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid net.Server");
179 return NULL;
180 }
181 return server;
182}
183
184static net_socket_t *net_require_socket(ant_t *js, ant_value_t this_val) {
185 net_socket_t *socket = net_socket_data(this_val);
186 if (!socket) {
187 js->thrown_exists = true;
188 js->thrown_value = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid net.Socket");
189 return NULL;
190 }
191 return socket;
192}
193
194static bool net_parse_write_args(ant_t *js, ant_value_t *args, int nargs, net_write_args_t *out) {
195 ant_value_t value = 0;
196
197 if (!out) return false;
198 memset(out, 0, sizeof(*out));
199 out->callback = js_mkundef();
200 out->error = js_mkundef();
201
202 if (nargs < 1 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return true;
203 value = args[0];
204
205 if (!buffer_source_get_bytes(js, value, &out->bytes, &out->len)) {
206 size_t slen = 0;
207 ant_value_t str_val = js_tostring_val(js, value);
208 const char *str = NULL;
209
210 if (is_err(str_val)) {
211 out->error = str_val;
212 return false;
213 }
214
215 str = js_getstr(js, str_val, &slen);
216 if (!str) {
217 out->error = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid socket write data");
218 return false;
219 }
220
221 out->bytes = (const uint8_t *)str;
222 out->len = slen;
223 }
224
225 if (nargs > 1 && is_callable(args[1])) out->callback = args[1];
226 else if (nargs > 2 && is_callable(args[2])) out->callback = args[2];
227 return true;
228}
229
230static bool net_parse_listen_args(ant_t *js, ant_value_t *args, int nargs, net_listen_args_t *out) {
231 if (!out) return false;
232
233 memset(out, 0, sizeof(*out));
234 out->host = "0.0.0.0";
235 out->backlog = 511;
236 out->callback = js_mkundef();
237 out->error = js_mkundef();
238
239 if (nargs == 0) return true;
240
241 if (vtype(args[0]) == T_NUM) {
242 out->port = (int)js_getnum(args[0]);
243 if (nargs > 1 && vtype(args[1]) == T_STR) {
244 size_t len = 0;
245 out->host = js_getstr(js, args[1], &len);
246 }
247 if (nargs > 2 && vtype(args[2]) == T_NUM) out->backlog = (int)js_getnum(args[2]);
248 if (nargs > 3 && is_callable(args[3])) out->callback = args[3];
249 else if (nargs > 2 && is_callable(args[2])) out->callback = args[2];
250 else if (nargs > 1 && is_callable(args[1])) out->callback = args[1];
251 return true;
252 }
253
254 if (vtype(args[0]) == T_OBJ) {
255 ant_value_t value = js_get(js, args[0], "path");
256 if (vtype(value) == T_STR) {
257 out->path = js_getstr(js, value, NULL);
258 }
259
260 value = js_get(js, args[0], "port");
261 if (vtype(value) == T_NUM) out->port = (int)js_getnum(value);
262 value = js_get(js, args[0], "host");
263 if (vtype(value) == T_STR) {
264 size_t len = 0;
265 out->host = js_getstr(js, value, &len);
266 }
267
268 value = js_get(js, args[0], "backlog");
269 if (vtype(value) == T_NUM) out->backlog = (int)js_getnum(value);
270 if (nargs > 1 && is_callable(args[1])) out->callback = args[1];
271
272 return true;
273 }
274
275 if (vtype(args[0]) == T_STR) {
276 out->path = js_getstr(js, args[0], NULL);
277 if (nargs > 1 && vtype(args[1]) == T_NUM) out->backlog = (int)js_getnum(args[1]);
278 if (nargs > 2 && is_callable(args[2])) out->callback = args[2];
279 else if (nargs > 1 && is_callable(args[1])) out->callback = args[1];
280 return true;
281 }
282
283 if (is_callable(args[0])) {
284 out->callback = args[0];
285 return true;
286 }
287
288 return true;
289}
290
291static ant_value_t net_make_buffer_chunk(ant_t *js, const char *data, size_t len) {
292 ArrayBufferData *ab = create_array_buffer_data(len);
293 if (!ab) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
294 if (len > 0 && data) memcpy(ab->data, data, len);
295 return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer");
296}
297
298static void net_socket_sync_state(net_socket_t *socket) {
299 ant_value_t obj = 0;
300 const char *ready_state = "open";
301
302 if (!socket) return;
303 obj = socket->obj;
304 if (!is_object_type(obj)) return;
305
306 if (socket->destroyed) ready_state = "closed";
307 js_set(socket->js, obj, "destroyed", js_bool(socket->destroyed));
308 js_set(socket->js, obj, "pending", js_bool(socket->conn == NULL && !socket->destroyed));
309 js_set(socket->js, obj, "connecting", js_false);
310 js_set(socket->js, obj, "readyState", js_mkstr(socket->js, ready_state, strlen(ready_state)));
311 js_set(socket->js, obj, "bytesRead", js_mknum((double)(socket->conn ? ant_conn_bytes_read(socket->conn) : 0)));
312 js_set(socket->js, obj, "bytesWritten", js_mknum((double)(socket->conn ? ant_conn_bytes_written(socket->conn) : 0)));
313 js_set(socket->js, obj, "timeout", js_mknum((double)(socket->conn ? ant_conn_timeout_ms(socket->conn) : 0)));
314}
315
316static void net_server_sync_state(net_server_t *server) {
317 if (!server || !is_object_type(server->obj)) return;
318 js_set(server->js, server->obj, "listening", js_bool(server->listening));
319 js_set(server->js, server->obj, "maxConnections", js_mknum((double)server->max_connections));
320 js_set(server->js, server->obj, "dropMaxConnection", js_false);
321}
322
323static int net_server_socket_count(net_server_t *server) {
324 int count = 0;
325 net_socket_t *socket = NULL;
326
327 for (socket = server ? server->sockets : NULL; socket; socket = socket->next_in_server)
328 count++;
329 return count;
330}
331
332static void net_server_maybe_finish_close(net_server_t *server) {
333 if (!server || !server->closing) return;
334 if (!ant_listener_is_closed(&server->listener)) return;
335 if (server->sockets) return;
336
337 server->closing = false;
338 server->listening = false;
339 net_server_sync_state(server);
340 net_emit(server->js, server->obj, "close", NULL, 0);
341 net_remove_active_server(server);
342}
343
344static void net_socket_detach(net_socket_t *socket) {
345 if (!socket) return;
346
347 if (socket->server) {
348 net_socket_t **it = NULL;
349 for (it = &socket->server->sockets; *it; it = &(*it)->next_in_server) {
350 if (*it == socket) {
351 *it = socket->next_in_server;
352 break;
353 }
354 }
355 socket->next_in_server = NULL;
356 }
357
358 net_remove_active_socket(socket);
359 if (is_object_type(socket->obj)) {
360 js_set_native_ptr(socket->obj, NULL);
361 js_set_native_tag(socket->obj, 0);
362 }
363 net_server_maybe_finish_close(socket->server);
364 free(socket);
365}
366
367static void net_socket_emit_error(net_socket_t *socket, const char *message) {
368 ant_value_t arg = js_mkerr_typed(socket->js, JS_ERR_TYPE, "%s", message);
369 socket->had_error = true;
370 net_emit(socket->js, socket->obj, "error", &arg, 1);
371}
372
373static net_socket_t *net_socket_create(ant_t *js, bool allow_half_open) {
374 ant_value_t obj = js_mkobj(js);
375 ant_value_t proto = js_instance_proto_from_new_target(js, g_net_socket_proto);
376 net_socket_t *socket = calloc(1, sizeof(*socket));
377
378 if (!socket) return NULL;
379 if (is_object_type(proto)) js_set_proto_init(obj, proto);
380
381 socket->js = js;
382 socket->obj = obj;
383 socket->encoding = js_mkundef();
384 socket->allow_half_open = allow_half_open;
385
386 js_set_native_ptr(obj, socket);
387 js_set_native_tag(obj, NET_SOCKET_NATIVE_TAG);
388 js_set(js, obj, "remoteAddress", js_mkundef());
389 js_set(js, obj, "remotePort", js_mkundef());
390 js_set(js, obj, "remoteFamily", js_mkundef());
391 js_set(js, obj, "localAddress", js_mkundef());
392 js_set(js, obj, "localPort", js_mkundef());
393 js_set(js, obj, "localFamily", js_mkundef());
394 net_socket_sync_state(socket);
395
396 return socket;
397}
398
399static void net_socket_attach_conn(net_socket_t *socket, ant_conn_t *conn) {
400 if (!socket || !conn) return;
401
402 socket->conn = conn;
403 ant_conn_set_user_data(conn, socket);
404 if (ant_conn_has_remote_addr(conn)) {
405 js_set(socket->js, socket->obj, "remoteAddress", js_mkstr(socket->js, ant_conn_remote_addr(conn), strlen(ant_conn_remote_addr(conn))));
406 js_set(socket->js, socket->obj, "remotePort", js_mknum(ant_conn_remote_port(conn)));
407 js_set(socket->js, socket->obj, "remoteFamily", js_mkstr(socket->js, ant_conn_remote_family(conn), strlen(ant_conn_remote_family(conn))));
408 }
409 if (ant_conn_has_local_addr(conn)) {
410 js_set(socket->js, socket->obj, "localAddress", js_mkstr(socket->js, ant_conn_local_addr(conn), strlen(ant_conn_local_addr(conn))));
411 js_set(socket->js, socket->obj, "localPort", js_mknum(ant_conn_local_port(conn)));
412 js_set(socket->js, socket->obj, "localFamily", js_mkstr(socket->js, ant_conn_local_family(conn), strlen(ant_conn_local_family(conn))));
413 }
414 net_socket_sync_state(socket);
415}
416
417static net_server_t *net_server_create(ant_t *js) {
418 ant_value_t obj = js_mkobj(js);
419 ant_value_t proto = js_instance_proto_from_new_target(js, g_net_server_proto);
420 net_server_t *server = calloc(1, sizeof(*server));
421
422 if (!server) return NULL;
423 if (is_object_type(proto)) js_set_proto_init(obj, proto);
424
425 server->js = js;
426 server->obj = obj;
427 server->host = strdup("0.0.0.0");
428 server->backlog = 511;
429 if (!server->host) {
430 free(server);
431 return NULL;
432 }
433
434 js_set_native_ptr(obj, server);
435 js_set_native_tag(obj, NET_SERVER_NATIVE_TAG);
436 net_server_sync_state(server);
437 return server;
438}
439
440static ant_value_t net_not_implemented(ant_t *js, const char *what) {
441 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not implemented yet", what);
442}
443
444static ant_value_t net_isIP(ant_t *js, ant_value_t *args, int nargs) {
445 size_t len = 0;
446 const char *host = NULL;
447 struct in_addr addr4;
448 struct in6_addr addr6;
449
450 if (nargs < 1) return js_mknum(0);
451 host = js_getstr(js, args[0], &len);
452 if (!host) return js_mknum(0);
453
454 if (inet_pton(AF_INET, host, &addr4) == 1) return js_mknum(4);
455 if (inet_pton(AF_INET6, host, &addr6) == 1) return js_mknum(6);
456 return js_mknum(0);
457}
458
459static ant_value_t net_isIPv4(ant_t *js, ant_value_t *args, int nargs) {
460 if (js_getnum(net_isIP(js, args, nargs)) == 4.0) return js_true;
461 return js_false;
462}
463
464static ant_value_t net_isIPv6(ant_t *js, ant_value_t *args, int nargs) {
465 if (js_getnum(net_isIP(js, args, nargs)) == 6.0) return js_true;
466 return js_false;
467}
468
469static void net_server_apply_options(ant_t *js, net_server_t *server, ant_value_t options) {
470 ant_value_t value = 0;
471
472 if (!server || vtype(options) != T_OBJ) return;
473
474 value = js_get(js, options, "allowHalfOpen");
475 if (vtype(value) != T_UNDEF) server->allow_half_open = js_truthy(js, value);
476
477 value = js_get(js, options, "pauseOnConnect");
478 if (vtype(value) != T_UNDEF) server->pause_on_connect = js_truthy(js, value);
479
480 value = js_get(js, options, "noDelay");
481 if (vtype(value) != T_UNDEF) server->no_delay = js_truthy(js, value);
482
483 value = js_get(js, options, "keepAlive");
484 if (vtype(value) != T_UNDEF) server->keep_alive = js_truthy(js, value);
485
486 value = js_get(js, options, "keepAliveInitialDelay");
487 if (vtype(value) == T_NUM && js_getnum(value) > 0)
488 server->keep_alive_initial_delay_secs = (unsigned int)(js_getnum(value) / 1000.0);
489
490 value = js_get(js, options, "backlog");
491 if (vtype(value) == T_NUM && js_getnum(value) > 0)
492 server->backlog = (int)js_getnum(value);
493}
494
495static void net_socket_on_read(ant_conn_t *conn, ssize_t nread, void *user_data) {
496 net_socket_t *socket = (net_socket_t *)ant_conn_get_user_data(conn);
497 const char *buffer = NULL;
498 ant_value_t chunk = 0;
499 size_t total = 0;
500 size_t offset = 0;
501
502 if (!socket || !conn || nread <= 0) return;
503
504 total = ant_conn_buffer_len(conn);
505 if ((size_t)nread > total) return;
506 offset = total - (size_t)nread;
507 buffer = ant_conn_buffer(conn) + offset;
508
509 if (vtype(socket->encoding) == T_STR)
510 chunk = js_mkstr(socket->js, buffer, (size_t)nread);
511 else chunk = net_make_buffer_chunk(socket->js, buffer, (size_t)nread);
512
513 ant_conn_consume(conn, total);
514 net_socket_sync_state(socket);
515 net_emit(socket->js, socket->obj, "data", &chunk, 1);
516}
517
518static void net_socket_on_end(ant_conn_t *conn, void *user_data) {
519 net_socket_t *socket = (net_socket_t *)ant_conn_get_user_data(conn);
520
521 if (!socket) return;
522 net_emit(socket->js, socket->obj, "end", NULL, 0);
523 if (!socket->allow_half_open && socket->conn) ant_conn_shutdown(socket->conn);
524}
525
526static void net_socket_on_error(ant_conn_t *conn, int status, void *user_data) {
527 net_socket_t *socket = (net_socket_t *)ant_conn_get_user_data(conn);
528
529 if (!socket) return;
530 socket->had_error = true; {
531 ant_value_t err = js_mkerr_typed(socket->js, JS_ERR_TYPE, "%s", uv_strerror(status));
532 net_emit(socket->js, socket->obj, "error", &err, 1);
533 }
534}
535
536static void net_socket_on_timeout(ant_conn_t *conn, void *user_data) {
537 net_socket_t *socket = (net_socket_t *)ant_conn_get_user_data(conn);
538 if (!socket) return;
539 net_emit(socket->js, socket->obj, "timeout", NULL, 0);
540}
541
542static void net_server_on_conn_close(ant_conn_t *conn, void *user_data) {
543 net_socket_t *socket = (net_socket_t *)ant_conn_get_user_data(conn);
544 ant_value_t arg = 0;
545
546 if (!socket) return;
547 arg = js_bool(socket->had_error);
548 socket->conn = NULL;
549 socket->destroyed = true;
550 net_socket_sync_state(socket);
551 net_emit(socket->js, socket->obj, "close", &arg, 1);
552 ant_conn_set_user_data(conn, NULL);
553 net_socket_detach(socket);
554}
555
556static void net_server_on_listener_close(ant_listener_t *listener, void *user_data) {
557 net_server_maybe_finish_close((net_server_t *)user_data);
558}
559
560static void net_server_on_accept(ant_listener_t *listener, ant_conn_t *conn, void *user_data) {
561 net_server_t *server = (net_server_t *)user_data;
562 net_socket_t *socket = NULL;
563 ant_value_t arg = 0;
564
565 if (!server || !conn) return;
566
567 if (server->max_connections > 0 && net_server_socket_count(server) >= server->max_connections) {
568 ant_conn_close(conn);
569 return;
570 }
571
572 socket = net_socket_create(server->js, server->allow_half_open);
573 if (!socket) {
574 ant_conn_close(conn);
575 return;
576 }
577
578 socket->server = server;
579 socket->next_in_server = server->sockets;
580 server->sockets = socket;
581 net_add_active_socket(socket);
582 net_socket_attach_conn(socket, conn);
583
584 if (server->no_delay) ant_conn_set_no_delay(conn, true);
585 if (server->keep_alive)
586 ant_conn_set_keep_alive(conn, true, server->keep_alive_initial_delay_secs);
587 if (server->pause_on_connect) ant_conn_pause_read(conn);
588
589 arg = socket->obj;
590 net_emit(server->js, server->obj, "connection", &arg, 1);
591}
592
593static bool net_server_parse_host(const char *input, const char **out) {
594 if (!input || !*input) {
595 *out = "0.0.0.0";
596 return true;
597 }
598 *out = input;
599 return true;
600}
601
602static ant_value_t js_net_socket_ctor(ant_t *js, ant_value_t *args, int nargs) {
603 net_socket_t *socket = NULL;
604 bool allow_half_open = false;
605
606 if (nargs > 0 && vtype(args[0]) == T_OBJ) {
607 ant_value_t value = js_get(js, args[0], "allowHalfOpen");
608 allow_half_open = js_truthy(js, value);
609 }
610
611 socket = net_socket_create(js, allow_half_open);
612 if (!socket) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
613 return socket->obj;
614}
615
616static ant_value_t js_net_socket_address(ant_t *js, ant_value_t *args, int nargs) {
617 net_socket_t *socket = net_require_socket(js, js_getthis(js));
618 ant_value_t out = js_mkobj(js);
619
620 if (!socket) return js->thrown_value;
621 if (!socket || !socket->conn || !ant_conn_has_local_addr(socket->conn)) return out;
622 js_set(js, out, "address", js_mkstr(js, ant_conn_local_addr(socket->conn), strlen(ant_conn_local_addr(socket->conn))));
623 js_set(js, out, "port", js_mknum(ant_conn_local_port(socket->conn)));
624 js_set(js, out, "family", js_mkstr(js, ant_conn_local_family(socket->conn), strlen(ant_conn_local_family(socket->conn))));
625 return out;
626}
627
628static ant_value_t js_net_socket_pause(ant_t *js, ant_value_t *args, int nargs) {
629 net_socket_t *socket = net_require_socket(js, js_getthis(js));
630 if (!socket) return js->thrown_value;
631 if (socket && socket->conn) ant_conn_pause_read(socket->conn);
632 return js_getthis(js);
633}
634
635static ant_value_t js_net_socket_resume(ant_t *js, ant_value_t *args, int nargs) {
636 net_socket_t *socket = net_require_socket(js, js_getthis(js));
637 if (!socket) return js->thrown_value;
638 if (socket && socket->conn) ant_conn_resume_read(socket->conn);
639 return js_getthis(js);
640}
641
642static ant_value_t js_net_socket_setEncoding(ant_t *js, ant_value_t *args, int nargs) {
643 net_socket_t *socket = net_require_socket(js, js_getthis(js));
644 if (!socket) return js->thrown_value;
645 socket->encoding = nargs > 0 && vtype(args[0]) != T_UNDEF ? js_tostring_val(js, args[0]) : js_mkundef();
646 return js_getthis(js);
647}
648
649static ant_value_t js_net_socket_setTimeout(ant_t *js, ant_value_t *args, int nargs) {
650 net_socket_t *socket = net_require_socket(js, js_getthis(js));
651 double timeout = 0;
652
653 if (!socket) return js->thrown_value;
654 if (nargs > 0 && vtype(args[0]) == T_NUM) timeout = js_getnum(args[0]);
655 if (socket->conn) ant_conn_set_timeout_ms(socket->conn, timeout > 0 ? (uint64_t)timeout : 0);
656 net_socket_sync_state(socket);
657
658 if (nargs > 1 && is_callable(args[1]))
659 net_add_listener(js, socket->obj, "timeout", args[1], true);
660
661 return js_getthis(js);
662}
663
664static ant_value_t js_net_socket_setNoDelay(ant_t *js, ant_value_t *args, int nargs) {
665 net_socket_t *socket = net_require_socket(js, js_getthis(js));
666 bool enable = nargs == 0 || js_truthy(js, args[0]);
667 if (!socket) return js->thrown_value;
668 if (socket && socket->conn) ant_conn_set_no_delay(socket->conn, enable);
669 return js_getthis(js);
670}
671
672static ant_value_t js_net_socket_setKeepAlive(ant_t *js, ant_value_t *args, int nargs) {
673 net_socket_t *socket = net_require_socket(js, js_getthis(js));
674 bool enable = nargs > 0 && js_truthy(js, args[0]);
675 unsigned int delay = nargs > 1 && vtype(args[1]) == T_NUM ? (unsigned int)(js_getnum(args[1]) / 1000.0) : 0;
676 if (!socket) return js->thrown_value;
677 if (socket && socket->conn) ant_conn_set_keep_alive(socket->conn, enable, delay);
678 return js_getthis(js);
679}
680
681static ant_value_t js_net_socket_ref(ant_t *js, ant_value_t *args, int nargs) {
682 net_socket_t *socket = net_require_socket(js, js_getthis(js));
683 if (!socket) return js->thrown_value;
684 if (socket && socket->conn) ant_conn_ref(socket->conn);
685 return js_getthis(js);
686}
687
688static ant_value_t js_net_socket_unref(ant_t *js, ant_value_t *args, int nargs) {
689 net_socket_t *socket = net_require_socket(js, js_getthis(js));
690 if (!socket) return js->thrown_value;
691 if (socket && socket->conn) ant_conn_unref(socket->conn);
692 return js_getthis(js);
693}
694
695static ant_value_t js_net_socket_write(ant_t *js, ant_value_t *args, int nargs) {
696 net_socket_t *socket = net_require_socket(js, js_getthis(js));
697 net_write_args_t parsed;
698 char *copy = NULL;
699
700 if (!socket) return js->thrown_value;
701 if (!socket->conn) return js_false;
702 if (!net_parse_write_args(js, args, nargs, &parsed)) return parsed.error;
703 if (parsed.len == 0) return js_true;
704
705 copy = malloc(parsed.len);
706 if (!copy) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
707
708 memcpy(copy, parsed.bytes, parsed.len);
709 if (ant_conn_write(socket->conn, copy, parsed.len, NULL, NULL) != 0) return js_false;
710
711 GC_ROOT_SAVE(root_mark, js);
712 GC_ROOT_PIN(js, parsed.callback);
713 net_socket_sync_state(socket);
714
715 if (is_callable(parsed.callback)) net_call_value(js, parsed.callback, js_mkundef(), NULL, 0);
716 GC_ROOT_RESTORE(js, root_mark);
717
718 return js_true;
719}
720
721static ant_value_t js_net_socket_end(ant_t *js, ant_value_t *args, int nargs) {
722 net_socket_t *socket = net_require_socket(js, js_getthis(js));
723 net_write_args_t parsed;
724 ant_value_t result = js_getthis(js);
725
726 if (!socket) return js->thrown_value;
727 if (!socket->conn) return result;
728 if (!net_parse_write_args(js, args, nargs, &parsed)) return parsed.error;
729
730 GC_ROOT_SAVE(root_mark, js);
731 GC_ROOT_PIN(js, parsed.callback);
732
733 if (parsed.len > 0) {
734 ant_value_t write_result = js_net_socket_write(js, args, nargs);
735 if (is_err(write_result)) {
736 GC_ROOT_RESTORE(js, root_mark);
737 return write_result;
738 }}
739
740 ant_conn_shutdown(socket->conn);
741 if (is_callable(parsed.callback)) net_call_value(js, parsed.callback, js_mkundef(), NULL, 0);
742 GC_ROOT_RESTORE(js, root_mark);
743
744 return result;
745}
746
747static ant_value_t js_net_socket_destroy(ant_t *js, ant_value_t *args, int nargs) {
748 net_socket_t *socket = net_require_socket(js, js_getthis(js));
749 if (!socket) return js->thrown_value;
750
751 if (nargs > 0 && vtype(args[0]) != T_UNDEF && vtype(args[0]) != T_NULL) {
752 ant_value_t err = args[0];
753 socket->had_error = true;
754 net_emit(js, socket->obj, "error", &err, 1);
755 }
756
757 if (socket->conn) ant_conn_close(socket->conn);
758 return js_getthis(js);
759}
760
761static ant_value_t js_net_socket_connect(ant_t *js, ant_value_t *args, int nargs) {
762 return net_not_implemented(js, "net.Socket.connect");
763}
764
765static ant_value_t js_net_server_ctor(ant_t *js, ant_value_t *args, int nargs) {
766 net_server_t *server = net_server_create(js);
767
768 if (!server) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
769 if (nargs > 0 && vtype(args[0]) == T_OBJ) net_server_apply_options(js, server, args[0]);
770 if (nargs > 0 && is_callable(args[0])) net_add_listener(js, server->obj, "connection", args[0], false);
771 else if (nargs > 1 && is_callable(args[1])) net_add_listener(js, server->obj, "connection", args[1], false);
772
773 return server->obj;
774}
775
776static ant_value_t net_server_bind_listener(
777 ant_t *js,
778 net_server_t *server,
779 const net_listen_args_t *parsed,
780 const ant_listener_callbacks_t *callbacks
781) {
782 uv_loop_t *loop = uv_default_loop();
783 int rc = 0;
784
785 if (parsed->path) {
786 server->path = strdup(parsed->path);
787 if (!server->path) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
788
789 rc = ant_listener_listen_pipe(
790 &server->listener, loop, server->path,
791 server->backlog, 0, callbacks, server
792 );
793 } else {
794 const char *host = parsed->host;
795 net_server_parse_host(host, &host);
796
797 free(server->host);
798 server->host = strdup(host ? host : "0.0.0.0");
799 if (!server->host) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
800
801 rc = ant_listener_listen_tcp(
802 &server->listener, loop, server->host, parsed->port,
803 server->backlog, 0, callbacks, server
804 );
805 }
806
807 if (rc != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc));
808 return js_mkundef();
809}
810
811static ant_value_t js_net_server_listen(ant_t *js, ant_value_t *args, int nargs) {
812 net_server_t *server = net_require_server(js, js_getthis(js));
813 ant_listener_callbacks_t callbacks = {0};
814 net_listen_args_t parsed;
815
816 if (!server) return js->thrown_value;
817 if (server->listening) return js_mkerr_typed(js, JS_ERR_TYPE, "Server is already listening");
818 if (!net_parse_listen_args(js, args, nargs, &parsed)) return parsed.error;
819
820 free(server->path);
821 server->path = NULL;
822 server->port = parsed.port;
823 server->backlog = parsed.backlog > 0 ? parsed.backlog : server->backlog;
824
825 callbacks.on_accept = net_server_on_accept;
826 callbacks.on_read = net_socket_on_read;
827 callbacks.on_end = net_socket_on_end;
828 callbacks.on_error = net_socket_on_error;
829 callbacks.on_timeout = net_socket_on_timeout;
830 callbacks.on_conn_close = net_server_on_conn_close;
831 callbacks.on_listener_close = net_server_on_listener_close;
832
833 GC_ROOT_SAVE(root_mark, js);
834 GC_ROOT_PIN(js, parsed.callback);
835
836 ant_value_t bind_result = net_server_bind_listener(js, server, &parsed, &callbacks);
837 if (is_err(bind_result)) {
838 GC_ROOT_RESTORE(js, root_mark);
839 return bind_result;
840 }
841
842 server->port = ant_listener_port(&server->listener);
843 server->listening = true;
844 server->closing = false;
845 net_server_sync_state(server);
846 net_add_active_server(server);
847
848 if (is_callable(parsed.callback)) net_call_value(js, parsed.callback, js_mkundef(), NULL, 0);
849 net_emit(js, server->obj, "listening", NULL, 0);
850 GC_ROOT_RESTORE(js, root_mark);
851
852 return js_getthis(js);
853}
854
855static ant_value_t js_net_server_close(ant_t *js, ant_value_t *args, int nargs) {
856 net_server_t *server = net_require_server(js, js_getthis(js));
857
858 if (!server) return js->thrown_value;
859 if (nargs > 0 && is_callable(args[0])) net_add_listener(js, server->obj, "close", args[0], true);
860
861 if (!server->listening && !server->closing) {
862 if (nargs > 0 && is_callable(args[0])) {
863 ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "Server is not running");
864 net_call_value(js, args[0], js_mkundef(), &err, 1);
865 }
866 return js_getthis(js);
867 }
868
869 server->closing = true;
870 ant_listener_stop(&server->listener, false);
871 net_server_maybe_finish_close(server);
872 return js_getthis(js);
873}
874
875static ant_value_t js_net_server_address(ant_t *js, ant_value_t *args, int nargs) {
876 net_server_t *server = net_require_server(js, js_getthis(js));
877 ant_value_t out = js_mknull();
878
879 if (!server) return js->thrown_value;
880 if (!server || !server->listening) return out;
881 if (server->path) return js_mkstr(js, server->path, strlen(server->path));
882
883 struct sockaddr_storage saddr;
884 int saddr_len = sizeof(saddr);
885
886 if (uv_tcp_getsockname(&server->listener.handle.tcp, (struct sockaddr *)&saddr, &saddr_len) == 0) {
887 char addr_str[INET6_ADDRSTRLEN] = {0};
888 const char *family = "IPv4";
889 int port = server->port;
890
891 if (saddr.ss_family == AF_INET) {
892 struct sockaddr_in *sa = (struct sockaddr_in *)&saddr;
893 inet_ntop(AF_INET, &sa->sin_addr, addr_str, sizeof(addr_str));
894 port = ntohs(sa->sin_port);
895 } else if (saddr.ss_family == AF_INET6) {
896 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&saddr;
897 inet_ntop(AF_INET6, &sa6->sin6_addr, addr_str, sizeof(addr_str));
898 port = ntohs(sa6->sin6_port);
899 family = "IPv6";
900 }
901
902 out = js_mkobj(js);
903 js_set(js, out, "port", js_mknum((double)port));
904 js_set(js, out, "family", js_mkstr(js, family, strlen(family)));
905 js_set(js, out, "address", js_mkstr(js, addr_str, strlen(addr_str)));
906
907 return out;
908 }
909
910 struct in6_addr addr6;
911 out = js_mkobj(js);
912 js_set(js, out, "port", js_mknum(server->port));
913
914 if (server->host && inet_pton(AF_INET6, server->host, &addr6) == 1) {
915 char normalized[INET6_ADDRSTRLEN];
916 inet_ntop(AF_INET6, &addr6, normalized, sizeof(normalized));
917 js_set(js, out, "family", js_mkstr(js, "IPv6", 4));
918 js_set(js, out, "address", js_mkstr(js, normalized, strlen(normalized)));
919 } else {
920 const char *h = server->host ? server->host : "0.0.0.0";
921 js_set(js, out, "family", js_mkstr(js, "IPv4", 4));
922 js_set(js, out, "address", js_mkstr(js, h, strlen(h)));
923 }
924
925 return out;
926}
927
928static ant_value_t js_net_server_getConnections(ant_t *js, ant_value_t *args, int nargs) {
929 net_server_t *server = net_require_server(js, js_getthis(js));
930 ant_value_t cb = nargs > 0 ? args[0] : js_mkundef();
931 ant_value_t argv[2] = { js_mknull(), js_mknum((double)net_server_socket_count(server)) };
932
933 if (!server) return js->thrown_value;
934 if (is_callable(cb)) net_call_value(js, cb, js_mkundef(), argv, 2);
935 return js_getthis(js);
936}
937
938static ant_value_t js_net_server_ref(ant_t *js, ant_value_t *args, int nargs) {
939 net_server_t *server = net_require_server(js, js_getthis(js));
940 if (!server) return js->thrown_value;
941 if (server) ant_listener_ref(&server->listener);
942 return js_getthis(js);
943}
944
945static ant_value_t js_net_server_unref(ant_t *js, ant_value_t *args, int nargs) {
946 net_server_t *server = net_require_server(js, js_getthis(js));
947 if (!server) return js->thrown_value;
948 if (server) ant_listener_unref(&server->listener);
949 return js_getthis(js);
950}
951
952static ant_value_t js_net_createServer(ant_t *js, ant_value_t *args, int nargs) {
953 return js_net_server_ctor(js, args, nargs);
954}
955
956static ant_value_t js_net_createConnection(ant_t *js, ant_value_t *args, int nargs) {
957 return net_not_implemented(js, "net.createConnection");
958}
959
960static ant_value_t js_net_connect(ant_t *js, ant_value_t *args, int nargs) {
961 return js_net_createConnection(js, args, nargs);
962}
963
964static ant_value_t js_net_getDefaultAutoSelectFamily(ant_t *js, ant_value_t *args, int nargs) {
965 return js_bool(g_default_auto_select_family);
966}
967
968static ant_value_t js_net_setDefaultAutoSelectFamily(ant_t *js, ant_value_t *args, int nargs) {
969 if (nargs > 0) g_default_auto_select_family = js_truthy(js, args[0]);
970 return js_mkundef();
971}
972
973static ant_value_t js_net_getDefaultAutoSelectFamilyAttemptTimeout(ant_t *js, ant_value_t *args, int nargs) {
974 return js_mknum(g_default_auto_select_family_attempt_timeout);
975}
976
977static ant_value_t js_net_setDefaultAutoSelectFamilyAttemptTimeout(ant_t *js, ant_value_t *args, int nargs) {
978 if (nargs > 0 && vtype(args[0]) == T_NUM) {
979 double value = js_getnum(args[0]);
980 if (value > 0 && value < 10) value = 10;
981 if (value > 0) g_default_auto_select_family_attempt_timeout = value;
982 }
983 return js_mkundef();
984}
985
986static void net_init_constructors(ant_t *js) {
987 ant_value_t events = 0;
988 ant_value_t ee_ctor = 0;
989 ant_value_t ee_proto = 0;
990
991 if (g_net_server_ctor && g_net_socket_ctor) return;
992
993 events = events_library(js);
994 ee_ctor = js_get(js, events, "EventEmitter");
995 ee_proto = js_get(js, ee_ctor, "prototype");
996
997 g_net_socket_proto = js_mkobj(js);
998 js_set_proto_init(g_net_socket_proto, ee_proto);
999 js_set(js, g_net_socket_proto, "address", js_mkfun(js_net_socket_address));
1000 js_set(js, g_net_socket_proto, "pause", js_mkfun(js_net_socket_pause));
1001 js_set(js, g_net_socket_proto, "resume", js_mkfun(js_net_socket_resume));
1002 js_set(js, g_net_socket_proto, "setEncoding", js_mkfun(js_net_socket_setEncoding));
1003 js_set(js, g_net_socket_proto, "setTimeout", js_mkfun(js_net_socket_setTimeout));
1004 js_set(js, g_net_socket_proto, "setNoDelay", js_mkfun(js_net_socket_setNoDelay));
1005 js_set(js, g_net_socket_proto, "setKeepAlive", js_mkfun(js_net_socket_setKeepAlive));
1006 js_set(js, g_net_socket_proto, "write", js_mkfun(js_net_socket_write));
1007 js_set(js, g_net_socket_proto, "end", js_mkfun(js_net_socket_end));
1008 js_set(js, g_net_socket_proto, "destroy", js_mkfun(js_net_socket_destroy));
1009 js_set(js, g_net_socket_proto, "connect", js_mkfun(js_net_socket_connect));
1010 js_set(js, g_net_socket_proto, "ref", js_mkfun(js_net_socket_ref));
1011 js_set(js, g_net_socket_proto, "unref", js_mkfun(js_net_socket_unref));
1012 js_set_sym(js, g_net_socket_proto, get_toStringTag_sym(), js_mkstr(js, "Socket", 6));
1013 g_net_socket_ctor = js_make_ctor(js, js_net_socket_ctor, g_net_socket_proto, "Socket", 6);
1014
1015 g_net_server_proto = js_mkobj(js);
1016 js_set_proto_init(g_net_server_proto, ee_proto);
1017 js_set(js, g_net_server_proto, "listen", js_mkfun(js_net_server_listen));
1018 js_set(js, g_net_server_proto, "close", js_mkfun(js_net_server_close));
1019 js_set(js, g_net_server_proto, "address", js_mkfun(js_net_server_address));
1020 js_set(js, g_net_server_proto, "getConnections", js_mkfun(js_net_server_getConnections));
1021 js_set(js, g_net_server_proto, "ref", js_mkfun(js_net_server_ref));
1022 js_set(js, g_net_server_proto, "unref", js_mkfun(js_net_server_unref));
1023 js_set_sym(js, g_net_server_proto, get_toStringTag_sym(), js_mkstr(js, "Server", 6));
1024 g_net_server_ctor = js_make_ctor(js, js_net_server_ctor, g_net_server_proto, "Server", 6);
1025}
1026
1027ant_value_t net_library(ant_t *js) {
1028 ant_value_t lib = js_mkobj(js);
1029
1030 net_init_constructors(js);
1031 js_set(js, lib, "Server", g_net_server_ctor);
1032 js_set(js, lib, "Socket", g_net_socket_ctor);
1033 js_set(js, lib, "createServer", js_mkfun(js_net_createServer));
1034 js_set(js, lib, "createConnection", js_mkfun(js_net_createConnection));
1035 js_set(js, lib, "connect", js_mkfun(js_net_connect));
1036 js_set(js, lib, "isIP", js_mkfun(net_isIP));
1037 js_set(js, lib, "isIPv4", js_mkfun(net_isIPv4));
1038 js_set(js, lib, "isIPv6", js_mkfun(net_isIPv6));
1039 js_set(js, lib, "getDefaultAutoSelectFamily", js_mkfun(js_net_getDefaultAutoSelectFamily));
1040 js_set(js, lib, "setDefaultAutoSelectFamily", js_mkfun(js_net_setDefaultAutoSelectFamily));
1041 js_set(js, lib, "getDefaultAutoSelectFamilyAttemptTimeout", js_mkfun(js_net_getDefaultAutoSelectFamilyAttemptTimeout));
1042 js_set(js, lib, "setDefaultAutoSelectFamilyAttemptTimeout", js_mkfun(js_net_setDefaultAutoSelectFamilyAttemptTimeout));
1043 js_set(js, lib, "default", lib);
1044 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "net", 3));
1045
1046 return lib;
1047}
1048
1049void gc_mark_net(ant_t *js, gc_mark_fn mark) {
1050 net_server_t *server = NULL;
1051 net_socket_t *socket = NULL;
1052
1053 if (g_net_server_proto) mark(js, g_net_server_proto);
1054 if (g_net_socket_proto) mark(js, g_net_socket_proto);
1055 if (g_net_server_ctor) mark(js, g_net_server_ctor);
1056 if (g_net_socket_ctor) mark(js, g_net_socket_ctor);
1057
1058 for (server = g_active_servers; server; server = server->next_active)
1059 mark(js, server->obj);
1060
1061 for (socket = g_active_sockets; socket; socket = socket->next_active) {
1062 mark(js, socket->obj);
1063 if (vtype(socket->encoding) != T_UNDEF) mark(js, socket->encoding);
1064 }
1065}