@bytecodealliance/preview2-shim 0.14.2 → 0.15.0
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/lib/browser/sockets.js +0 -14
- package/lib/io/calls.js +72 -135
- package/lib/io/worker-http.js +21 -18
- package/lib/io/worker-io.js +186 -44
- package/lib/io/worker-socket-tcp.js +250 -96
- package/lib/io/worker-socket-udp.js +524 -167
- package/lib/io/worker-sockets.js +371 -0
- package/lib/io/worker-thread.js +677 -489
- package/lib/nodejs/cli.js +3 -3
- package/lib/nodejs/clocks.js +9 -6
- package/lib/nodejs/filesystem.js +87 -25
- package/lib/nodejs/http.js +106 -96
- package/lib/nodejs/index.js +0 -2
- package/lib/nodejs/sockets.js +563 -17
- package/lib/synckit/index.js +4 -3
- package/package.json +2 -2
- package/lib/common/assert.js +0 -7
- package/lib/nodejs/sockets/socket-common.js +0 -129
- package/lib/nodejs/sockets/socketopts-bindings.js +0 -94
- package/lib/nodejs/sockets/tcp-socket-impl.js +0 -885
- package/lib/nodejs/sockets/udp-socket-impl.js +0 -768
- package/lib/nodejs/sockets/wasi-sockets.js +0 -341
- package/lib/synckit/index.d.ts +0 -71
|
@@ -1,885 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-network.js").Network} Network
|
|
3
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-network.js").IpSocketAddress} IpSocketAddress
|
|
4
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-tcp.js").TcpSocket} TcpSocket
|
|
5
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-tcp.js").InputStream} InputStream
|
|
6
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-tcp.js").OutputStream} OutputStream
|
|
7
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-tcp.js").IpAddressFamily} IpAddressFamily
|
|
8
|
-
* @typedef {import("../../../types/interfaces/wasi-io-poll-poll").Pollable} Pollable
|
|
9
|
-
* @typedef {import("../../../types/interfaces/wasi-sockets-tcp.js").ShutdownType} ShutdownType
|
|
10
|
-
* @typedef {import("../../../types/interfaces/wasi-clocks-monotonic-clock.js").Duration} Duration
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { isIP } from "node:net";
|
|
14
|
-
import { platform } from "node:os";
|
|
15
|
-
import { assert } from "../../common/assert.js";
|
|
16
|
-
// import { streams } from "../io.js";
|
|
17
|
-
// const { InputStream, OutputStream } = streams;
|
|
18
|
-
|
|
19
|
-
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
20
|
-
const symbolSocketState =
|
|
21
|
-
Symbol.SocketInternalState || Symbol.for("SocketInternalState");
|
|
22
|
-
const symbolOperations =
|
|
23
|
-
Symbol.SocketOperationsState || Symbol.for("SocketOperationsState");
|
|
24
|
-
|
|
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";
|
|
44
|
-
import {
|
|
45
|
-
deserializeIpAddress,
|
|
46
|
-
findUnusedLocalAddress,
|
|
47
|
-
isIPv4MappedAddress,
|
|
48
|
-
isMulticastIpAddress,
|
|
49
|
-
isUnicastIpAddress,
|
|
50
|
-
isWildcardAddress,
|
|
51
|
-
serializeIpAddress,
|
|
52
|
-
} from "./socket-common.js";
|
|
53
|
-
|
|
54
|
-
// TODO: move to a common
|
|
55
|
-
const ShutdownType = {
|
|
56
|
-
Receive: "receive",
|
|
57
|
-
Send: "send",
|
|
58
|
-
Both: "both",
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// TODO: move to a common
|
|
62
|
-
const SocketConnectionState = {
|
|
63
|
-
Error: "Error",
|
|
64
|
-
Closed: "Closed",
|
|
65
|
-
Connecting: "Connecting",
|
|
66
|
-
Connected: "Connected",
|
|
67
|
-
Listening: "Listening",
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// As a workaround, we store the bound address in a global map
|
|
71
|
-
// this is needed because 'address-in-use' is not always thrown when binding
|
|
72
|
-
// more than one socket to the same address
|
|
73
|
-
// TODO: remove this workaround when we figure out why!
|
|
74
|
-
const globalBoundAddresses = new Map();
|
|
75
|
-
|
|
76
|
-
// TODO: implement would-block exceptions
|
|
77
|
-
// TODO: implement concurrency-conflict exceptions
|
|
78
|
-
export class TcpSocket {
|
|
79
|
-
/** @type {Network} */ network = null;
|
|
80
|
-
|
|
81
|
-
id = 1;
|
|
82
|
-
|
|
83
|
-
#allowTcp = true;
|
|
84
|
-
#pollId = null;
|
|
85
|
-
|
|
86
|
-
// track in-progress operations
|
|
87
|
-
// counter must be 0 for the operation to be considered complete
|
|
88
|
-
// we increment the counter when the operation starts
|
|
89
|
-
// and decrement it when the operation finishes
|
|
90
|
-
[symbolOperations] = {
|
|
91
|
-
bind: 0,
|
|
92
|
-
connect: 0,
|
|
93
|
-
listen: 0,
|
|
94
|
-
accept: 0,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
[symbolSocketState] = {
|
|
98
|
-
lastErrorState: null,
|
|
99
|
-
isBound: false,
|
|
100
|
-
ipv6Only: false,
|
|
101
|
-
connectionState: SocketConnectionState.Closed,
|
|
102
|
-
acceptedClient: null,
|
|
103
|
-
canReceive: true,
|
|
104
|
-
canSend: true,
|
|
105
|
-
|
|
106
|
-
// See: https://github.com/torvalds/linux/blob/fe3cfe869d5e0453754cf2b4c75110276b5e8527/net/core/request_sock.c#L19-L31
|
|
107
|
-
backlogSize: 128,
|
|
108
|
-
|
|
109
|
-
// TODO: what these default values should be?
|
|
110
|
-
keepAlive: false,
|
|
111
|
-
keepAliveCount: 1,
|
|
112
|
-
keepAliveIdleTime: 1,
|
|
113
|
-
keepAliveInterval: 1,
|
|
114
|
-
hopLimit: 1,
|
|
115
|
-
receiveBufferSize: 1,
|
|
116
|
-
sendBufferSize: 1,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
#socketOptions = {
|
|
120
|
-
family: "ipv4",
|
|
121
|
-
localAddress: "",
|
|
122
|
-
localPort: 0,
|
|
123
|
-
remoteAddress: "",
|
|
124
|
-
remotePort: 0,
|
|
125
|
-
localIpSocketAddress: null,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {IpAddressFamily} addressFamily
|
|
130
|
-
* @param {number} id
|
|
131
|
-
* @returns {void}
|
|
132
|
-
*/
|
|
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,
|
|
145
|
-
});
|
|
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);
|
|
153
|
-
this.finishBind();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
#cacheBoundAddress() {
|
|
157
|
-
let { localIpSocketAddress: boundAddress, localPort } = this.#socketOptions;
|
|
158
|
-
// when port is 0, the OS will assign an ephemeral port
|
|
159
|
-
// we need to get the actual port assigned by the OS
|
|
160
|
-
if (localPort === 0) {
|
|
161
|
-
boundAddress = this.localAddress();
|
|
162
|
-
}
|
|
163
|
-
globalBoundAddresses.set(serializeIpAddress(boundAddress, true), this.id);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @param {Network} network
|
|
168
|
-
* @param {IpSocketAddress} localAddress
|
|
169
|
-
* @returns {void}
|
|
170
|
-
* @throws {invalid-argument} The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
|
|
171
|
-
* @throws {invalid-argument} `local-address` is not a unicast address. (EINVAL)
|
|
172
|
-
* @throws {invalid-argument} `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL)
|
|
173
|
-
* @throws {invalid-state} The socket is already bound. (EINVAL)
|
|
174
|
-
*/
|
|
175
|
-
startBind(network, localAddress) {
|
|
176
|
-
|
|
177
|
-
if (!this.allowed()) throw "access-denied";
|
|
178
|
-
try {
|
|
179
|
-
assert(
|
|
180
|
-
this[symbolSocketState].isBound,
|
|
181
|
-
"invalid-state",
|
|
182
|
-
"The socket is already bound"
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const address = serializeIpAddress(localAddress);
|
|
186
|
-
const ipFamily = `ipv${isIP(address)}`;
|
|
187
|
-
|
|
188
|
-
assert(
|
|
189
|
-
this.#socketOptions.family.toLocaleLowerCase() !==
|
|
190
|
-
ipFamily.toLocaleLowerCase(),
|
|
191
|
-
"invalid-argument",
|
|
192
|
-
"The `local-address` has the wrong address family"
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
assert(isUnicastIpAddress(localAddress) === false, "invalid-argument");
|
|
196
|
-
assert(
|
|
197
|
-
isIPv4MappedAddress(localAddress) && this.ipv6Only(),
|
|
198
|
-
"invalid-argument"
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const { port } = localAddress.val;
|
|
202
|
-
this.#socketOptions.localIpSocketAddress = localAddress;
|
|
203
|
-
this.#socketOptions.localAddress = address;
|
|
204
|
-
this.#socketOptions.localPort = port;
|
|
205
|
-
this.network = network;
|
|
206
|
-
this[symbolOperations].bind++;
|
|
207
|
-
this[symbolSocketState].lastErrorState = null;
|
|
208
|
-
} catch (err) {
|
|
209
|
-
this[symbolSocketState].lastErrorState = err;
|
|
210
|
-
throw err;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* @returns {void}
|
|
216
|
-
* @throws {address-in-use} No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
|
|
217
|
-
* @throws {address-in-use} Address is already in use. (EADDRINUSE)
|
|
218
|
-
* @throws {address-not-bindable} `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
|
|
219
|
-
* @throws {not-in-progress} A `bind` operation is not in progress.
|
|
220
|
-
* @throws {would-block} Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
|
|
221
|
-
**/
|
|
222
|
-
finishBind() {
|
|
223
|
-
try {
|
|
224
|
-
assert(this[symbolOperations].bind === 0, "not-in-progress");
|
|
225
|
-
|
|
226
|
-
const { localAddress, localIpSocketAddress, localPort, family } =
|
|
227
|
-
this.#socketOptions;
|
|
228
|
-
assert(isIP(localAddress) === 0, "address-not-bindable");
|
|
229
|
-
assert(
|
|
230
|
-
globalBoundAddresses.has(
|
|
231
|
-
serializeIpAddress(localIpSocketAddress, true)
|
|
232
|
-
),
|
|
233
|
-
"address-in-use"
|
|
234
|
-
);
|
|
235
|
-
|
|
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
|
-
});
|
|
243
|
-
|
|
244
|
-
if (err) {
|
|
245
|
-
assert(err === -22, "address-in-use");
|
|
246
|
-
assert(err === -49, "address-not-bindable");
|
|
247
|
-
assert(err === -99, "address-not-bindable"); // EADDRNOTAVAIL
|
|
248
|
-
assert(true, "unknown", err);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this[symbolSocketState].lastErrorState = null;
|
|
252
|
-
this[symbolSocketState].isBound = true;
|
|
253
|
-
this[symbolOperations].bind--;
|
|
254
|
-
|
|
255
|
-
this.#cacheBoundAddress();
|
|
256
|
-
} catch (err) {
|
|
257
|
-
this[symbolSocketState].lastErrorState = err;
|
|
258
|
-
throw err;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* @param {Network} network
|
|
264
|
-
* @param {IpSocketAddress} remoteAddress
|
|
265
|
-
* @returns {void}
|
|
266
|
-
* @throws {invalid-argument} The `remote-address` has the wrong address family. (EAFNOSUPPORT)
|
|
267
|
-
* @throws {invalid-argument} `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
|
|
268
|
-
* @throws {invalid-argument} `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
|
|
269
|
-
* @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)
|
|
270
|
-
* @throws {invalid-argument} The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
|
|
271
|
-
* @throws {invalid-argument} The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
|
|
272
|
-
* @throws {invalid-argument} The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
|
|
273
|
-
* @throws {invalid-state} The socket is already in the Connection state. (EISCONN)
|
|
274
|
-
* @throws {invalid-state} The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
|
|
275
|
-
*/
|
|
276
|
-
startConnect(network, remoteAddress) {
|
|
277
|
-
|
|
278
|
-
if (!this.allowed()) throw "access-denied";
|
|
279
|
-
const host = serializeIpAddress(remoteAddress);
|
|
280
|
-
const ipFamily = `ipv${isIP(host)}`;
|
|
281
|
-
try {
|
|
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
|
-
);
|
|
297
|
-
|
|
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
|
-
);
|
|
308
|
-
assert(isUnicastIpAddress(remoteAddress) === false, "invalid-argument");
|
|
309
|
-
assert(isMulticastIpAddress(remoteAddress), "invalid-argument");
|
|
310
|
-
const iPv4MappedAddress = isIPv4MappedAddress(remoteAddress);
|
|
311
|
-
assert(iPv4MappedAddress && this.ipv6Only(), "invalid-argument");
|
|
312
|
-
assert(remoteAddress.val.port === 0, "invalid-argument");
|
|
313
|
-
|
|
314
|
-
if (this[symbolSocketState].isBound === false) {
|
|
315
|
-
this.#autoBind(network, ipFamily, {
|
|
316
|
-
iPv4MappedAddress,
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
assert(network !== this.network, "invalid-argument");
|
|
321
|
-
assert(ipFamily.toLocaleLowerCase() === "ipv0", "invalid-argument");
|
|
322
|
-
assert(
|
|
323
|
-
remoteAddress.val.port === 0 && platform() === "win32",
|
|
324
|
-
"invalid-argument"
|
|
325
|
-
);
|
|
326
|
-
} catch (err) {
|
|
327
|
-
this[symbolSocketState].lastErrorState = err;
|
|
328
|
-
throw err;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
this[symbolSocketState].lastErrorState = null;
|
|
332
|
-
|
|
333
|
-
this.#socketOptions.remoteIpSocketAddress = remoteAddress;
|
|
334
|
-
this.#socketOptions.remoteAddress = host;
|
|
335
|
-
this.#socketOptions.remotePort = remoteAddress.val.port;
|
|
336
|
-
this.network = network;
|
|
337
|
-
this[symbolOperations].connect++;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* @returns {Array<InputStream, OutputStream>}
|
|
342
|
-
* @throws {timeout} Connection timed out. (ETIMEDOUT)
|
|
343
|
-
* @throws {connection-refused} The connection was forcefully rejected. (ECONNREFUSED)
|
|
344
|
-
* @throws {connection-reset} The connection was reset. (ECONNRESET)
|
|
345
|
-
* @throws {connection-aborted} The connection was aborted. (ECONNABORTED)
|
|
346
|
-
* @throws {remote-unreachable} The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
|
|
347
|
-
* @throws {address-in-use} Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
|
|
348
|
-
* @throws {not-in-progress} A `connect` operation is not in progress.
|
|
349
|
-
* @throws {would-block} Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
|
|
350
|
-
*/
|
|
351
|
-
finishConnect() {
|
|
352
|
-
try {
|
|
353
|
-
assert(this[symbolOperations].connect === 0, "not-in-progress");
|
|
354
|
-
} catch (err) {
|
|
355
|
-
this[symbolSocketState].lastErrorState = err;
|
|
356
|
-
throw err;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
this[symbolSocketState].lastErrorState = null;
|
|
360
|
-
|
|
361
|
-
const { localAddress, localPort, remoteAddress, remotePort, family } =
|
|
362
|
-
this.#socketOptions;
|
|
363
|
-
|
|
364
|
-
this[symbolSocketState].connectionState = SocketConnectionState.Connecting;
|
|
365
|
-
|
|
366
|
-
const err = ioCall(SOCKET_TCP_CONNECT, this.id, {
|
|
367
|
-
remoteAddress,
|
|
368
|
-
remotePort,
|
|
369
|
-
localAddress,
|
|
370
|
-
localPort,
|
|
371
|
-
family,
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
if (err) {
|
|
375
|
-
// The remote address has changed.
|
|
376
|
-
// TODO: what error should be thrown for EREMCHG?
|
|
377
|
-
assert(err === -89, "unknown"); // on macos
|
|
378
|
-
|
|
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
|
|
382
|
-
|
|
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
|
-
}
|
|
393
|
-
|
|
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);
|
|
398
|
-
|
|
399
|
-
this[symbolSocketState].connectionState = SocketConnectionState.Connected;
|
|
400
|
-
this[symbolOperations].connect--;
|
|
401
|
-
|
|
402
|
-
return [inputStream, outputStream];
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* @returns {void}
|
|
407
|
-
* @throws {invalid-state} The socket is not bound to any local address. (EDESTADDRREQ)
|
|
408
|
-
* @throws {invalid-state} The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
|
|
409
|
-
* @throws {invalid-state} The socket is already in the Listener state.
|
|
410
|
-
*/
|
|
411
|
-
startListen() {
|
|
412
|
-
|
|
413
|
-
if (!this.allowed()) throw "access-denied";
|
|
414
|
-
try {
|
|
415
|
-
assert(this[symbolSocketState].lastErrorState !== null, "invalid-state");
|
|
416
|
-
assert(this[symbolSocketState].isBound === false, "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
|
-
);
|
|
427
|
-
} catch (err) {
|
|
428
|
-
this[symbolSocketState].lastErrorState = err;
|
|
429
|
-
throw err;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
this[symbolSocketState].lastErrorState = null;
|
|
433
|
-
this[symbolOperations].listen++;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* @returns {void}
|
|
438
|
-
* @throws {address-in-use} Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
|
|
439
|
-
* @throws {not-in-progress} A `listen` operation is not in progress.
|
|
440
|
-
* @throws {would-block} Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
|
|
441
|
-
*/
|
|
442
|
-
finishListen() {
|
|
443
|
-
try {
|
|
444
|
-
assert(this[symbolOperations].listen === 0, "not-in-progress");
|
|
445
|
-
} catch (err) {
|
|
446
|
-
this[symbolSocketState].lastErrorState = err;
|
|
447
|
-
throw err;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
this[symbolSocketState].lastErrorState = null;
|
|
451
|
-
|
|
452
|
-
const err = ioCall(SOCKET_TCP_LISTEN, this.id, {
|
|
453
|
-
backlogSize: this[symbolSocketState].backlogSize,
|
|
454
|
-
});
|
|
455
|
-
if (err) {
|
|
456
|
-
assert(true, "unknown", err);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
this[symbolSocketState].connectionState = SocketConnectionState.Listening;
|
|
460
|
-
this[symbolOperations].listen--;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* @returns {Array<TcpSocket, InputStream, OutputStream>}
|
|
465
|
-
* @throws {invalid-state} Socket is not in the Listener state. (EINVAL)
|
|
466
|
-
* @throws {would-block} No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
|
|
467
|
-
* @throws {connection-aborted} An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
|
|
468
|
-
* @throws {new-socket-limit} The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
|
|
469
|
-
*/
|
|
470
|
-
accept() {
|
|
471
|
-
if (!this.allowed()) throw "access-denied";
|
|
472
|
-
this[symbolOperations].accept++;
|
|
473
|
-
|
|
474
|
-
try {
|
|
475
|
-
assert(
|
|
476
|
-
this[symbolSocketState].connectionState !==
|
|
477
|
-
SocketConnectionState.Listening,
|
|
478
|
-
"invalid-state"
|
|
479
|
-
);
|
|
480
|
-
} catch (err) {
|
|
481
|
-
this[symbolSocketState].lastErrorState = err;
|
|
482
|
-
throw err;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
this[symbolSocketState].lastErrorState = null;
|
|
486
|
-
|
|
487
|
-
if (this[symbolSocketState].isBound === false) {
|
|
488
|
-
this.#autoBind(this.network, this.addressFamily());
|
|
489
|
-
}
|
|
490
|
-
|
|
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);
|
|
502
|
-
|
|
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:
|
|
511
|
-
// - `address-family`
|
|
512
|
-
// - `ipv6-only`
|
|
513
|
-
// - `keep-alive-enabled`
|
|
514
|
-
// - `keep-alive-idle-time`
|
|
515
|
-
// - `keep-alive-interval`
|
|
516
|
-
// - `keep-alive-count`
|
|
517
|
-
// - `hop-limit`
|
|
518
|
-
// - `receive-buffer-size`
|
|
519
|
-
// - `send-buffer-size`
|
|
520
|
-
|
|
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
|
-
};
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* @returns {IpSocketAddress}
|
|
552
|
-
* @throws {invalid-state} The socket is not bound to any local address.
|
|
553
|
-
*/
|
|
554
|
-
localAddress() {
|
|
555
|
-
assert(this[symbolSocketState].isBound === false, "invalid-state");
|
|
556
|
-
|
|
557
|
-
const { address, port, family } = ioCall(
|
|
558
|
-
SOCKET_TCP_GET_LOCAL_ADDRESS,
|
|
559
|
-
this.id
|
|
560
|
-
);
|
|
561
|
-
this.#socketOptions.localAddress = address;
|
|
562
|
-
this.#socketOptions.localPort = port;
|
|
563
|
-
this.#socketOptions.family = family.toLocaleLowerCase();
|
|
564
|
-
|
|
565
|
-
return {
|
|
566
|
-
tag: family.toLocaleLowerCase(),
|
|
567
|
-
val: {
|
|
568
|
-
address: deserializeIpAddress(address, family),
|
|
569
|
-
port,
|
|
570
|
-
},
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* @returns {IpSocketAddress}
|
|
576
|
-
* @throws {invalid-state} The socket is not connected to a remote address. (ENOTCONN)
|
|
577
|
-
*/
|
|
578
|
-
remoteAddress() {
|
|
579
|
-
|
|
580
|
-
assert(
|
|
581
|
-
this[symbolSocketState].connectionState !==
|
|
582
|
-
SocketConnectionState.Connected,
|
|
583
|
-
"invalid-state"
|
|
584
|
-
);
|
|
585
|
-
|
|
586
|
-
const { address, port, family } = ioCall(
|
|
587
|
-
SOCKET_TCP_GET_REMOTE_ADDRESS,
|
|
588
|
-
this.id
|
|
589
|
-
);
|
|
590
|
-
this.#socketOptions.remoteAddress = address;
|
|
591
|
-
this.#socketOptions.remotePort = port;
|
|
592
|
-
this.#socketOptions.family = family.toLocaleLowerCase();
|
|
593
|
-
|
|
594
|
-
return {
|
|
595
|
-
tag: family.toLocaleLowerCase(),
|
|
596
|
-
val: {
|
|
597
|
-
address: deserializeIpAddress(address, family),
|
|
598
|
-
port,
|
|
599
|
-
},
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
isListening() {
|
|
604
|
-
return (
|
|
605
|
-
this[symbolSocketState].connectionState ===
|
|
606
|
-
SocketConnectionState.Listening
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* @returns {IpAddressFamily}
|
|
612
|
-
*/
|
|
613
|
-
addressFamily() {
|
|
614
|
-
return this.#socketOptions.family;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* @returns {boolean}
|
|
619
|
-
* @throws {not-supported} (get/set) `this` socket is an IPv4 socket.
|
|
620
|
-
*/
|
|
621
|
-
ipv6Only() {
|
|
622
|
-
|
|
623
|
-
assert(
|
|
624
|
-
this.#socketOptions.family.toLocaleLowerCase() === "ipv4",
|
|
625
|
-
"not-supported"
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
return this[symbolSocketState].ipv6Only;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* @param {boolean} value
|
|
633
|
-
* @returns {void}
|
|
634
|
-
* @throws {invalid-state} (set) The socket is already bound.
|
|
635
|
-
* @throws {invalid-state} (get/set) `this` socket is an IPv4 socket.
|
|
636
|
-
* @throws {not-supported} (set) Host does not support dual-stack sockets. (Implementations are not required to.)
|
|
637
|
-
*/
|
|
638
|
-
setIpv6Only(value) {
|
|
639
|
-
assert(
|
|
640
|
-
this.#socketOptions.family.toLocaleLowerCase() === "ipv4",
|
|
641
|
-
"not-supported"
|
|
642
|
-
);
|
|
643
|
-
assert(this[symbolSocketState].isBound, "invalid-state");
|
|
644
|
-
|
|
645
|
-
this[symbolSocketState].ipv6Only = value;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* @param {bigint} value
|
|
650
|
-
* @returns {void}
|
|
651
|
-
* @throws {not-supported} (set) The platform does not support changing the backlog size after the initial listen.
|
|
652
|
-
* @throws {invalid-argument} (set) The provided value was 0.
|
|
653
|
-
* @throws {invalid-state} (set) The socket is already in the Connection state.
|
|
654
|
-
*/
|
|
655
|
-
setListenBacklogSize(value) {
|
|
656
|
-
|
|
657
|
-
assert(value === 0n, "invalid-argument", "The provided value was 0.");
|
|
658
|
-
assert(
|
|
659
|
-
this[symbolSocketState].connectionState ===
|
|
660
|
-
SocketConnectionState.Connected,
|
|
661
|
-
"invalid-state"
|
|
662
|
-
);
|
|
663
|
-
|
|
664
|
-
this[symbolSocketState].backlogSize = Number(value);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* @returns {boolean}
|
|
669
|
-
*/
|
|
670
|
-
keepAliveEnabled() {
|
|
671
|
-
return this[symbolSocketState].keepAlive;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* @param {boolean} value
|
|
676
|
-
* @returns {void}
|
|
677
|
-
*/
|
|
678
|
-
setKeepAliveEnabled(value) {
|
|
679
|
-
ioCall(SOCKET_TCP_SET_KEEP_ALIVE, this.id, {
|
|
680
|
-
keepAlive: value,
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
this[symbolSocketState].keepAlive = value;
|
|
684
|
-
|
|
685
|
-
if (value === true) {
|
|
686
|
-
this.setKeepAliveIdleTime(this.keepAliveIdleTime());
|
|
687
|
-
this.setKeepAliveInterval(this.keepAliveInterval());
|
|
688
|
-
this.setKeepAliveCount(this.keepAliveCount());
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
*
|
|
694
|
-
* @returns {Duration}
|
|
695
|
-
*/
|
|
696
|
-
keepAliveIdleTime() {
|
|
697
|
-
return this[symbolSocketState].keepAliveIdleTime;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
*
|
|
702
|
-
* @param {Duration} value
|
|
703
|
-
* @returns {void}
|
|
704
|
-
* @throws {invalid-argument} (set) The idle time must be 1 or higher.
|
|
705
|
-
*/
|
|
706
|
-
setKeepAliveIdleTime(value) {
|
|
707
|
-
value = Number(value);
|
|
708
|
-
assert(value < 1, "invalid-argument", "The idle time must be 1 or higher.");
|
|
709
|
-
|
|
710
|
-
this[symbolSocketState].keepAliveIdleTime = value;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
/**
|
|
714
|
-
*
|
|
715
|
-
* @returns {Duration}
|
|
716
|
-
*/
|
|
717
|
-
keepAliveInterval() {
|
|
718
|
-
return this[symbolSocketState].keepAliveInterval;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
*
|
|
723
|
-
* @param {Duration} value
|
|
724
|
-
* @returns {void}
|
|
725
|
-
* @throws {invalid-argument} (set) The interval must be 1 or higher.
|
|
726
|
-
*/
|
|
727
|
-
setKeepAliveInterval(value) {
|
|
728
|
-
value = Number(value);
|
|
729
|
-
assert(value < 1, "invalid-argument", "The interval must be 1 or higher.");
|
|
730
|
-
|
|
731
|
-
this[symbolSocketState].keepAliveInterval = value;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
*
|
|
736
|
-
* @returns {Duration}
|
|
737
|
-
*/
|
|
738
|
-
keepAliveCount() {
|
|
739
|
-
return this[symbolSocketState].keepAliveCount;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
*
|
|
744
|
-
* @param {Duration} value
|
|
745
|
-
* @returns {void}
|
|
746
|
-
* @throws {invalid-argument} (set) The count must be 1 or higher.
|
|
747
|
-
*/
|
|
748
|
-
setKeepAliveCount(value) {
|
|
749
|
-
value = Number(value);
|
|
750
|
-
assert(value < 1, "invalid-argument", "The count must be 1 or higher.");
|
|
751
|
-
|
|
752
|
-
// TODO: set this on the client socket as well
|
|
753
|
-
this[symbolSocketState].keepAliveCount = value;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* @returns {number}
|
|
758
|
-
* @description Not available on Node.js (see https://github.com/WebAssembly/wasi-sockets/blob/main/Posix-compatibility.md#socket-options)
|
|
759
|
-
*/
|
|
760
|
-
hopLimit() {
|
|
761
|
-
return this[symbolSocketState].hopLimit;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* @param {number} value
|
|
766
|
-
* @returns {void}
|
|
767
|
-
* @throws {invalid-argument} (set) The TTL value must be 1 or higher.
|
|
768
|
-
* @throws {invalid-state} (set) The socket is already in the Connection state.
|
|
769
|
-
* @throws {invalid-state} (set) The socket is already in the Listener state.
|
|
770
|
-
* @description Not available on Node.js (see https://github.com/WebAssembly/wasi-sockets/blob/main/Posix-compatibility.md#socket-options)
|
|
771
|
-
*/
|
|
772
|
-
setHopLimit(value) {
|
|
773
|
-
value = Number(value);
|
|
774
|
-
assert(value < 1, "invalid-argument", "The TTL value must be 1 or higher.");
|
|
775
|
-
|
|
776
|
-
this[symbolSocketState].hopLimit = value;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* @returns {bigint}
|
|
781
|
-
*/
|
|
782
|
-
receiveBufferSize() {
|
|
783
|
-
return BigInt(this[symbolSocketState].receiveBufferSize);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
* @param {number} value
|
|
788
|
-
* @returns {void}
|
|
789
|
-
* @throws {not-supported} (set) The platform does not support changing the backlog size after the initial listen.
|
|
790
|
-
* @throws {invalid-argument} (set) The provided value was 0.
|
|
791
|
-
* @throws {invalid-state} (set) The socket is already in the Connection state.
|
|
792
|
-
*/
|
|
793
|
-
setReceiveBufferSize(value) {
|
|
794
|
-
value = Number(value);
|
|
795
|
-
|
|
796
|
-
// TODO: review these assertions based on WIT specs
|
|
797
|
-
// assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
|
|
798
|
-
assert(value === 0, "invalid-argument", "The provided value was 0.");
|
|
799
|
-
|
|
800
|
-
// TODO: set this on the client socket as well
|
|
801
|
-
this[symbolSocketState].receiveBufferSize = value;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* @returns {bigint}
|
|
806
|
-
*/
|
|
807
|
-
sendBufferSize() {
|
|
808
|
-
return BigInt(this[symbolSocketState].sendBufferSize);
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/**
|
|
812
|
-
* @param {bigint} value
|
|
813
|
-
* @returns {void}
|
|
814
|
-
* @throws {invalid-argument} (set) The provided value was 0.
|
|
815
|
-
* @throws {invalid-state} (set) The socket is already in the Connection state.
|
|
816
|
-
* @throws {invalid-state} (set) The socket is already in the Listener state.
|
|
817
|
-
*/
|
|
818
|
-
setSendBufferSize(value) {
|
|
819
|
-
value = Number(value);
|
|
820
|
-
|
|
821
|
-
// TODO: review these assertions based on WIT specs
|
|
822
|
-
// assert(this[symbolSocketState].connectionState === SocketConnectionState.Connected, "invalid-state");
|
|
823
|
-
assert(value === 0, "invalid-argument", "The provided value was 0.");
|
|
824
|
-
|
|
825
|
-
// TODO: set this on the client socket as well
|
|
826
|
-
this[symbolSocketState].sendBufferSize = value;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/**
|
|
830
|
-
* @returns {Pollable}
|
|
831
|
-
*/
|
|
832
|
-
subscribe() {
|
|
833
|
-
if (this.#pollId) return pollableCreate(this.#pollId);
|
|
834
|
-
// 0 poll is immediately resolving
|
|
835
|
-
return pollableCreate(0);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
/**
|
|
839
|
-
* @param {ShutdownType} shutdownType
|
|
840
|
-
* @returns {void}
|
|
841
|
-
* @throws {invalid-state} The socket is not in the Connection state. (ENOTCONN)
|
|
842
|
-
*/
|
|
843
|
-
shutdown(shutdownType) {
|
|
844
|
-
assert(
|
|
845
|
-
this[symbolSocketState].connectionState !==
|
|
846
|
-
SocketConnectionState.Connected,
|
|
847
|
-
"invalid-state"
|
|
848
|
-
);
|
|
849
|
-
|
|
850
|
-
// TODO: figure out how to handle shutdownTypes
|
|
851
|
-
if (shutdownType === ShutdownType.Receive) {
|
|
852
|
-
this[symbolSocketState].canReceive = false;
|
|
853
|
-
} else if (shutdownType === ShutdownType.Send) {
|
|
854
|
-
this[symbolSocketState].canSend = false;
|
|
855
|
-
} else if (shutdownType === ShutdownType.Both) {
|
|
856
|
-
this[symbolSocketState].canReceive = false;
|
|
857
|
-
this[symbolSocketState].canSend = false;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
const err = ioCall(SOCKET_TCP_SHUTDOWN, this.id, {
|
|
861
|
-
shutdownType,
|
|
862
|
-
});
|
|
863
|
-
|
|
864
|
-
assert(err === 1, "invalid-state");
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
[symbolDispose]() {
|
|
868
|
-
ioCall(SOCKET_TCP_DISPOSE, this.id);
|
|
869
|
-
|
|
870
|
-
// we only need to remove the bound address from the global map
|
|
871
|
-
// if the socket was already bound
|
|
872
|
-
if (this[symbolSocketState].isBound) {
|
|
873
|
-
globalBoundAddresses.delete(
|
|
874
|
-
serializeIpAddress(this.#socketOptions.localIpSocketAddress, true)
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
handle() {
|
|
880
|
-
// return this.#socket;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
export const tcpSocketImplCreate = TcpSocket._create;
|
|
885
|
-
delete TcpSocket._create;
|