xbintsc 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/src/codegen/generator/calls.js +11 -1
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/module.js +2 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +19 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +2 -0
- package/dist/src/codegen/generator/tables.js +25 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.js +1 -0
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/node/buffer/index.d.ts +10 -0
- package/dist/src/extensions/node/buffer/index.js +15 -0
- package/dist/src/extensions/node/buffer/index.js.map +1 -0
- package/dist/src/extensions/node/dgram/index.d.ts +8 -0
- package/dist/src/extensions/node/dgram/index.js +13 -0
- package/dist/src/extensions/node/dgram/index.js.map +1 -0
- package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
- package/dist/src/extensions/node/fs/fs-ops.js +18 -0
- package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
- package/dist/src/extensions/node/fs/index.d.ts +3 -1
- package/dist/src/extensions/node/fs/index.js +11 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
- package/dist/src/extensions/node/fs/write-file.js +8 -0
- package/dist/src/extensions/node/fs/write-file.js.map +1 -0
- package/dist/src/extensions/node/fs-promises/index.d.ts +10 -0
- package/dist/src/extensions/node/fs-promises/index.js +37 -0
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -0
- package/dist/src/extensions/node/http/index.d.ts +10 -0
- package/dist/src/extensions/node/http/index.js +18 -0
- package/dist/src/extensions/node/http/index.js.map +1 -0
- package/dist/src/extensions/node/index.d.ts +3 -1
- package/dist/src/extensions/node/index.js +28 -4
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/net/index.d.ts +9 -0
- package/dist/src/extensions/node/net/index.js +14 -0
- package/dist/src/extensions/node/net/index.js.map +1 -0
- package/dist/src/extensions/node/os/index.d.ts +9 -0
- package/dist/src/extensions/node/os/index.js +14 -0
- package/dist/src/extensions/node/os/index.js.map +1 -0
- package/dist/src/extensions/node/path/index.d.ts +10 -0
- package/dist/src/extensions/node/path/index.js +15 -0
- package/dist/src/extensions/node/path/index.js.map +1 -0
- package/dist/src/extensions/node/process/index.d.ts +10 -0
- package/dist/src/extensions/node/process/index.js +15 -0
- package/dist/src/extensions/node/process/index.js.map +1 -0
- package/dist/src/extensions/node/stream/index.d.ts +10 -0
- package/dist/src/extensions/node/stream/index.js +15 -0
- package/dist/src/extensions/node/stream/index.js.map +1 -0
- package/dist/tests/e2e/compile.test.js +55 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/node-modules.test.d.ts +9 -0
- package/dist/tests/e2e/node-modules.test.js +179 -0
- package/dist/tests/e2e/node-modules.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +9 -3
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/ext_node/buffer/buffer.c +569 -0
- package/runtime/ext_node/dgram/dgram.c +337 -0
- package/runtime/ext_node/fs/fs_common.h +166 -0
- package/runtime/ext_node/fs/fs_ops.c +342 -0
- package/runtime/ext_node/fs/promises.c +60 -0
- package/runtime/ext_node/fs/read_file.c +12 -6
- package/runtime/ext_node/fs/write_file.c +47 -0
- package/runtime/ext_node/http/http.c +760 -0
- package/runtime/ext_node/net/net.c +546 -0
- package/runtime/ext_node/node_common.h +305 -0
- package/runtime/ext_node/os/os.c +165 -0
- package/runtime/ext_node/path/path.c +324 -0
- package/runtime/ext_node/process/process.c +137 -0
- package/runtime/ext_node/stream/stream.c +348 -0
- package/runtime/rt.h +19 -0
- package/runtime/xt_containers.c +2 -0
- package/runtime/xt_io.c +9 -0
- package/runtime/xt_loop.c +117 -0
- package/scripts/build-runtime.mjs +14 -0
- package/src/codegen/generator/calls.ts +13 -0
- package/src/codegen/generator/module.ts +2 -0
- package/src/codegen/generator/state.ts +19 -0
- package/src/codegen/generator/tables.ts +26 -0
- package/src/driver/compiler.ts +1 -0
- package/src/extensions/node/buffer/index.ts +17 -0
- package/src/extensions/node/dgram/index.ts +15 -0
- package/src/extensions/node/fs/fs-ops.ts +20 -0
- package/src/extensions/node/fs/index.ts +11 -3
- package/src/extensions/node/fs/write-file.ts +10 -0
- package/src/extensions/node/fs-promises/index.ts +41 -0
- package/src/extensions/node/http/index.ts +20 -0
- package/src/extensions/node/index.ts +28 -4
- package/src/extensions/node/net/index.ts +16 -0
- package/src/extensions/node/os/index.ts +16 -0
- package/src/extensions/node/path/index.ts +17 -0
- package/src/extensions/node/process/index.ts +17 -0
- package/src/extensions/node/stream/index.ts +17 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Node.js `net` for xbintsc.
|
|
3
|
+
*
|
|
4
|
+
* TCP client and server support built directly on the core event loop
|
|
5
|
+
* (`xt_loop_add`). `net.connect` performs a conventional blocking connect and
|
|
6
|
+
* then registers the socket for readiness notifications, while
|
|
7
|
+
* `net.createServer(...).listen(...)` registers the listening descriptor and
|
|
8
|
+
* accepts connections as they arrive. Sockets are EventEmitters that expose
|
|
9
|
+
* `write`/`end`/`destroy` and emit `data`, `end`, `close`, `connect`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "../node_common.h"
|
|
13
|
+
|
|
14
|
+
#include <errno.h>
|
|
15
|
+
#include <string.h>
|
|
16
|
+
|
|
17
|
+
#if defined(_WIN32)
|
|
18
|
+
#include <winsock2.h>
|
|
19
|
+
#include <ws2tcpip.h>
|
|
20
|
+
typedef int xt_socklen_t;
|
|
21
|
+
#define xt_close_socket closesocket
|
|
22
|
+
#else
|
|
23
|
+
#include <arpa/inet.h>
|
|
24
|
+
#include <fcntl.h>
|
|
25
|
+
#include <netdb.h>
|
|
26
|
+
#include <netinet/in.h>
|
|
27
|
+
#include <netinet/tcp.h>
|
|
28
|
+
#include <sys/socket.h>
|
|
29
|
+
#include <sys/types.h>
|
|
30
|
+
#include <unistd.h>
|
|
31
|
+
typedef socklen_t xt_socklen_t;
|
|
32
|
+
#define xt_close_socket close
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
/* Byte access helpers exported by the Buffer module. */
|
|
36
|
+
int xt_node_is_buffer(xt_value value);
|
|
37
|
+
unsigned char *xt_node_buffer_bytes(xt_value value, size_t *outLength);
|
|
38
|
+
|
|
39
|
+
static xt_value xt_net_socket_proto(void);
|
|
40
|
+
static xt_value xt_net_server_proto(void);
|
|
41
|
+
|
|
42
|
+
/* -- platform helpers ----------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
static void xt_net_set_nonblocking(int fd) {
|
|
45
|
+
#if defined(_WIN32)
|
|
46
|
+
u_long mode = 1;
|
|
47
|
+
ioctlsocket(fd, FIONBIO, &mode);
|
|
48
|
+
#else
|
|
49
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
50
|
+
if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
51
|
+
#endif
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static xt_value *xt_net_box(xt_value value) {
|
|
55
|
+
xt_value *handle = (xt_value *)malloc(sizeof(xt_value));
|
|
56
|
+
*handle = value;
|
|
57
|
+
return handle;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static int xt_net_fd(xt_value object) {
|
|
61
|
+
return (int)xt_to_number(xt_object_get_cstr(object, "__fd"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static unsigned char *xt_net_bytes(xt_value data, size_t *outLength) {
|
|
65
|
+
if (xt_node_is_buffer(data)) return xt_node_buffer_bytes(data, outLength);
|
|
66
|
+
xt_value text = xt_to_string(data);
|
|
67
|
+
const char *string = xt_string_data(text);
|
|
68
|
+
size_t length = string ? (size_t)xt_string_length(xt_as_string(text)) : 0;
|
|
69
|
+
unsigned char *bytes = (unsigned char *)malloc(length + 1);
|
|
70
|
+
if (length > 0 && string) memcpy(bytes, string, length);
|
|
71
|
+
bytes[length] = '\0';
|
|
72
|
+
*outLength = length;
|
|
73
|
+
return bytes;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static int xt_net_send_all(int fd, const unsigned char *data, size_t length) {
|
|
77
|
+
size_t sent = 0;
|
|
78
|
+
while (sent < length) {
|
|
79
|
+
#if defined(_WIN32)
|
|
80
|
+
int written = send(fd, (const char *)data + sent, (int)(length - sent), 0);
|
|
81
|
+
#else
|
|
82
|
+
ssize_t written = send(fd, data + sent, length - sent, 0);
|
|
83
|
+
#endif
|
|
84
|
+
if (written > 0) {
|
|
85
|
+
sent += (size_t)written;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
|
89
|
+
fd_set writeSet;
|
|
90
|
+
FD_ZERO(&writeSet);
|
|
91
|
+
FD_SET(fd, &writeSet);
|
|
92
|
+
struct timeval timeout;
|
|
93
|
+
timeout.tv_sec = 2;
|
|
94
|
+
timeout.tv_usec = 0;
|
|
95
|
+
if (select(fd + 1, NULL, &writeSet, NULL, &timeout) <= 0) return -1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
return -1;
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* -- socket read loop ----------------------------------------------------- */
|
|
104
|
+
|
|
105
|
+
static void xt_net_socket_read(void *userdata, int events) {
|
|
106
|
+
(void)events;
|
|
107
|
+
xt_value socket = *(xt_value *)userdata;
|
|
108
|
+
int fd = xt_net_fd(socket);
|
|
109
|
+
if (fd < 0) return;
|
|
110
|
+
|
|
111
|
+
unsigned char buffer[65536];
|
|
112
|
+
#if defined(_WIN32)
|
|
113
|
+
int count = recv(fd, (char *)buffer, (int)sizeof(buffer), 0);
|
|
114
|
+
#else
|
|
115
|
+
ssize_t count = recv(fd, buffer, sizeof(buffer), 0);
|
|
116
|
+
#endif
|
|
117
|
+
if (count > 0) {
|
|
118
|
+
const char *encoding = xt_node_encoding(xt_object_get_cstr(socket, "__encoding"));
|
|
119
|
+
xt_value chunk = xt_node_encode(buffer, (size_t)count, encoding);
|
|
120
|
+
xt_node_emit1(socket, "data", chunk);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (count == 0) {
|
|
124
|
+
xt_loop_remove(fd);
|
|
125
|
+
xt_close_socket(fd);
|
|
126
|
+
xt_node_set(socket, "__fd", xt_number(-1));
|
|
127
|
+
xt_node_set(socket, "__closed", xt_bool(1));
|
|
128
|
+
xt_node_emit0(socket, "end");
|
|
129
|
+
xt_node_emit0(socket, "close");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) return;
|
|
133
|
+
xt_loop_remove(fd);
|
|
134
|
+
xt_close_socket(fd);
|
|
135
|
+
xt_node_set(socket, "__fd", xt_number(-1));
|
|
136
|
+
xt_node_set(socket, "__closed", xt_bool(1));
|
|
137
|
+
xt_value error = xt_string_from_cstr("ECONNRESET");
|
|
138
|
+
xt_node_emit1(socket, "error", error);
|
|
139
|
+
xt_node_emit0(socket, "close");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static xt_value xt_net_socket_from_fd(int fd, const struct sockaddr *peer, xt_socklen_t peerLength) {
|
|
143
|
+
xt_value socket = xt_object_new_with_proto(xt_net_socket_proto());
|
|
144
|
+
xt_node_set(socket, "__fd", xt_number((double)fd));
|
|
145
|
+
xt_node_set(socket, "__connected", xt_bool(1));
|
|
146
|
+
xt_node_set(socket, "__readable", xt_array_new(0, NULL));
|
|
147
|
+
|
|
148
|
+
char host[NI_MAXHOST];
|
|
149
|
+
char service[NI_MAXSERV];
|
|
150
|
+
if (peer && getnameinfo(peer, peerLength, host, sizeof(host), service, sizeof(service),
|
|
151
|
+
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
|
152
|
+
xt_node_set(socket, "remoteAddress", xt_string_from_cstr(host));
|
|
153
|
+
xt_node_set(socket, "remotePort", xt_number((double)atoi(service)));
|
|
154
|
+
xt_node_set(socket, "remoteFamily", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
xt_net_set_nonblocking(fd);
|
|
158
|
+
xt_loop_add(fd, XT_IO_READ, xt_net_socket_read, xt_net_box(socket));
|
|
159
|
+
return socket;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* -- socket methods ------------------------------------------------------- */
|
|
163
|
+
|
|
164
|
+
static xt_value xt_net_socket_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
165
|
+
(void)env;
|
|
166
|
+
int fd = xt_net_fd(thisValue);
|
|
167
|
+
if (fd < 0) {
|
|
168
|
+
xt_value callback = xt_arg(argc, argv, argc > 1 ? 2 : 1);
|
|
169
|
+
xt_value error = xt_string_from_cstr("ERR_SOCKET_CLOSED");
|
|
170
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
171
|
+
return xt_bool(0);
|
|
172
|
+
}
|
|
173
|
+
size_t length = 0;
|
|
174
|
+
unsigned char *bytes = xt_net_bytes(xt_arg(argc, argv, 0), &length);
|
|
175
|
+
xt_net_send_all(fd, bytes, length);
|
|
176
|
+
free(bytes);
|
|
177
|
+
xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 1)) ? xt_arg(argc, argv, 1) : xt_arg(argc, argv, 2);
|
|
178
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
179
|
+
return xt_bool(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static xt_value xt_net_socket_end(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
183
|
+
(void)env;
|
|
184
|
+
int fd = xt_net_fd(thisValue);
|
|
185
|
+
if (fd < 0) return thisValue;
|
|
186
|
+
if (argc > 0 && !xt_truthy(xt_is_nullish(argv[0]))) {
|
|
187
|
+
xt_value writeFn = xt_object_get_cstr(thisValue, "write");
|
|
188
|
+
if (XT_IS_FUNCTION(writeFn)) xt_call_with_this(writeFn, thisValue, 1, &argv[0]);
|
|
189
|
+
}
|
|
190
|
+
#if defined(_WIN32)
|
|
191
|
+
shutdown(fd, SD_SEND);
|
|
192
|
+
#else
|
|
193
|
+
shutdown(fd, SHUT_WR);
|
|
194
|
+
#endif
|
|
195
|
+
return thisValue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static xt_value xt_net_socket_destroy(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
199
|
+
(void)env;
|
|
200
|
+
(void)argc;
|
|
201
|
+
(void)argv;
|
|
202
|
+
int fd = xt_net_fd(thisValue);
|
|
203
|
+
if (fd >= 0) {
|
|
204
|
+
xt_loop_remove(fd);
|
|
205
|
+
xt_close_socket(fd);
|
|
206
|
+
}
|
|
207
|
+
xt_node_set(thisValue, "__fd", xt_number(-1));
|
|
208
|
+
if (!xt_truthy(xt_object_get_cstr(thisValue, "__closed"))) {
|
|
209
|
+
xt_node_set(thisValue, "__closed", xt_bool(1));
|
|
210
|
+
xt_node_emit0(thisValue, "close");
|
|
211
|
+
}
|
|
212
|
+
return thisValue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static xt_value xt_net_socket_set_encoding(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
216
|
+
(void)env;
|
|
217
|
+
xt_node_set(thisValue, "__encoding", xt_arg(argc, argv, 0));
|
|
218
|
+
return thisValue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static xt_value xt_net_socket_pause(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
222
|
+
(void)env;
|
|
223
|
+
(void)argc;
|
|
224
|
+
(void)argv;
|
|
225
|
+
return thisValue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static xt_value xt_net_socket_resume(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
229
|
+
(void)env;
|
|
230
|
+
(void)argc;
|
|
231
|
+
(void)argv;
|
|
232
|
+
return thisValue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static xt_value xt_net_socket_noop(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
236
|
+
(void)env;
|
|
237
|
+
(void)argc;
|
|
238
|
+
(void)argv;
|
|
239
|
+
return thisValue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static xt_value xt_net_socket_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
243
|
+
(void)env;
|
|
244
|
+
(void)argc;
|
|
245
|
+
(void)argv;
|
|
246
|
+
int fd = xt_net_fd(thisValue);
|
|
247
|
+
if (fd < 0) return xt_null();
|
|
248
|
+
struct sockaddr_storage storage;
|
|
249
|
+
xt_socklen_t length = sizeof(storage);
|
|
250
|
+
if (getsockname(fd, (struct sockaddr *)&storage, &length) != 0) return xt_null();
|
|
251
|
+
char host[NI_MAXHOST];
|
|
252
|
+
char service[NI_MAXSERV];
|
|
253
|
+
if (getnameinfo((struct sockaddr *)&storage, length, host, sizeof(host), service, sizeof(service),
|
|
254
|
+
NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
255
|
+
return xt_null();
|
|
256
|
+
xt_value object = xt_object_new();
|
|
257
|
+
xt_node_set(object, "address", xt_string_from_cstr(host));
|
|
258
|
+
xt_node_set(object, "port", xt_number((double)atoi(service)));
|
|
259
|
+
xt_node_set(object, "family", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
|
|
260
|
+
return object;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* -- server --------------------------------------------------------------- */
|
|
264
|
+
|
|
265
|
+
static void xt_net_server_accept(void *userdata, int events) {
|
|
266
|
+
(void)events;
|
|
267
|
+
xt_value server = *(xt_value *)userdata;
|
|
268
|
+
int listenFd = xt_net_fd(server);
|
|
269
|
+
if (listenFd < 0) return;
|
|
270
|
+
for (;;) {
|
|
271
|
+
struct sockaddr_storage storage;
|
|
272
|
+
xt_socklen_t length = sizeof(storage);
|
|
273
|
+
int fd = accept(listenFd, (struct sockaddr *)&storage, &length);
|
|
274
|
+
if (fd < 0) break;
|
|
275
|
+
xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
|
|
276
|
+
xt_node_emit1(server, "connection", socket);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Parse the many listen() signatures into a port + host. */
|
|
281
|
+
static void xt_net_parse_listen(int32_t argc, xt_value *argv, int *port, const char **host, xt_value *callback) {
|
|
282
|
+
*port = 0;
|
|
283
|
+
*host = "0.0.0.0";
|
|
284
|
+
*callback = XT_UNDEFINED;
|
|
285
|
+
int index = 0;
|
|
286
|
+
if (argc > 0 && XT_IS_OBJECT(argv[0])) {
|
|
287
|
+
xt_value portValue = xt_object_get_cstr(argv[0], "port");
|
|
288
|
+
if (!xt_truthy(xt_is_nullish(portValue))) *port = (int)xt_to_number(portValue);
|
|
289
|
+
xt_value hostValue = xt_object_get_cstr(argv[0], "host");
|
|
290
|
+
if (XT_IS_STRING(hostValue)) *host = xt_string_data(hostValue);
|
|
291
|
+
index = 1;
|
|
292
|
+
} else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
|
|
293
|
+
*port = (int)xt_to_number(argv[0]);
|
|
294
|
+
index = 1;
|
|
295
|
+
if (argc > 1 && XT_IS_STRING(argv[1])) {
|
|
296
|
+
*host = xt_string_data(argv[1]);
|
|
297
|
+
index = 2;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
for (int32_t i = index; i < argc; i++) {
|
|
301
|
+
if (XT_IS_FUNCTION(argv[i])) *callback = argv[i];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static xt_value xt_net_server_listen(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
306
|
+
(void)env;
|
|
307
|
+
int port = 0;
|
|
308
|
+
const char *host = "0.0.0.0";
|
|
309
|
+
xt_value callback = XT_UNDEFINED;
|
|
310
|
+
xt_net_parse_listen(argc, argv, &port, &host, &callback);
|
|
311
|
+
|
|
312
|
+
char service[16];
|
|
313
|
+
snprintf(service, sizeof(service), "%d", port);
|
|
314
|
+
struct addrinfo hints;
|
|
315
|
+
memset(&hints, 0, sizeof(hints));
|
|
316
|
+
hints.ai_family = AF_UNSPEC;
|
|
317
|
+
hints.ai_socktype = SOCK_STREAM;
|
|
318
|
+
hints.ai_flags = AI_PASSIVE;
|
|
319
|
+
struct addrinfo *result = NULL;
|
|
320
|
+
if (getaddrinfo(host, service, &hints, &result) != 0) {
|
|
321
|
+
if (XT_IS_FUNCTION(callback)) {
|
|
322
|
+
xt_value error = xt_string_from_cstr("EADDRNOTAVAIL");
|
|
323
|
+
xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
324
|
+
}
|
|
325
|
+
return thisValue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
int listenFd = -1;
|
|
329
|
+
for (struct addrinfo *info = result; info; info = info->ai_next) {
|
|
330
|
+
int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
|
|
331
|
+
if (fd < 0) continue;
|
|
332
|
+
int yes = 1;
|
|
333
|
+
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
|
|
334
|
+
if (bind(fd, info->ai_addr, info->ai_addrlen) == 0 && listen(fd, 511) == 0) {
|
|
335
|
+
listenFd = fd;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
xt_close_socket(fd);
|
|
339
|
+
}
|
|
340
|
+
freeaddrinfo(result);
|
|
341
|
+
|
|
342
|
+
if (listenFd < 0) {
|
|
343
|
+
if (XT_IS_FUNCTION(callback)) {
|
|
344
|
+
xt_value error = xt_string_from_cstr("EADDRINUSE");
|
|
345
|
+
xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
346
|
+
}
|
|
347
|
+
return thisValue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
xt_net_set_nonblocking(listenFd);
|
|
351
|
+
xt_node_set(thisValue, "__fd", xt_number((double)listenFd));
|
|
352
|
+
xt_node_set(thisValue, "__listening", xt_bool(1));
|
|
353
|
+
xt_loop_add(listenFd, XT_IO_READ, xt_net_server_accept, xt_net_box(thisValue));
|
|
354
|
+
|
|
355
|
+
xt_node_emit0(thisValue, "listening");
|
|
356
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
357
|
+
return thisValue;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
static xt_value xt_net_server_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
361
|
+
(void)env;
|
|
362
|
+
int fd = xt_net_fd(thisValue);
|
|
363
|
+
if (fd >= 0) {
|
|
364
|
+
xt_loop_remove(fd);
|
|
365
|
+
xt_close_socket(fd);
|
|
366
|
+
}
|
|
367
|
+
xt_node_set(thisValue, "__fd", xt_number(-1));
|
|
368
|
+
xt_node_set(thisValue, "__listening", xt_bool(0));
|
|
369
|
+
xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 0)) ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
|
|
370
|
+
xt_node_emit0(thisValue, "close");
|
|
371
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
372
|
+
return thisValue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
static xt_value xt_net_server_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
376
|
+
return xt_net_socket_address(thisValue, env, argc, argv);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
static xt_value xt_net_server_get_connections(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
380
|
+
(void)thisValue;
|
|
381
|
+
(void)env;
|
|
382
|
+
(void)argc;
|
|
383
|
+
(void)argv;
|
|
384
|
+
xt_value callback = xt_arg(argc, argv, 0);
|
|
385
|
+
if (XT_IS_FUNCTION(callback)) {
|
|
386
|
+
xt_value count = xt_number(0);
|
|
387
|
+
xt_value args[2];
|
|
388
|
+
args[0] = xt_null();
|
|
389
|
+
args[1] = count;
|
|
390
|
+
xt_call_with_this(callback, xt_undefined(), 2, args);
|
|
391
|
+
}
|
|
392
|
+
return xt_undefined();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* -- construction --------------------------------------------------------- */
|
|
396
|
+
|
|
397
|
+
xt_value xt_net_socket_ctor(int32_t argc, xt_value *argv) {
|
|
398
|
+
(void)argc;
|
|
399
|
+
(void)argv;
|
|
400
|
+
xt_value socket = xt_object_new_with_proto(xt_net_socket_proto());
|
|
401
|
+
xt_node_set(socket, "__fd", xt_number(-1));
|
|
402
|
+
xt_node_set(socket, "__readable", xt_array_new(0, NULL));
|
|
403
|
+
return socket;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
xt_value xt_net_server_ctor(int32_t argc, xt_value *argv) {
|
|
407
|
+
xt_value server = xt_object_new_with_proto(xt_net_server_proto());
|
|
408
|
+
xt_node_set(server, "__fd", xt_number(-1));
|
|
409
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
410
|
+
if (XT_IS_FUNCTION(argv[i])) {
|
|
411
|
+
xt_array_push(xt_node_listeners(server, "connection", 1), argv[i]);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return server;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* -- connect / static API ------------------------------------------------- */
|
|
419
|
+
|
|
420
|
+
static int xt_net_connect_fd(const char *host, int port) {
|
|
421
|
+
char service[16];
|
|
422
|
+
snprintf(service, sizeof(service), "%d", port);
|
|
423
|
+
struct addrinfo hints;
|
|
424
|
+
memset(&hints, 0, sizeof(hints));
|
|
425
|
+
hints.ai_family = AF_UNSPEC;
|
|
426
|
+
hints.ai_socktype = SOCK_STREAM;
|
|
427
|
+
struct addrinfo *result = NULL;
|
|
428
|
+
if (getaddrinfo(host, service, &hints, &result) != 0) return -1;
|
|
429
|
+
int fd = -1;
|
|
430
|
+
for (struct addrinfo *info = result; info; info = info->ai_next) {
|
|
431
|
+
int candidate = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
|
|
432
|
+
if (candidate < 0) continue;
|
|
433
|
+
if (connect(candidate, info->ai_addr, info->ai_addrlen) == 0) {
|
|
434
|
+
fd = candidate;
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
xt_close_socket(candidate);
|
|
438
|
+
}
|
|
439
|
+
freeaddrinfo(result);
|
|
440
|
+
return fd;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
xt_value xt_node_net_connect(int32_t argc, xt_value *argv) {
|
|
444
|
+
int port = 0;
|
|
445
|
+
const char *host = "127.0.0.1";
|
|
446
|
+
xt_value callback = XT_UNDEFINED;
|
|
447
|
+
int index = 0;
|
|
448
|
+
if (argc > 0 && XT_IS_OBJECT(argv[0])) {
|
|
449
|
+
xt_value portValue = xt_object_get_cstr(argv[0], "port");
|
|
450
|
+
if (!xt_truthy(xt_is_nullish(portValue))) port = (int)xt_to_number(portValue);
|
|
451
|
+
xt_value hostValue = xt_object_get_cstr(argv[0], "host");
|
|
452
|
+
if (XT_IS_STRING(hostValue)) host = xt_string_data(hostValue);
|
|
453
|
+
index = 1;
|
|
454
|
+
} else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
|
|
455
|
+
port = (int)xt_to_number(argv[0]);
|
|
456
|
+
index = 1;
|
|
457
|
+
if (argc > 1 && XT_IS_STRING(argv[1])) {
|
|
458
|
+
host = xt_string_data(argv[1]);
|
|
459
|
+
index = 2;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
for (int32_t i = index; i < argc; i++) {
|
|
463
|
+
if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
int fd = xt_net_connect_fd(host, port);
|
|
467
|
+
if (fd < 0) {
|
|
468
|
+
xt_value socket = xt_net_socket_ctor(0, NULL);
|
|
469
|
+
xt_value error = xt_string_from_cstr("ECONNREFUSED");
|
|
470
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
471
|
+
else xt_node_emit1(socket, "error", error);
|
|
472
|
+
return socket;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
struct sockaddr_storage storage;
|
|
476
|
+
xt_socklen_t length = sizeof(storage);
|
|
477
|
+
getpeername(fd, (struct sockaddr *)&storage, &length);
|
|
478
|
+
xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
|
|
479
|
+
xt_node_set(socket, "__connecting", xt_bool(0));
|
|
480
|
+
xt_node_emit0(socket, "connect");
|
|
481
|
+
xt_node_emit0(socket, "ready");
|
|
482
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
483
|
+
return socket;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
xt_value xt_net_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
487
|
+
const char *fn = xt_string_data(name);
|
|
488
|
+
if (!fn) return xt_undefined();
|
|
489
|
+
if (strcmp(fn, "createServer") == 0) {
|
|
490
|
+
xt_value server = xt_net_server_ctor(0, NULL);
|
|
491
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
492
|
+
if (XT_IS_FUNCTION(argv[i])) {
|
|
493
|
+
xt_array_push(xt_node_listeners(server, "connection", 1), argv[i]);
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return server;
|
|
498
|
+
}
|
|
499
|
+
if (strcmp(fn, "connect") == 0 || strcmp(fn, "createConnection") == 0) return xt_node_net_connect(argc, argv);
|
|
500
|
+
if (strcmp(fn, "isIP") == 0 || strcmp(fn, "isIPv4") == 0 || strcmp(fn, "isIPv6") == 0) {
|
|
501
|
+
const char *input = xt_string_data(xt_to_string(xt_arg(argc, argv, 0)));
|
|
502
|
+
if (!input) return xt_number(0);
|
|
503
|
+
if (strcmp(fn, "isIPv4") == 0) return xt_bool(strchr(input, '.') && !strchr(input, ':'));
|
|
504
|
+
if (strcmp(fn, "isIPv6") == 0) return xt_bool(strchr(input, ':') != NULL);
|
|
505
|
+
if (strchr(input, ':')) return xt_number(6);
|
|
506
|
+
if (strchr(input, '.')) return xt_number(4);
|
|
507
|
+
return xt_number(0);
|
|
508
|
+
}
|
|
509
|
+
return xt_undefined();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* -- prototypes ----------------------------------------------------------- */
|
|
513
|
+
|
|
514
|
+
static xt_value xt_net_socket_proto(void) {
|
|
515
|
+
static xt_value proto = 0;
|
|
516
|
+
if (proto) return proto;
|
|
517
|
+
proto = xt_object_new();
|
|
518
|
+
xt_node_install_emitter(proto);
|
|
519
|
+
xt_node_define_method(proto, "write", (void *)xt_net_socket_write);
|
|
520
|
+
xt_node_define_method(proto, "end", (void *)xt_net_socket_end);
|
|
521
|
+
xt_node_define_method(proto, "destroy", (void *)xt_net_socket_destroy);
|
|
522
|
+
xt_node_define_method(proto, "setEncoding", (void *)xt_net_socket_set_encoding);
|
|
523
|
+
xt_node_define_method(proto, "pause", (void *)xt_net_socket_pause);
|
|
524
|
+
xt_node_define_method(proto, "resume", (void *)xt_net_socket_resume);
|
|
525
|
+
xt_node_define_method(proto, "setNoDelay", (void *)xt_net_socket_noop);
|
|
526
|
+
xt_node_define_method(proto, "setKeepAlive", (void *)xt_net_socket_noop);
|
|
527
|
+
xt_node_define_method(proto, "setTimeout", (void *)xt_net_socket_noop);
|
|
528
|
+
xt_node_define_method(proto, "ref", (void *)xt_net_socket_noop);
|
|
529
|
+
xt_node_define_method(proto, "unref", (void *)xt_net_socket_noop);
|
|
530
|
+
xt_node_define_method(proto, "address", (void *)xt_net_socket_address);
|
|
531
|
+
return proto;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
static xt_value xt_net_server_proto(void) {
|
|
535
|
+
static xt_value proto = 0;
|
|
536
|
+
if (proto) return proto;
|
|
537
|
+
proto = xt_object_new();
|
|
538
|
+
xt_node_install_emitter(proto);
|
|
539
|
+
xt_node_define_method(proto, "listen", (void *)xt_net_server_listen);
|
|
540
|
+
xt_node_define_method(proto, "close", (void *)xt_net_server_close);
|
|
541
|
+
xt_node_define_method(proto, "address", (void *)xt_net_server_address);
|
|
542
|
+
xt_node_define_method(proto, "getConnections", (void *)xt_net_server_get_connections);
|
|
543
|
+
xt_node_define_method(proto, "ref", (void *)xt_net_socket_noop);
|
|
544
|
+
xt_node_define_method(proto, "unref", (void *)xt_net_socket_noop);
|
|
545
|
+
return proto;
|
|
546
|
+
}
|