@bytecodealliance/preview2-shim 0.0.21 → 0.14.1

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.
Files changed (44) hide show
  1. package/README.md +4 -14
  2. package/lib/browser/cli.js +2 -4
  3. package/lib/browser/clocks.js +15 -27
  4. package/lib/browser/filesystem.js +2 -30
  5. package/lib/browser/http.js +1 -3
  6. package/lib/browser/io.js +4 -2
  7. package/lib/common/assert.js +7 -0
  8. package/lib/io/calls.js +64 -0
  9. package/lib/io/worker-http.js +95 -0
  10. package/lib/io/worker-io.js +322 -0
  11. package/lib/io/worker-thread.js +569 -0
  12. package/lib/nodejs/cli.js +45 -59
  13. package/lib/nodejs/clocks.js +13 -27
  14. package/lib/nodejs/filesystem.js +539 -459
  15. package/lib/nodejs/http.js +440 -173
  16. package/lib/nodejs/index.js +4 -1
  17. package/lib/nodejs/io.js +1 -0
  18. package/lib/nodejs/sockets/socket-common.js +116 -0
  19. package/lib/nodejs/sockets/socketopts-bindings.js +94 -0
  20. package/lib/nodejs/sockets/tcp-socket-impl.js +794 -0
  21. package/lib/nodejs/sockets/udp-socket-impl.js +628 -0
  22. package/lib/nodejs/sockets/wasi-sockets.js +320 -0
  23. package/lib/nodejs/sockets.js +11 -200
  24. package/lib/synckit/index.js +4 -2
  25. package/package.json +1 -5
  26. package/types/interfaces/wasi-cli-terminal-input.d.ts +4 -0
  27. package/types/interfaces/wasi-cli-terminal-output.d.ts +4 -0
  28. package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +19 -6
  29. package/types/interfaces/wasi-filesystem-types.d.ts +1 -178
  30. package/types/interfaces/wasi-http-outgoing-handler.d.ts +2 -2
  31. package/types/interfaces/wasi-http-types.d.ts +412 -82
  32. package/types/interfaces/wasi-io-error.d.ts +16 -0
  33. package/types/interfaces/wasi-io-poll.d.ts +19 -8
  34. package/types/interfaces/wasi-io-streams.d.ts +26 -46
  35. package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +9 -21
  36. package/types/interfaces/wasi-sockets-network.d.ts +4 -0
  37. package/types/interfaces/wasi-sockets-tcp.d.ts +75 -18
  38. package/types/interfaces/wasi-sockets-udp-create-socket.d.ts +1 -1
  39. package/types/interfaces/wasi-sockets-udp.d.ts +282 -193
  40. package/types/wasi-cli-command.d.ts +28 -28
  41. package/types/wasi-http-proxy.d.ts +12 -12
  42. package/lib/common/io.js +0 -183
  43. package/lib/common/make-request.js +0 -30
  44. package/types/interfaces/wasi-clocks-timezone.d.ts +0 -56
@@ -0,0 +1,628 @@
1
+ /**
2
+ * @typedef {import("../../types/interfaces/wasi-sockets-network").Network} Network
3
+ * @typedef {import("../../types/interfaces/wasi-sockets-network").IpSocketAddress} IpSocketAddress
4
+ * @typedef {import("../../types/interfaces/wasi-sockets-network").IpAddressFamily} IpAddressFamily
5
+ * @typedef {import("../../types/interfaces/wasi-sockets-udp").Datagram} Datagram
6
+ * @typedef {import("../../types/interfaces/wasi-io-poll-poll").Pollable} Pollable
7
+ */
8
+
9
+ // See: https://github.com/nodejs/node/blob/main/src/udp_wrap.cc
10
+ const { UDP, SendWrap } = process.binding("udp_wrap");
11
+ import { isIP } from "node:net";
12
+ import { assert } from "../../common/assert.js";
13
+ import { pollableCreate } from "../../io/worker-io.js";
14
+ import { cappedUint32, deserializeIpAddress, serializeIpAddress } from "./socket-common.js";
15
+
16
+ const symbolDispose = Symbol.dispose || Symbol.for("dispose");
17
+ const symbolSocketState = Symbol.SocketInternalState || Symbol.for("SocketInternalState");
18
+ const symbolOperations = Symbol.SocketOperationsState || Symbol.for("SocketOperationsState");
19
+
20
+ // TODO: move to a common
21
+ const SocketConnectionState = {
22
+ Error: "Error",
23
+ Closed: "Closed",
24
+ Connecting: "Connecting",
25
+ Connected: "Connected",
26
+ Listening: "Listening",
27
+ };
28
+
29
+ // see https://github.com/libuv/libuv/blob/master/docs/src/udp.rst
30
+ // TODO: move to a common
31
+ const Flags = {
32
+ UV_UDP_IPV6ONLY: 1,
33
+ UV_UDP_REUSEADDR: 4,
34
+ };
35
+
36
+ // TODO: move to a common
37
+ const BufferSizeFlags = {
38
+ SO_RCVBUF: true,
39
+ SO_SNDBUF: false,
40
+ };
41
+
42
+ // As a workaround, we store the bound address in a global map
43
+ // this is needed because 'address-in-use' is not always thrown when binding
44
+ // more than one socket to the same address
45
+ // TODO: remove this workaround when we figure out why!
46
+ const globalBoundAddresses = new Map();
47
+
48
+ export class IncomingDatagramStream {
49
+ static _create(socket) {
50
+ const stream = new IncomingDatagramStream(socket);
51
+ return stream;
52
+ }
53
+
54
+ #socket = null;
55
+ constructor(socket) {
56
+ this.#socket = socket;
57
+ }
58
+
59
+ /**
60
+ *
61
+ * @param {bigint} maxResults
62
+ * @returns {Datagram[]}
63
+ * @throws {invalid-state} The socket is not bound to any local address. (EINVAL)
64
+ * @throws {not-in-progress} The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
65
+ * @throws {remote-unreachable} The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
66
+ * @throws {connection-refused} The connection was refused. (ECONNREFUSED)
67
+ * @throws {would-block} There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN)
68
+ */
69
+ receive(maxResults) {
70
+ assert(self[symbolSocketState].isBound === false, "invalid-state");
71
+ assert(self[symbolOperations].receive === 0, "not-in-progress");
72
+
73
+ if (maxResults === 0n) {
74
+ return [];
75
+ }
76
+
77
+ // TODO: not sure this is the right API to use!
78
+ const socket = this.#socket;
79
+ socket.onmessage = (...args) => console.log("recv onmessage", args[2].toString());
80
+ socket.onerror = (err) => console.log("recv error", err);
81
+ socket.recvStart();
82
+ const datagrams = [];
83
+ return datagrams;
84
+ }
85
+
86
+ /**
87
+ *
88
+ * @returns {Pollable} A pollable which will resolve once the stream is ready to receive again.
89
+ */
90
+ subscribe() {
91
+ throw new Error("Not implemented");
92
+ }
93
+
94
+ [symbolDispose]() {
95
+ // TODO: stop receiving
96
+ }
97
+ }
98
+ const incomingDatagramStreamCreate = IncomingDatagramStream._create;
99
+ delete IncomingDatagramStream._create;
100
+
101
+ export class OutgoingDatagramStream {
102
+ static _create(socket) {
103
+ const stream = new OutgoingDatagramStream(socket);
104
+ return stream;
105
+ }
106
+
107
+ #socket = null;
108
+ constructor(socket) {
109
+ this.#socket = socket;
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @returns {bigint}
115
+ * @throws {invalid-state} The socket is not bound to any local address. (EINVAL)
116
+ */
117
+ checkSend() {
118
+ throw new Error("Not implemented");
119
+ }
120
+
121
+ /**
122
+ *
123
+ * @param {Datagram[]} datagrams
124
+ * @returns {bigint}
125
+ * @throws {invalid-argument} The `remote-address` has the wrong address family. (EAFNOSUPPORT)
126
+ * @throws {invalid-argument} `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
127
+ * @throws {invalid-argument} The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
128
+ * @throws {invalid-argument} The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
129
+ * @throws {invalid-argument} The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN)
130
+ * @throws {invalid-argument} The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
131
+ * @throws {remote-unreachable} The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
132
+ * @throws {connection-refused} The connection was refused. (ECONNREFUSED)
133
+ * @throws {datagram-too-large} The datagram is too large. (EMSGSIZE)
134
+ */
135
+ send(datagrams) {
136
+ const req = new SendWrap();
137
+ const doSend = (data, port, host, family) => {
138
+ // setting hasCallback to false will make send() synchronous
139
+ // TODO: handle async send
140
+ const hasCallback = false;
141
+ const socket = this.#socket;
142
+
143
+ let err = null;
144
+ if (family.toLocaleLowerCase() === "ipv4") {
145
+ err = socket.send(req, data, data.length, port, host, hasCallback);
146
+ } else if (family.toLocaleLowerCase() === "ipv6") {
147
+ err = socket.send6(req, data, data.length, port, host, hasCallback);
148
+ }
149
+ return err;
150
+ };
151
+
152
+ datagrams.forEach((datagram) => {
153
+ const { data, remoteAddress } = datagram;
154
+ const { tag: family, val } = remoteAddress;
155
+ const { /*address, */port } = val;
156
+ const err = doSend(data, port, serializeIpAddress(remoteAddress), family);
157
+ console.error({
158
+ err,
159
+ });
160
+ });
161
+ }
162
+
163
+ /**
164
+ *
165
+ * @returns {Pollable} A pollable which will resolve once the stream is ready to send again.
166
+ */
167
+ subscribe() {
168
+ throw new Error("Not implemented");
169
+ }
170
+
171
+ [symbolDispose]() {
172
+ // TODO: stop sending
173
+ }
174
+ }
175
+ const outgoingDatagramStreamCreate = OutgoingDatagramStream._create;
176
+ delete OutgoingDatagramStream._create;
177
+
178
+ export class UdpSocketImpl {
179
+ id = 1;
180
+ /** @type {UDP} */ #socket = null;
181
+ /** @type {Network} */ network = null;
182
+
183
+ // track in-progress operations
184
+ // counter must be 0 for the operation to be considered complete
185
+ // we increment the counter when the operation starts
186
+ // and decrement it when the operation finishes
187
+ [symbolOperations] = {
188
+ bind: 0,
189
+ connect: 0,
190
+ listen: 0,
191
+ accept: 0,
192
+ receive: 0,
193
+ send: 0,
194
+ };
195
+
196
+ [symbolSocketState] = {
197
+ lastErrorState: null,
198
+ isBound: false,
199
+ ipv6Only: false,
200
+ connectionState: SocketConnectionState.Closed,
201
+
202
+ // TODO: what these default values should be?
203
+ unicastHopLimit: 1,
204
+ receiveBufferSize: 1,
205
+ sendBufferSize: 1,
206
+ };
207
+
208
+ #socketOptions = {
209
+ family: "ipv4",
210
+ localAddress: undefined,
211
+ localPort: 0,
212
+ remoteAddress: undefined,
213
+ remotePort: 0,
214
+ };
215
+
216
+ /**
217
+ * @param {IpAddressFamily} addressFamily
218
+ * @returns {void}
219
+ */
220
+ constructor(addressFamily, id) {
221
+ this.id = id;
222
+ this.#socketOptions.family = addressFamily;
223
+
224
+ this.#socket = new UDP();
225
+ }
226
+
227
+ #cacheBoundAddress() {
228
+ let { localIpSocketAddress: boundAddress, localPort } = this.#socketOptions;
229
+ // when port is 0, the OS will assign an ephemeral port
230
+ // we need to get the actual port assigned by the OS
231
+ if (localPort === 0) {
232
+ boundAddress = this.localAddress();
233
+ }
234
+ globalBoundAddresses.set(serializeIpAddress(boundAddress, true), this.#socket);
235
+ }
236
+
237
+ /**
238
+ *
239
+ * @param {Network} network
240
+ * @param {IpAddressFamily} localAddress
241
+ * @returns {void}
242
+ * @throws {invalid-argument} The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
243
+ * @throws {invalid-state} The socket is already bound. (EINVAL)
244
+ */
245
+ startBind(network, localAddress) {
246
+ try {
247
+ assert(this[symbolSocketState].isBound, "invalid-state", "The socket is already bound");
248
+
249
+ const address = serializeIpAddress(localAddress);
250
+ const ipFamily = `ipv${isIP(address)}`;
251
+
252
+ assert(
253
+ this.#socketOptions.family.toLocaleLowerCase() !== ipFamily.toLocaleLowerCase(),
254
+ "invalid-argument",
255
+ "The `local-address` has the wrong address family"
256
+ );
257
+ assert(this[symbolSocketState].ipv6Only, "invalid-argument", "The `local-address` has the wrong address family");
258
+
259
+ const { port } = localAddress.val;
260
+ this.#socketOptions.localIpSocketAddress = localAddress;
261
+ this.#socketOptions.localAddress = address;
262
+ this.#socketOptions.localPort = port;
263
+ this.network = network;
264
+ this[symbolOperations].bind++;
265
+ this[symbolSocketState].lastErrorState = null;
266
+ } catch (err) {
267
+ this[symbolSocketState].lastErrorState = err;
268
+ throw err;
269
+ }
270
+ }
271
+
272
+ /**
273
+ *
274
+ * @returns {void}
275
+ * @throws {address-in-use} No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
276
+ * @throws {address-in-use} Address is already in use. (EADDRINUSE)
277
+ * @throws {address-not-bindable} `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
278
+ * @throws {not-in-progress} A `bind` operation is not in progress.
279
+ * @throws {would-block} Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
280
+ **/
281
+ finishBind() {
282
+ try {
283
+ assert(this[symbolOperations].bind === 0, "not-in-progress");
284
+
285
+ const { localAddress, localIpSocketAddress, localPort, family } = this.#socketOptions;
286
+ assert(isIP(localAddress) === 0, "address-not-bindable");
287
+ assert(globalBoundAddresses.has(serializeIpAddress(localIpSocketAddress, true)), "address-in-use");
288
+
289
+ let flags = 0;
290
+ if (this[symbolSocketState].ipv6Only) {
291
+ flags |= Flags.UV_UDP_IPV6ONLY;
292
+ }
293
+
294
+ let err = null;
295
+ let bind = "bind"; // ipv4
296
+ if (family.toLocaleLowerCase() === "ipv6") {
297
+ bind = "bind6";
298
+ }
299
+
300
+ err = this.#socket[bind](localAddress, localPort, flags);
301
+
302
+ if (err === 0) {
303
+ this[symbolSocketState].isBound = true;
304
+ } else {
305
+ assert(err === -22, "address-in-use");
306
+ assert(err === -48, "address-in-use"); // macos
307
+ assert(err === -49, "address-not-bindable");
308
+ assert(err === -98, "address-in-use"); // WSL
309
+ assert(err === -99, "address-not-bindable"); // EADDRNOTAVAIL
310
+ assert(true, "unknown", err);
311
+ }
312
+
313
+ this[symbolSocketState].lastErrorState = null;
314
+ this[symbolSocketState].isBound = true;
315
+ this[symbolOperations].bind--;
316
+
317
+ this.#cacheBoundAddress();
318
+ } catch (err) {
319
+ this[symbolSocketState].lastErrorState = err;
320
+ throw err;
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Alias for startBind() and finishBind()
326
+ */
327
+ bind(network, localAddress) {
328
+ this.startBind(network, localAddress);
329
+ this.finishBind();
330
+ }
331
+
332
+ /**
333
+ *
334
+ * @param {Network} network
335
+ * @param {IpAddressFamily | undefined} remoteAddress
336
+ * @returns {void}
337
+ * @throws {invalid-argument} The `remote-address` has the wrong address family. (EAFNOSUPPORT)
338
+ * @throws {invalid-argument} `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
339
+ * @throws {invalid-argument} The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
340
+ * @throws {invalid-argument} The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
341
+ * @throws {invalid-argument} The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
342
+ */
343
+ #startConnect(network, remoteAddress = undefined) {
344
+ this[symbolOperations].connect++;
345
+
346
+ if (remoteAddress === undefined || this[symbolSocketState].connectionState === SocketConnectionState.Connected) {
347
+ // TODO: should we reuse a connected socket if remoteAddress is undefined?
348
+ // See #finishConnect()
349
+ return;
350
+ }
351
+
352
+ const host = serializeIpAddress(remoteAddress);
353
+ const ipFamily = `ipv${isIP(host)}`;
354
+
355
+ assert(ipFamily.toLocaleLowerCase() === "ipv0", "invalid-argument");
356
+ assert(this.#socketOptions.family.toLocaleLowerCase() !== ipFamily.toLocaleLowerCase(), "invalid-argument");
357
+
358
+ const { port } = remoteAddress.val;
359
+ this.#socketOptions.remoteAddress = host; // can be undefined
360
+ this.#socketOptions.remotePort = port;
361
+
362
+ this.network = network;
363
+ }
364
+
365
+ /**
366
+ *
367
+ * @returns {void}
368
+ * @throws {address-in-use} Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
369
+ * @throws {not-in-progress} A `connect` operation is not in progress.
370
+ * @throws {would-block} Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
371
+ */
372
+ #finishConnect() {
373
+ // Note: remoteAddress can be undefined
374
+ const { remoteAddress, remotePort } = this.#socketOptions;
375
+ this[symbolSocketState].connectionState = SocketConnectionState.Connecting;
376
+
377
+ // TODO: figure out how to reuse a connected socket
378
+ const err = this.#socket.connect(remoteAddress ?? null, remotePort);
379
+
380
+ if (!err) {
381
+ this[symbolSocketState].connectionState = SocketConnectionState.Connected;
382
+ } else {
383
+ assert(err === -22, "invalid-argument");
384
+ assert(true, "unknown", err);
385
+ }
386
+
387
+ this[symbolOperations].connect--;
388
+ }
389
+
390
+ /**
391
+ * Alias for startBind() and finishBind()
392
+ */
393
+ #connect(network, remoteAddress = undefined) {
394
+ this.#startConnect(network, remoteAddress);
395
+ this.#finishConnect();
396
+ }
397
+
398
+ /**
399
+ *
400
+ * @param {IpSocketAddress | undefined} remoteAddress
401
+ * @returns {Array<IncomingDatagramStream, OutgoingDatagramStream>}
402
+ * @throws {invalid-argument} The `remote-address` has the wrong address family. (EAFNOSUPPORT)
403
+ * @throws {invalid-argument} remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
404
+ * @throws {invalid-argument} The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / :`). (EDESTADDRREQ, EADDRNOTAVAIL)
405
+ * @throws {invalid-argument} The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
406
+ * @throws {invalid-state} The socket is not bound.
407
+ * @throws {address-in-use} Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
408
+ * @throws {remote-unreachable} The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
409
+ * @throws {connection-refused} The connection was refused. (ECONNREFUSED)
410
+ */
411
+ stream(remoteAddress = undefined) {
412
+ assert(this[symbolSocketState].isBound === false, "invalid-state");
413
+ this.#connect(this.network, remoteAddress);
414
+ return [incomingDatagramStreamCreate(this.#socket), outgoingDatagramStreamCreate(this.#socket)];
415
+ }
416
+
417
+ /**
418
+ *
419
+ * Note: Concurrent invocations of this test can yield port to be 0 on Windows/WSL.
420
+ * @returns {IpSocketAddress}
421
+ * @throws {invalid-state} The socket is not bound to any local address.
422
+ */
423
+ localAddress() {
424
+ assert(this[symbolSocketState].isBound === false, "invalid-state");
425
+
426
+ const out = {};
427
+ this.#socket.getsockname(out);
428
+
429
+ const { address, port, family } = out;
430
+ this.#socketOptions.localAddress = address;
431
+ this.#socketOptions.localPort = port;
432
+ this.#socketOptions.family = family.toLocaleLowerCase();
433
+
434
+ return {
435
+ tag: family.toLocaleLowerCase(),
436
+ val: {
437
+ address: deserializeIpAddress(address, family),
438
+ port,
439
+ },
440
+ };
441
+ }
442
+
443
+ /**
444
+ *
445
+ * @returns {IpSocketAddress}
446
+ * @throws {invalid-state} The socket is not streaming to a specific remote address. (ENOTCONN)
447
+ */
448
+ remoteAddress() {
449
+ console.log("remoteAddress", this[symbolSocketState]);
450
+ assert(
451
+ this[symbolSocketState].connectionState !== SocketConnectionState.Connected,
452
+ "invalid-state",
453
+ "The socket is not streaming to a specific remote address"
454
+ );
455
+
456
+ const out = {};
457
+ this.#socket.getpeername(out);
458
+
459
+ assert(out.address === undefined, "invalid-state", "The socket is not streaming to a specific remote address");
460
+
461
+ const { address, port, family } = out;
462
+ this.#socketOptions.remoteAddress = address;
463
+ this.#socketOptions.remotePort = port;
464
+ this.#socketOptions.family = family.toLocaleLowerCase();
465
+
466
+ return {
467
+ tag: family.toLocaleLowerCase(),
468
+ val: {
469
+ address: deserializeIpAddress(address),
470
+ port,
471
+ },
472
+ };
473
+ }
474
+
475
+ /**
476
+ *
477
+ * @returns {IpAddressFamily}
478
+ */
479
+ addressFamily() {
480
+ return this.#socketOptions.family;
481
+ }
482
+
483
+ /**
484
+ *
485
+ * @returns {boolean}
486
+ * @throws {not-supported} (get/set) `this` socket is an IPv4 socket.
487
+ */
488
+ ipv6Only() {
489
+ assert(this.#socketOptions.family.toLocaleLowerCase() === "ipv4", "not-supported", "Socket is an IPv4 socket.");
490
+
491
+ return this[symbolSocketState].ipv6Only;
492
+ }
493
+
494
+ /**
495
+ *
496
+ * @param {boolean} value
497
+ * @returns {void}
498
+ * @throws {not-supported} (get/set) `this` socket is an IPv4 socket.
499
+ * @throws {invalid-state} (set) The socket is already bound.
500
+ * @throws {not-supported} (set) Host does not support dual-stack sockets. (Implementations are not required to.)
501
+ */
502
+ setIpv6Only(value) {
503
+ assert(
504
+ value === true && this.#socketOptions.family.toLocaleLowerCase() === "ipv4",
505
+ "not-supported",
506
+ "Socket is an IPv4 socket."
507
+ );
508
+ assert(this[symbolSocketState].isBound, "invalid-state", "The socket is already bound");
509
+
510
+ this[symbolSocketState].ipv6Only = value;
511
+ }
512
+
513
+ /**
514
+ *
515
+ * @returns {number}
516
+ */
517
+ unicastHopLimit() {
518
+ return this[symbolSocketState].unicastHopLimit;
519
+ }
520
+
521
+ /**
522
+ *
523
+ * @param {number} value
524
+ * @returns {void}
525
+ * @throws {invalid-argument} The TTL value must be 1 or higher.
526
+ */
527
+ setUnicastHopLimit(value) {
528
+ assert(value < 1, "invalid-argument", "The TTL value must be 1 or higher");
529
+
530
+ this.#socket.setTTL(value);
531
+ this[symbolSocketState].unicastHopLimit = value;
532
+ }
533
+
534
+ /**
535
+ *
536
+ * @returns {bigint}
537
+ */
538
+ receiveBufferSize() {
539
+ const exceptionInfo = {};
540
+ const value = this.#socket.bufferSize(0, BufferSizeFlags.SO_RCVBUF, exceptionInfo);
541
+
542
+ if (exceptionInfo.code === "EBADF") {
543
+ // TODO: handle the case where bad file descriptor is returned
544
+ // This happens when the socket is not bound
545
+ return this[symbolSocketState].receiveBufferSize;
546
+ }
547
+
548
+ console.log({
549
+ value,
550
+ });
551
+
552
+ return value;
553
+ }
554
+
555
+ /**
556
+ *
557
+ * @param {bigint} value
558
+ * @returns {void}
559
+ * @throws {invalid-argument} The provided value was 0.
560
+ */
561
+ setReceiveBufferSize(value) {
562
+ assert(value === 0n, "invalid-argument", "The provided value was 0");
563
+
564
+ const cappedValue = cappedUint32(value);
565
+ const exceptionInfo = {};
566
+ this.#socket.bufferSize(Number(cappedValue), BufferSizeFlags.SO_RCVBUF, exceptionInfo);
567
+ this[symbolSocketState].receiveBufferSize = cappedValue;
568
+ }
569
+
570
+ /**
571
+ *
572
+ * @returns {bigint}
573
+ */
574
+ sendBufferSize() {
575
+ const exceptionInfo = {};
576
+ const value = this.#socket.bufferSize(0, BufferSizeFlags.SO_SNDBUF, exceptionInfo);
577
+
578
+ if (exceptionInfo.code === "EBADF") {
579
+ // TODO: handle the case where bad file descriptor is returned
580
+ // This happens when the socket is not bound
581
+ return this[symbolSocketState].sendBufferSize;
582
+ }
583
+
584
+ return value;
585
+ }
586
+
587
+ /**
588
+ *
589
+ * @param {bigint} value
590
+ * @returns {void}
591
+ * @throws {invalid-argument} The provided value was 0.
592
+ */
593
+ setSendBufferSize(value) {
594
+ assert(value === 0n, "invalid-argument", "The provided value was 0");
595
+
596
+ const cappedValue = cappedUint32(value);
597
+ const exceptionInfo = {};
598
+ this.#socket.bufferSize(Number(cappedValue), BufferSizeFlags.SO_SNDBUF, exceptionInfo);
599
+ this[symbolSocketState].sendBufferSize = cappedValue;
600
+ }
601
+
602
+ /**
603
+ *
604
+ * @returns {Pollable}
605
+ */
606
+ subscribe() {
607
+ return pollableCreate(0);
608
+ }
609
+
610
+ [symbolDispose]() {
611
+ let err = null;
612
+ err = this.#socket.recvStop((...args) => {
613
+ console.log("stop recv", args);
614
+ });
615
+
616
+ if (err) {
617
+ assert(err === -9, "invalid-state", "Interface is not currently Up");
618
+ assert(err === -11, "not-in-progress");
619
+ assert(true, "", err);
620
+ }
621
+
622
+ this.#socket.close();
623
+ }
624
+
625
+ handle() {
626
+ return this.#socket;
627
+ }
628
+ }