@bytecodealliance/preview2-shim 0.14.0 → 0.14.2

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.
@@ -10,28 +10,44 @@
10
10
  * @typedef {import("../../../types/interfaces/wasi-clocks-monotonic-clock.js").Duration} Duration
11
11
  */
12
12
 
13
- import { isIP, Socket as NodeSocket } from "node:net";
13
+ import { isIP } from "node:net";
14
14
  import { platform } from "node:os";
15
15
  import { assert } from "../../common/assert.js";
16
16
  // import { streams } from "../io.js";
17
17
  // const { InputStream, OutputStream } = streams;
18
18
 
19
19
  const symbolDispose = Symbol.dispose || Symbol.for("dispose");
20
- const symbolSocketState = Symbol.SocketInternalState || Symbol.for("SocketInternalState");
21
- const symbolOperations = Symbol.SocketOperationsState || Symbol.for("SocketOperationsState");
20
+ const symbolSocketState =
21
+ Symbol.SocketInternalState || Symbol.for("SocketInternalState");
22
+ const symbolOperations =
23
+ Symbol.SocketOperationsState || Symbol.for("SocketOperationsState");
22
24
 
23
- // See: https://github.com/nodejs/node/blob/main/src/tcp_wrap.cc
24
- const { TCP, TCPConnectWrap, constants: TCPConstants } = process.binding("tcp_wrap");
25
- const { ShutdownWrap } = process.binding("stream_wrap");
26
-
27
- import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE, SOCKET } from "../../io/calls.js";
28
- import { inputStreamCreate, ioCall, outputStreamCreate, pollableCreate } from "../../io/worker-io.js";
25
+ import {
26
+ SOCKET,
27
+ SOCKET_TCP_BIND,
28
+ SOCKET_TCP_CONNECT,
29
+ SOCKET_TCP_CREATE_HANDLE,
30
+ SOCKET_TCP_CREATE_INPUT_STREAM,
31
+ SOCKET_TCP_CREATE_OUTPUT_STREAM,
32
+ SOCKET_TCP_DISPOSE,
33
+ SOCKET_TCP_GET_LOCAL_ADDRESS,
34
+ SOCKET_TCP_GET_REMOTE_ADDRESS,
35
+ SOCKET_TCP_LISTEN,
36
+ SOCKET_TCP_SET_KEEP_ALIVE,
37
+ } from "../../io/calls.js";
38
+ import {
39
+ inputStreamCreate,
40
+ ioCall,
41
+ outputStreamCreate,
42
+ pollableCreate,
43
+ } from "../../io/worker-io.js";
29
44
  import {
30
45
  deserializeIpAddress,
31
- findUnsuedLocalAddress,
46
+ findUnusedLocalAddress,
32
47
  isIPv4MappedAddress,
33
48
  isMulticastIpAddress,
34
49
  isUnicastIpAddress,
50
+ isWildcardAddress,
35
51
  serializeIpAddress,
36
52
  } from "./socket-common.js";
37
53
 
@@ -59,13 +75,12 @@ const globalBoundAddresses = new Map();
59
75
 
60
76
  // TODO: implement would-block exceptions
61
77
  // TODO: implement concurrency-conflict exceptions
62
- export class TcpSocketImpl {
63
- id = 1;
64
- /** @type {TCP.TCPConstants.SOCKET} */ #socket = null;
78
+ export class TcpSocket {
65
79
  /** @type {Network} */ network = null;
66
80
 
67
- #connections = 0;
81
+ id = 1;
68
82
 
83
+ #allowTcp = true;
69
84
  #pollId = null;
70
85
 
71
86
  // track in-progress operations
@@ -107,81 +122,34 @@ export class TcpSocketImpl {
107
122
  localPort: 0,
108
123
  remoteAddress: "",
109
124
  remotePort: 0,
125
+ localIpSocketAddress: null,
110
126
  };
111
127
 
112
- // this is set by the TcpSocket child class
113
- #tcpSocketChildClassType = null;
114
-
115
128
  /**
116
129
  * @param {IpAddressFamily} addressFamily
117
- * @param {TcpSocket} childClassType
118
130
  * @param {number} id
131
+ * @returns {void}
119
132
  */
120
- constructor(addressFamily, childClassType, id) {
121
- this.id = id;
122
-
123
- this.#socketOptions.family = addressFamily.toLocaleLowerCase();
124
- this.#tcpSocketChildClassType = childClassType;
125
-
126
- this.#socket = new TCP(TCPConstants.SOCKET | TCPConstants.SERVER);
127
- }
128
-
129
- #handleConnection(err, newClientSocket) {
130
- if (err) {
131
- assert(true, "unknown", err);
132
- }
133
-
134
- this.#connections++;
135
-
136
- this[symbolSocketState].acceptedClient = new NodeSocket({
137
- handle: newClientSocket,
133
+ static _create(addressFamily, id, allowed) {
134
+ const socket = new TcpSocket();
135
+ socket.#pollId = ioCall(SOCKET_TCP_CREATE_HANDLE);
136
+ socket.id = id;
137
+ socket.#allowTcp = allowed;
138
+ socket.#socketOptions.family = addressFamily.toLocaleLowerCase();
139
+ return socket;
140
+ }
141
+
142
+ #autoBind(network, ipFamily, { iPv4MappedAddress = false } = {}) {
143
+ const localAddress = findUnusedLocalAddress(ipFamily, {
144
+ iPv4MappedAddress,
138
145
  });
139
- this[symbolSocketState].acceptedClient.server = this.#socket;
140
- this[symbolSocketState].acceptedClient._server = this.#socket;
141
-
142
- // TODO: handle data received from the client
143
- this[symbolSocketState].acceptedClient._handle.onread = (nread, buffer) => {
144
- if (nread > 0) {
145
- const data = buffer.toString("utf8", 0, nread);
146
- console.log("accepted socket on read:", data);
147
- }
148
- };
149
- }
150
-
151
- #handleDisconnect(err) {
152
- if (err) {
153
- assert(true, "unknown", err);
154
- }
155
-
156
- this.#connections--;
157
- }
158
-
159
- #onClientConnectComplete(err) {
160
- if (err) {
161
- // TODO: figure out what theis error mean and why it is thrown
162
- assert(err === -89, "-89"); // on macos
163
-
164
- assert(err === -99, "ephemeral-ports-exhausted");
165
- assert(err === -104, "connection-reset");
166
- assert(err === -110, "timeout");
167
- assert(err === -111, "connection-refused");
168
- assert(err === -113, "remote-unreachable");
169
- assert(err === -125, "operation-cancelled");
170
-
171
- throw new Error(err);
172
- }
173
-
174
- this[symbolSocketState].connectionState = SocketConnectionState.Connected;
175
- }
176
-
177
- // TODO: is this needed?
178
- #handleAfterShutdown() {}
179
-
180
- #autoBind(network, ipFamily) {
181
- const unsusedLocalAddress = findUnsuedLocalAddress(ipFamily);
182
- this.#socketOptions.localAddress = serializeIpAddress(unsusedLocalAddress, this.#socketOptions.family);
183
- this.#socketOptions.localPort = unsusedLocalAddress.val.port;
184
- this.startBind(network, unsusedLocalAddress);
146
+ this.#socketOptions.localAddress = serializeIpAddress(
147
+ localAddress,
148
+ this.#socketOptions.family
149
+ );
150
+ this.#socketOptions.localPort = localAddress.val.port;
151
+ this.#socketOptions.localIpSocketAddress = localAddress;
152
+ this.startBind(network, localAddress);
185
153
  this.finishBind();
186
154
  }
187
155
 
@@ -192,7 +160,7 @@ export class TcpSocketImpl {
192
160
  if (localPort === 0) {
193
161
  boundAddress = this.localAddress();
194
162
  }
195
- globalBoundAddresses.set(serializeIpAddress(boundAddress, true), this.#socket);
163
+ globalBoundAddresses.set(serializeIpAddress(boundAddress, true), this.id);
196
164
  }
197
165
 
198
166
  /**
@@ -205,20 +173,30 @@ export class TcpSocketImpl {
205
173
  * @throws {invalid-state} The socket is already bound. (EINVAL)
206
174
  */
207
175
  startBind(network, localAddress) {
176
+
177
+ if (!this.allowed()) throw "access-denied";
208
178
  try {
209
- assert(this[symbolSocketState].isBound, "invalid-state", "The socket is already bound");
179
+ assert(
180
+ this[symbolSocketState].isBound,
181
+ "invalid-state",
182
+ "The socket is already bound"
183
+ );
210
184
 
211
185
  const address = serializeIpAddress(localAddress);
212
186
  const ipFamily = `ipv${isIP(address)}`;
213
187
 
214
188
  assert(
215
- this.#socketOptions.family.toLocaleLowerCase() !== ipFamily.toLocaleLowerCase(),
189
+ this.#socketOptions.family.toLocaleLowerCase() !==
190
+ ipFamily.toLocaleLowerCase(),
216
191
  "invalid-argument",
217
192
  "The `local-address` has the wrong address family"
218
193
  );
219
194
 
220
195
  assert(isUnicastIpAddress(localAddress) === false, "invalid-argument");
221
- assert(isIPv4MappedAddress(localAddress) && this.ipv6Only(), "invalid-argument");
196
+ assert(
197
+ isIPv4MappedAddress(localAddress) && this.ipv6Only(),
198
+ "invalid-argument"
199
+ );
222
200
 
223
201
  const { port } = localAddress.val;
224
202
  this.#socketOptions.localIpSocketAddress = localAddress;
@@ -245,20 +223,25 @@ export class TcpSocketImpl {
245
223
  try {
246
224
  assert(this[symbolOperations].bind === 0, "not-in-progress");
247
225
 
248
- const { localAddress, localIpSocketAddress, localPort, family } = this.#socketOptions;
226
+ const { localAddress, localIpSocketAddress, localPort, family } =
227
+ this.#socketOptions;
249
228
  assert(isIP(localAddress) === 0, "address-not-bindable");
250
- assert(globalBoundAddresses.has(serializeIpAddress(localIpSocketAddress, true)), "address-in-use");
251
-
252
- let err = null;
253
- let bind = "bind"; // ipv4
254
- if (family.toLocaleLowerCase() === "ipv6") {
255
- bind = "bind6";
256
- }
229
+ assert(
230
+ globalBoundAddresses.has(
231
+ serializeIpAddress(localIpSocketAddress, true)
232
+ ),
233
+ "address-in-use"
234
+ );
257
235
 
258
- err = this.#socket[bind](localAddress, localPort);
236
+ const err = ioCall(SOCKET_TCP_BIND, this.id, {
237
+ localAddress,
238
+ localPort,
239
+ family,
240
+ // Note: don't call getter method here, it will throw because of the assertion
241
+ isIpV6Only: this[symbolSocketState].ipv6Only,
242
+ });
259
243
 
260
244
  if (err) {
261
- this.#socket.close();
262
245
  assert(err === -22, "address-in-use");
263
246
  assert(err === -49, "address-not-bindable");
264
247
  assert(err === -99, "address-not-bindable"); // EADDRNOTAVAIL
@@ -291,27 +274,55 @@ export class TcpSocketImpl {
291
274
  * @throws {invalid-state} The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
292
275
  */
293
276
  startConnect(network, remoteAddress) {
277
+
278
+ if (!this.allowed()) throw "access-denied";
294
279
  const host = serializeIpAddress(remoteAddress);
295
280
  const ipFamily = `ipv${isIP(host)}`;
296
281
  try {
297
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
298
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Connecting, "invalid-state");
299
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Listening, "invalid-state");
282
+ assert(
283
+ this[symbolSocketState].connectionState ===
284
+ SocketConnectionState.Connected,
285
+ "invalid-state"
286
+ );
287
+ assert(
288
+ this[symbolSocketState].connectionState ===
289
+ SocketConnectionState.Connecting,
290
+ "invalid-state"
291
+ );
292
+ assert(
293
+ this[symbolSocketState].connectionState ===
294
+ SocketConnectionState.Listening,
295
+ "invalid-state"
296
+ );
300
297
 
301
- assert(host === "0.0.0.0" || host === "0:0:0:0:0:0:0:0", "invalid-argument");
302
- assert(this.#socketOptions.family.toLocaleLowerCase() !== ipFamily.toLocaleLowerCase(), "invalid-argument");
298
+ assert(
299
+ isWildcardAddress(remoteAddress),
300
+ "invalid-argument",
301
+ "The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`)"
302
+ );
303
+ assert(
304
+ this.#socketOptions.family.toLocaleLowerCase() !==
305
+ ipFamily.toLocaleLowerCase(),
306
+ "invalid-argument"
307
+ );
303
308
  assert(isUnicastIpAddress(remoteAddress) === false, "invalid-argument");
304
309
  assert(isMulticastIpAddress(remoteAddress), "invalid-argument");
305
- assert(isIPv4MappedAddress(remoteAddress) && this.ipv6Only(), "invalid-argument");
310
+ const iPv4MappedAddress = isIPv4MappedAddress(remoteAddress);
311
+ assert(iPv4MappedAddress && this.ipv6Only(), "invalid-argument");
306
312
  assert(remoteAddress.val.port === 0, "invalid-argument");
307
313
 
308
314
  if (this[symbolSocketState].isBound === false) {
309
- this.#autoBind(network, ipFamily);
315
+ this.#autoBind(network, ipFamily, {
316
+ iPv4MappedAddress,
317
+ });
310
318
  }
311
319
 
312
320
  assert(network !== this.network, "invalid-argument");
313
321
  assert(ipFamily.toLocaleLowerCase() === "ipv0", "invalid-argument");
314
- assert(remoteAddress.val.port === 0 && platform() === "win32", "invalid-argument");
322
+ assert(
323
+ remoteAddress.val.port === 0 && platform() === "win32",
324
+ "invalid-argument"
325
+ );
315
326
  } catch (err) {
316
327
  this[symbolSocketState].lastErrorState = err;
317
328
  throw err;
@@ -347,43 +358,46 @@ export class TcpSocketImpl {
347
358
 
348
359
  this[symbolSocketState].lastErrorState = null;
349
360
 
350
- const { localAddress, localPort, remoteAddress, remotePort, family } = this.#socketOptions;
351
- const connectReq = new TCPConnectWrap();
361
+ const { localAddress, localPort, remoteAddress, remotePort, family } =
362
+ this.#socketOptions;
352
363
 
353
- let err = null;
354
- let connect = "connect"; // ipv4
355
- if (family.toLocaleLowerCase() === "ipv6") {
356
- connect = "connect6";
357
- }
364
+ this[symbolSocketState].connectionState = SocketConnectionState.Connecting;
358
365
 
359
- err = this.#socket[connect](connectReq, remoteAddress, remotePort);
366
+ const err = ioCall(SOCKET_TCP_CONNECT, this.id, {
367
+ remoteAddress,
368
+ remotePort,
369
+ localAddress,
370
+ localPort,
371
+ family,
372
+ });
360
373
 
361
374
  if (err) {
362
- console.error(`[tcp] connect error on socket: ${err}`);
363
- this[symbolSocketState].connectionState = SocketConnectionState.Error;
364
- }
375
+ // The remote address has changed.
376
+ // TODO: what error should be thrown for EREMCHG?
377
+ assert(err === -89, "unknown"); // on macos
365
378
 
366
- connectReq.oncomplete = this.#onClientConnectComplete.bind(this);
367
- connectReq.address = remoteAddress;
368
- connectReq.port = remotePort;
369
- connectReq.localAddress = localAddress;
370
- connectReq.localPort = localPort;
379
+ // The calling host cannot reach the specified destination.
380
+ // TODO: what error should be thrown for EADDRNOTAVAIL?
381
+ assert(err === -49, "unknown"); // on macos
371
382
 
372
- this.#socket.onread = (_buffer) => {
373
- // TODO: handle data received from the server
374
- };
375
-
376
- this.#socket.readStart();
377
-
378
- const inputStream = inputStreamCreate(SOCKET, ioCall(INPUT_STREAM_CREATE | SOCKET, null, {}));
379
- const outputStream = outputStreamCreate(SOCKET, ioCall(OUTPUT_STREAM_CREATE | SOCKET, null, {}));
383
+ assert(err === -99, "ephemeral-ports-exhausted");
384
+ assert(err === -101, "remote-unreachable"); // wsl ubuntu
385
+ assert(err === -104, "connection-reset");
386
+ assert(err === -110, "timeout");
387
+ assert(err === -111, "connection-refused");
388
+ assert(err === -113, "remote-unreachable");
389
+ assert(err === -125, "operation-cancelled");
390
+ this[symbolSocketState].connectionState = SocketConnectionState.Error;
391
+ throw new Error(err);
392
+ }
380
393
 
381
- this[symbolOperations].connect--;
382
- this[symbolSocketState].connectionState = SocketConnectionState.Connecting;
394
+ const inputStreamId = ioCall(SOCKET_TCP_CREATE_INPUT_STREAM);
395
+ const outputStreamId = ioCall(SOCKET_TCP_CREATE_OUTPUT_STREAM);
396
+ const inputStream = inputStreamCreate(SOCKET, inputStreamId);
397
+ const outputStream = outputStreamCreate(SOCKET, outputStreamId);
383
398
 
384
- // TODO: this is a temporary workaround, move this to the connection callback
385
- // when the connection is actually established
386
399
  this[symbolSocketState].connectionState = SocketConnectionState.Connected;
400
+ this[symbolOperations].connect--;
387
401
 
388
402
  return [inputStream, outputStream];
389
403
  }
@@ -395,11 +409,21 @@ export class TcpSocketImpl {
395
409
  * @throws {invalid-state} The socket is already in the Listener state.
396
410
  */
397
411
  startListen() {
412
+
413
+ if (!this.allowed()) throw "access-denied";
398
414
  try {
399
415
  assert(this[symbolSocketState].lastErrorState !== null, "invalid-state");
400
416
  assert(this[symbolSocketState].isBound === false, "invalid-state");
401
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
402
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Listening, "invalid-state");
417
+ assert(
418
+ this[symbolSocketState].connectionState ===
419
+ SocketConnectionState.Connected,
420
+ "invalid-state"
421
+ );
422
+ assert(
423
+ this[symbolSocketState].connectionState ===
424
+ SocketConnectionState.Listening,
425
+ "invalid-state"
426
+ );
403
427
  } catch (err) {
404
428
  this[symbolSocketState].lastErrorState = err;
405
429
  throw err;
@@ -425,13 +449,11 @@ export class TcpSocketImpl {
425
449
 
426
450
  this[symbolSocketState].lastErrorState = null;
427
451
 
428
- const err = this.#socket.listen(this[symbolSocketState].backlogSize);
452
+ const err = ioCall(SOCKET_TCP_LISTEN, this.id, {
453
+ backlogSize: this[symbolSocketState].backlogSize,
454
+ });
429
455
  if (err) {
430
- console.error(`[tcp] listen error on socket: ${err}`);
431
- this.#socket.close();
432
-
433
- // TODO: handle errors
434
- throw new Error(err);
456
+ assert(true, "unknown", err);
435
457
  }
436
458
 
437
459
  this[symbolSocketState].connectionState = SocketConnectionState.Listening;
@@ -446,10 +468,15 @@ export class TcpSocketImpl {
446
468
  * @throws {new-socket-limit} The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
447
469
  */
448
470
  accept() {
471
+ if (!this.allowed()) throw "access-denied";
449
472
  this[symbolOperations].accept++;
450
473
 
451
474
  try {
452
- assert(this[symbolSocketState].connectionState !== SocketConnectionState.Listening, "invalid-state");
475
+ assert(
476
+ this[symbolSocketState].connectionState !==
477
+ SocketConnectionState.Listening,
478
+ "invalid-state"
479
+ );
453
480
  } catch (err) {
454
481
  this[symbolSocketState].lastErrorState = err;
455
482
  throw err;
@@ -460,16 +487,27 @@ export class TcpSocketImpl {
460
487
  if (this[symbolSocketState].isBound === false) {
461
488
  this.#autoBind(this.network, this.addressFamily());
462
489
  }
463
- const inputStream = inputStreamCreate(SOCKET, ioCall(INPUT_STREAM_CREATE | SOCKET, null, {}));
464
- const outputStream = outputStreamCreate(SOCKET, ioCall(OUTPUT_STREAM_CREATE | SOCKET, null, {}));
465
490
 
466
- // Because we have to return a valid TcpSocket resrouce type,
467
- // we need to instantiate the correct child class
468
- // TODO: figure out a more elegant way to do this
469
- const socket = new this.#tcpSocketChildClassType(this.addressFamily());
491
+ const inputStreamId = ioCall(SOCKET_TCP_CREATE_INPUT_STREAM);
492
+ const outputStreamId = ioCall(SOCKET_TCP_CREATE_OUTPUT_STREAM);
493
+ const inputStream = inputStreamCreate(SOCKET, inputStreamId);
494
+ const outputStream = outputStreamCreate(SOCKET, outputStreamId);
495
+
496
+ const socket = tcpSocketImplCreate(
497
+ this.addressFamily(),
498
+ this.id + 1,
499
+ this.allowed()
500
+ );
501
+ this.#cloneSocketState(socket);
470
502
 
471
- // The returned socket is bound and in the Connection state.
472
- // The following properties are inherited from the listener socket:
503
+ this[symbolOperations].accept--;
504
+
505
+ return [socket, inputStream, outputStream];
506
+ }
507
+
508
+ #cloneSocketState(socket) {
509
+ // Copy the socket state:
510
+ // The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
473
511
  // - `address-family`
474
512
  // - `ipv6-only`
475
513
  // - `keep-alive-enabled`
@@ -479,19 +517,34 @@ export class TcpSocketImpl {
479
517
  // - `hop-limit`
480
518
  // - `receive-buffer-size`
481
519
  // - `send-buffer-size`
482
- //
483
- socket[symbolSocketState].ipv6Only = this[symbolSocketState].ipv6Only;
484
- socket[symbolSocketState].keepAlive = this[symbolSocketState].keepAlive;
485
- socket[symbolSocketState].keepAliveIdleTime = this[symbolSocketState].keepAliveIdleTime;
486
- socket[symbolSocketState].keepAliveInterval = this[symbolSocketState].keepAliveInterval;
487
- socket[symbolSocketState].keepAliveCount = this[symbolSocketState].keepAliveCount;
488
- socket[symbolSocketState].hopLimit = this[symbolSocketState].hopLimit;
489
- socket[symbolSocketState].receiveBufferSize = this[symbolSocketState].receiveBufferSize;
490
- socket[symbolSocketState].sendBufferSize = this[symbolSocketState].sendBufferSize;
491
-
492
- this[symbolOperations].accept--;
493
520
 
494
- return [socket, inputStream, outputStream];
521
+
522
+ // Note: don't call getter/setters methods here, they will throw
523
+ socket.#socketOptions.family = this.#socketOptions.family;
524
+
525
+ // Note: don't call getter/setters methods here, they will throw
526
+ const {
527
+ ipv6Only,
528
+ keepAlive,
529
+ keepAliveIdleTime,
530
+ keepAliveInterval,
531
+ keepAliveCount,
532
+ hopLimit,
533
+ receiveBufferSize,
534
+ sendBufferSize,
535
+ } = this[symbolSocketState];
536
+
537
+ socket[symbolSocketState] = {
538
+ ...socket[symbolSocketState],
539
+ ipv6Only,
540
+ keepAlive,
541
+ keepAliveIdleTime,
542
+ keepAliveInterval,
543
+ keepAliveCount,
544
+ hopLimit,
545
+ receiveBufferSize,
546
+ sendBufferSize,
547
+ };
495
548
  }
496
549
 
497
550
  /**
@@ -501,10 +554,10 @@ export class TcpSocketImpl {
501
554
  localAddress() {
502
555
  assert(this[symbolSocketState].isBound === false, "invalid-state");
503
556
 
504
- const out = {};
505
- this.#socket.getsockname(out);
506
-
507
- const { address, port, family } = out;
557
+ const { address, port, family } = ioCall(
558
+ SOCKET_TCP_GET_LOCAL_ADDRESS,
559
+ this.id
560
+ );
508
561
  this.#socketOptions.localAddress = address;
509
562
  this.#socketOptions.localPort = port;
510
563
  this.#socketOptions.family = family.toLocaleLowerCase();
@@ -523,12 +576,17 @@ export class TcpSocketImpl {
523
576
  * @throws {invalid-state} The socket is not connected to a remote address. (ENOTCONN)
524
577
  */
525
578
  remoteAddress() {
526
- assert(this[symbolSocketState].connectionState !== SocketConnectionState.Connected, "invalid-state");
527
579
 
528
- const out = {};
529
- this.#socket.getpeername(out);
580
+ assert(
581
+ this[symbolSocketState].connectionState !==
582
+ SocketConnectionState.Connected,
583
+ "invalid-state"
584
+ );
530
585
 
531
- const { address, port, family } = out;
586
+ const { address, port, family } = ioCall(
587
+ SOCKET_TCP_GET_REMOTE_ADDRESS,
588
+ this.id
589
+ );
532
590
  this.#socketOptions.remoteAddress = address;
533
591
  this.#socketOptions.remotePort = port;
534
592
  this.#socketOptions.family = family.toLocaleLowerCase();
@@ -543,7 +601,10 @@ export class TcpSocketImpl {
543
601
  }
544
602
 
545
603
  isListening() {
546
- return this[symbolSocketState].connectionState === SocketConnectionState.Listening;
604
+ return (
605
+ this[symbolSocketState].connectionState ===
606
+ SocketConnectionState.Listening
607
+ );
547
608
  }
548
609
 
549
610
  /**
@@ -558,7 +619,11 @@ export class TcpSocketImpl {
558
619
  * @throws {not-supported} (get/set) `this` socket is an IPv4 socket.
559
620
  */
560
621
  ipv6Only() {
561
- assert(this.#socketOptions.family.toLocaleLowerCase() === "ipv4", "not-supported");
622
+
623
+ assert(
624
+ this.#socketOptions.family.toLocaleLowerCase() === "ipv4",
625
+ "not-supported"
626
+ );
562
627
 
563
628
  return this[symbolSocketState].ipv6Only;
564
629
  }
@@ -571,7 +636,10 @@ export class TcpSocketImpl {
571
636
  * @throws {not-supported} (set) Host does not support dual-stack sockets. (Implementations are not required to.)
572
637
  */
573
638
  setIpv6Only(value) {
574
- assert(this.#socketOptions.family.toLocaleLowerCase() === "ipv4", "not-supported");
639
+ assert(
640
+ this.#socketOptions.family.toLocaleLowerCase() === "ipv4",
641
+ "not-supported"
642
+ );
575
643
  assert(this[symbolSocketState].isBound, "invalid-state");
576
644
 
577
645
  this[symbolSocketState].ipv6Only = value;
@@ -585,8 +653,13 @@ export class TcpSocketImpl {
585
653
  * @throws {invalid-state} (set) The socket is already in the Connection state.
586
654
  */
587
655
  setListenBacklogSize(value) {
656
+
588
657
  assert(value === 0n, "invalid-argument", "The provided value was 0.");
589
- assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
658
+ assert(
659
+ this[symbolSocketState].connectionState ===
660
+ SocketConnectionState.Connected,
661
+ "invalid-state"
662
+ );
590
663
 
591
664
  this[symbolSocketState].backlogSize = Number(value);
592
665
  }
@@ -603,10 +676,13 @@ export class TcpSocketImpl {
603
676
  * @returns {void}
604
677
  */
605
678
  setKeepAliveEnabled(value) {
606
- this.#socket.setKeepAlive(value);
679
+ ioCall(SOCKET_TCP_SET_KEEP_ALIVE, this.id, {
680
+ keepAlive: value,
681
+ });
682
+
607
683
  this[symbolSocketState].keepAlive = value;
608
684
 
609
- if (value) {
685
+ if (value === true) {
610
686
  this.setKeepAliveIdleTime(this.keepAliveIdleTime());
611
687
  this.setKeepAliveInterval(this.keepAliveInterval());
612
688
  this.setKeepAliveCount(this.keepAliveCount());
@@ -628,6 +704,7 @@ export class TcpSocketImpl {
628
704
  * @throws {invalid-argument} (set) The idle time must be 1 or higher.
629
705
  */
630
706
  setKeepAliveIdleTime(value) {
707
+ value = Number(value);
631
708
  assert(value < 1, "invalid-argument", "The idle time must be 1 or higher.");
632
709
 
633
710
  this[symbolSocketState].keepAliveIdleTime = value;
@@ -648,6 +725,7 @@ export class TcpSocketImpl {
648
725
  * @throws {invalid-argument} (set) The interval must be 1 or higher.
649
726
  */
650
727
  setKeepAliveInterval(value) {
728
+ value = Number(value);
651
729
  assert(value < 1, "invalid-argument", "The interval must be 1 or higher.");
652
730
 
653
731
  this[symbolSocketState].keepAliveInterval = value;
@@ -668,6 +746,7 @@ export class TcpSocketImpl {
668
746
  * @throws {invalid-argument} (set) The count must be 1 or higher.
669
747
  */
670
748
  setKeepAliveCount(value) {
749
+ value = Number(value);
671
750
  assert(value < 1, "invalid-argument", "The count must be 1 or higher.");
672
751
 
673
752
  // TODO: set this on the client socket as well
@@ -691,6 +770,7 @@ export class TcpSocketImpl {
691
770
  * @description Not available on Node.js (see https://github.com/WebAssembly/wasi-sockets/blob/main/Posix-compatibility.md#socket-options)
692
771
  */
693
772
  setHopLimit(value) {
773
+ value = Number(value);
694
774
  assert(value < 1, "invalid-argument", "The TTL value must be 1 or higher.");
695
775
 
696
776
  this[symbolSocketState].hopLimit = value;
@@ -700,7 +780,7 @@ export class TcpSocketImpl {
700
780
  * @returns {bigint}
701
781
  */
702
782
  receiveBufferSize() {
703
- return this[symbolSocketState].receiveBufferSize;
783
+ return BigInt(this[symbolSocketState].receiveBufferSize);
704
784
  }
705
785
 
706
786
  /**
@@ -711,9 +791,11 @@ export class TcpSocketImpl {
711
791
  * @throws {invalid-state} (set) The socket is already in the Connection state.
712
792
  */
713
793
  setReceiveBufferSize(value) {
794
+ value = Number(value);
795
+
714
796
  // TODO: review these assertions based on WIT specs
715
797
  // assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
716
- assert(value === 0n, "invalid-argument", "The provided value was 0.");
798
+ assert(value === 0, "invalid-argument", "The provided value was 0.");
717
799
 
718
800
  // TODO: set this on the client socket as well
719
801
  this[symbolSocketState].receiveBufferSize = value;
@@ -723,7 +805,7 @@ export class TcpSocketImpl {
723
805
  * @returns {bigint}
724
806
  */
725
807
  sendBufferSize() {
726
- return this[symbolSocketState].sendBufferSize;
808
+ return BigInt(this[symbolSocketState].sendBufferSize);
727
809
  }
728
810
 
729
811
  /**
@@ -734,9 +816,11 @@ export class TcpSocketImpl {
734
816
  * @throws {invalid-state} (set) The socket is already in the Listener state.
735
817
  */
736
818
  setSendBufferSize(value) {
819
+ value = Number(value);
820
+
737
821
  // TODO: review these assertions based on WIT specs
738
822
  // assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
739
- assert(value === 0n, "invalid-argument", "The provided value was 0.");
823
+ assert(value === 0, "invalid-argument", "The provided value was 0.");
740
824
 
741
825
  // TODO: set this on the client socket as well
742
826
  this[symbolSocketState].sendBufferSize = value;
@@ -757,7 +841,11 @@ export class TcpSocketImpl {
757
841
  * @throws {invalid-state} The socket is not in the Connection state. (ENOTCONN)
758
842
  */
759
843
  shutdown(shutdownType) {
760
- assert(this[symbolSocketState].connectionState !== SocketConnectionState.Connected, "invalid-state");
844
+ assert(
845
+ this[symbolSocketState].connectionState !==
846
+ SocketConnectionState.Connected,
847
+ "invalid-state"
848
+ );
761
849
 
762
850
  // TODO: figure out how to handle shutdownTypes
763
851
  if (shutdownType === ShutdownType.Receive) {
@@ -769,26 +857,29 @@ export class TcpSocketImpl {
769
857
  this[symbolSocketState].canSend = false;
770
858
  }
771
859
 
772
- const req = new ShutdownWrap();
773
- req.oncomplete = this.#handleAfterShutdown.bind(this);
774
- req.handle = this._handle;
775
- req.callback = () => {};
776
- const err = this._handle.shutdown(req);
860
+ const err = ioCall(SOCKET_TCP_SHUTDOWN, this.id, {
861
+ shutdownType,
862
+ });
777
863
 
778
864
  assert(err === 1, "invalid-state");
779
865
  }
780
866
 
781
867
  [symbolDispose]() {
782
- this.#socket.close();
868
+ ioCall(SOCKET_TCP_DISPOSE, this.id);
783
869
 
784
870
  // we only need to remove the bound address from the global map
785
871
  // if the socket was already bound
786
872
  if (this[symbolSocketState].isBound) {
787
- globalBoundAddresses.delete(serializeIpAddress(this.#socketOptions.localIpSocketAddress, true));
873
+ globalBoundAddresses.delete(
874
+ serializeIpAddress(this.#socketOptions.localIpSocketAddress, true)
875
+ );
788
876
  }
789
877
  }
790
878
 
791
879
  handle() {
792
- return this.#socket;
880
+ // return this.#socket;
793
881
  }
794
882
  }
883
+
884
+ export const tcpSocketImplCreate = TcpSocket._create;
885
+ delete TcpSocket._create;