@bytecodealliance/preview2-shim 0.14.1 → 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.
- package/README.md +2 -2
- package/lib/browser/filesystem.js +6 -5
- package/lib/browser/random.js +1 -1
- package/lib/io/calls.js +128 -1
- package/lib/io/worker-http.js +159 -65
- package/lib/io/worker-io.js +40 -43
- package/lib/io/worker-socket-tcp.js +131 -0
- package/lib/io/worker-socket-udp.js +219 -0
- package/lib/io/worker-thread.js +288 -82
- package/lib/nodejs/cli.js +27 -11
- package/lib/nodejs/filesystem.js +89 -38
- package/lib/nodejs/http.js +643 -522
- package/lib/nodejs/index.js +0 -1
- package/lib/nodejs/sockets/socket-common.js +15 -2
- package/lib/nodejs/sockets/tcp-socket-impl.js +279 -188
- package/lib/nodejs/sockets/udp-socket-impl.js +305 -165
- package/lib/nodejs/sockets/wasi-sockets.js +54 -33
- package/lib/nodejs/sockets.js +25 -11
- package/lib/synckit/index.js +22 -39
- package/package.json +1 -1
- package/types/interfaces/wasi-http-types.d.ts +53 -41
- package/types/interfaces/wasi-sockets-tcp.d.ts +5 -0
|
@@ -10,28 +10,44 @@
|
|
|
10
10
|
* @typedef {import("../../../types/interfaces/wasi-clocks-monotonic-clock.js").Duration} Duration
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { isIP
|
|
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 =
|
|
21
|
-
|
|
20
|
+
const symbolSocketState =
|
|
21
|
+
Symbol.SocketInternalState || Symbol.for("SocketInternalState");
|
|
22
|
+
const symbolOperations =
|
|
23
|
+
Symbol.SocketOperationsState || Symbol.for("SocketOperationsState");
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
63
|
-
id = 1;
|
|
64
|
-
/** @type {TCP.TCPConstants.SOCKET} */ #socket = null;
|
|
78
|
+
export class TcpSocket {
|
|
65
79
|
/** @type {Network} */ network = null;
|
|
66
80
|
|
|
67
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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(
|
|
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() !==
|
|
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(
|
|
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 } =
|
|
226
|
+
const { localAddress, localIpSocketAddress, localPort, family } =
|
|
227
|
+
this.#socketOptions;
|
|
249
228
|
assert(isIP(localAddress) === 0, "address-not-bindable");
|
|
250
|
-
assert(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
229
|
+
assert(
|
|
230
|
+
globalBoundAddresses.has(
|
|
231
|
+
serializeIpAddress(localIpSocketAddress, true)
|
|
232
|
+
),
|
|
233
|
+
"address-in-use"
|
|
234
|
+
);
|
|
257
235
|
|
|
258
|
-
err =
|
|
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(
|
|
298
|
-
|
|
299
|
-
|
|
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(
|
|
302
|
-
|
|
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
|
-
|
|
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(
|
|
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 } =
|
|
351
|
-
|
|
361
|
+
const { localAddress, localPort, remoteAddress, remotePort, family } =
|
|
362
|
+
this.#socketOptions;
|
|
352
363
|
|
|
353
|
-
|
|
354
|
-
let connect = "connect"; // ipv4
|
|
355
|
-
if (family.toLocaleLowerCase() === "ipv6") {
|
|
356
|
-
connect = "connect6";
|
|
357
|
-
}
|
|
364
|
+
this[symbolSocketState].connectionState = SocketConnectionState.Connecting;
|
|
358
365
|
|
|
359
|
-
err =
|
|
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
|
-
|
|
363
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
382
|
-
|
|
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(
|
|
402
|
-
|
|
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 =
|
|
452
|
+
const err = ioCall(SOCKET_TCP_LISTEN, this.id, {
|
|
453
|
+
backlogSize: this[symbolSocketState].backlogSize,
|
|
454
|
+
});
|
|
429
455
|
if (err) {
|
|
430
|
-
|
|
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(
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
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
|
-
|
|
472
|
-
|
|
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
|
-
|
|
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
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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
|
-
|
|
529
|
-
|
|
580
|
+
assert(
|
|
581
|
+
this[symbolSocketState].connectionState !==
|
|
582
|
+
SocketConnectionState.Connected,
|
|
583
|
+
"invalid-state"
|
|
584
|
+
);
|
|
530
585
|
|
|
531
|
-
const { address, port, family } =
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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 ===
|
|
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 ===
|
|
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(
|
|
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
|
|
773
|
-
|
|
774
|
-
|
|
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
|
|
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(
|
|
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;
|