@bytecodealliance/preview3-shim 0.3.1 → 0.4.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.
|
@@ -255,7 +255,13 @@ export class TcpSocket {
|
|
|
255
255
|
}
|
|
256
256
|
let stream;
|
|
257
257
|
try {
|
|
258
|
-
|
|
258
|
+
// A remote error arrives from the TCP worker through a MessagePort. Yield
|
|
259
|
+
// after each chunk so a guest producer that completes writes immediately
|
|
260
|
+
// cannot starve that error (and its stream cancellation) indefinitely.
|
|
261
|
+
stream = readableByteStreamFromReader(data, {
|
|
262
|
+
name: "tcp send data",
|
|
263
|
+
yieldAfterChunk: true,
|
|
264
|
+
});
|
|
259
265
|
}
|
|
260
266
|
catch (error) {
|
|
261
267
|
if (error instanceof TypeError) {
|
package/dist/nodejs/stream.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export declare const DEFAULT_BYTE_STREAM_CHUNK_SIZE: number;
|
|
2
2
|
declare const symbolDispose: symbol;
|
|
3
|
+
interface ReadableByteStreamOptions {
|
|
4
|
+
chunkSize?: number;
|
|
5
|
+
name?: string;
|
|
6
|
+
yieldAfterChunk?: boolean;
|
|
7
|
+
}
|
|
3
8
|
/**
|
|
4
9
|
* Creates a transferable ReadableStream from an async iterator.
|
|
5
10
|
*
|
|
@@ -22,9 +27,10 @@ export declare function readableStreamFromIterator(iterator: any, name?: string)
|
|
|
22
27
|
* @param {object} [opts={}] - Optional adapter settings.
|
|
23
28
|
* @param {number} [opts.chunkSize=DEFAULT_BYTE_STREAM_CHUNK_SIZE] - Maximum bytes to request per generated `read()` call.
|
|
24
29
|
* @param {string} [opts.name="stream reader"] - Reader name used in error messages.
|
|
30
|
+
* @param {boolean} [opts.yieldAfterChunk=false] - Yield one event-loop turn after each chunk.
|
|
25
31
|
* @returns {ReadableStream<Uint8Array>} A transferable stream of byte chunks.
|
|
26
32
|
*/
|
|
27
|
-
export declare function readableByteStreamFromReader(reader: any, opts?:
|
|
33
|
+
export declare function readableByteStreamFromReader(reader: any, opts?: ReadableByteStreamOptions): ReadableStream<any>;
|
|
28
34
|
export declare function _byteChunk(value: any): any;
|
|
29
35
|
/**
|
|
30
36
|
* Creates a bidirectional stream with separate reader and writer interfaces.
|
package/dist/nodejs/stream.js
CHANGED
|
@@ -46,6 +46,7 @@ export function readableStreamFromIterator(iterator, name = "iterator") {
|
|
|
46
46
|
* @param {object} [opts={}] - Optional adapter settings.
|
|
47
47
|
* @param {number} [opts.chunkSize=DEFAULT_BYTE_STREAM_CHUNK_SIZE] - Maximum bytes to request per generated `read()` call.
|
|
48
48
|
* @param {string} [opts.name="stream reader"] - Reader name used in error messages.
|
|
49
|
+
* @param {boolean} [opts.yieldAfterChunk=false] - Yield one event-loop turn after each chunk.
|
|
49
50
|
* @returns {ReadableStream<Uint8Array>} A transferable stream of byte chunks.
|
|
50
51
|
*/
|
|
51
52
|
export function readableByteStreamFromReader(reader, opts = {}) {
|
|
@@ -58,6 +59,9 @@ export function readableByteStreamFromReader(reader, opts = {}) {
|
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
controller.enqueue(_byteChunk(value));
|
|
62
|
+
if (opts.yieldAfterChunk) {
|
|
63
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
64
|
+
}
|
|
61
65
|
},
|
|
62
66
|
cancel(reason) {
|
|
63
67
|
return source.cancel?.(reason);
|
|
@@ -3,7 +3,6 @@ import { Readable } from "stream";
|
|
|
3
3
|
import { pipeline } from "stream/promises";
|
|
4
4
|
import { once } from "node:events";
|
|
5
5
|
import { isMainThread, parentPort } from "worker_threads";
|
|
6
|
-
import process from "node:process";
|
|
7
6
|
//#region dist/nodejs/workers/resource-worker.js
|
|
8
7
|
function Router() {
|
|
9
8
|
const _hooks = {
|
|
@@ -255,7 +254,6 @@ var SocketError = class SocketError extends Error {
|
|
|
255
254
|
};
|
|
256
255
|
//#endregion
|
|
257
256
|
//#region dist/nodejs/workers/tcp-worker.js
|
|
258
|
-
const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
|
|
259
257
|
const sockets = /* @__PURE__ */ new Map();
|
|
260
258
|
let NEXT_SOCKET_ID = 0n;
|
|
261
259
|
Router().beforeAll((msg) => {
|
|
@@ -263,14 +261,15 @@ Router().beforeAll((msg) => {
|
|
|
263
261
|
}).op("tcp-create", handleTcpCreate).op("tcp-bind", handleTcpBind).op("tcp-connect", handleTcpConnect).op("tcp-listen", handleTcpListen).op("tcp-send", handleTcpSend).op("tcp-receive", handleTcpReceive).op("tcp-get-local-address", handleGetLocalAddress).op("tcp-get-remote-address", handleGetRemoteAddress).op("tcp-set-listen-backlog-size", handleTcpSetBacklogSize).op("tcp-set-keep-alive", handleTcpSetKeepAlive).op("tcp-recv-buffer-size", handleRecvBufferSize).op("tcp-send-buffer-size", handleSendBufferSize).op("tcp-dispose", handleTcpDispose);
|
|
264
262
|
function handleTcpCreate({ family }) {
|
|
265
263
|
const socketId = NEXT_SOCKET_ID++;
|
|
266
|
-
const handle = new TCP(TCPConstants.SOCKET);
|
|
267
264
|
sockets.set(socketId, {
|
|
268
|
-
handle,
|
|
269
265
|
family,
|
|
270
266
|
tcp: null,
|
|
271
267
|
server: null,
|
|
268
|
+
acceptWriter: null,
|
|
272
269
|
backlog: 128,
|
|
273
270
|
localAddress: null,
|
|
271
|
+
keepAliveEnabled: false,
|
|
272
|
+
keepAliveIdleTime: 0,
|
|
274
273
|
disposed: false,
|
|
275
274
|
activeStreams: 0
|
|
276
275
|
});
|
|
@@ -280,80 +279,104 @@ async function handleTcpBind({ socketId, localAddress }) {
|
|
|
280
279
|
const socket = sockets.get(socketId);
|
|
281
280
|
const address = serializeIpAddress(localAddress);
|
|
282
281
|
const port = localAddress.val.port;
|
|
283
|
-
const {
|
|
282
|
+
const { family } = socket;
|
|
284
283
|
if ([...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress))) {
|
|
285
284
|
const err = /* @__PURE__ */ new Error("EADDRINUSE");
|
|
286
285
|
err.code = "EADDRINUSE";
|
|
287
286
|
throw err;
|
|
288
287
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
288
|
+
const server = socket.server = createTcpServer(socket);
|
|
289
|
+
const onListening = once(server, "listening");
|
|
290
|
+
server.listen({
|
|
291
|
+
host: address,
|
|
292
|
+
port,
|
|
293
|
+
backlog: socket.backlog,
|
|
294
|
+
ipv6Only: family === "ipv6"
|
|
295
295
|
});
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
socket.localAddress = makeIpAddress(
|
|
296
|
+
await onListening;
|
|
297
|
+
const boundAddress = server.address();
|
|
298
|
+
if (!boundAddress || typeof boundAddress === "string") throw new SocketError("invalid-state");
|
|
299
|
+
socket.localAddress = makeIpAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
300
300
|
}
|
|
301
301
|
async function handleTcpConnect({ socketId, remoteAddress }) {
|
|
302
302
|
const socket = sockets.get(socketId);
|
|
303
303
|
const host = serializeIpAddress(remoteAddress);
|
|
304
304
|
const port = remoteAddress.val.port;
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
});
|
|
305
|
+
const localAddress = socket.localAddress;
|
|
306
|
+
await closeTcpServer(socket);
|
|
307
|
+
const tcp = socket.tcp = new Socket({ allowHalfOpen: true });
|
|
308
|
+
tcp.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
310
309
|
const onConnect = once(tcp, "connect");
|
|
311
|
-
const onError = once(tcp, "error").then(([err]) => {
|
|
312
|
-
throw err;
|
|
313
|
-
});
|
|
314
310
|
tcp.connect({
|
|
315
311
|
port,
|
|
316
|
-
host
|
|
312
|
+
host,
|
|
313
|
+
localAddress: localAddress ? serializeIpAddress(localAddress) : void 0,
|
|
314
|
+
localPort: localAddress?.val.port
|
|
317
315
|
});
|
|
318
|
-
await
|
|
316
|
+
await onConnect;
|
|
319
317
|
}
|
|
320
318
|
async function handleTcpListen({ socketId, stream }) {
|
|
321
319
|
const writer = stream.getWriter();
|
|
322
320
|
const socket = sockets.get(socketId);
|
|
323
|
-
const {
|
|
321
|
+
const { backlog, family } = socket;
|
|
322
|
+
socket.acceptWriter = writer;
|
|
323
|
+
let server = socket.server;
|
|
324
|
+
if (!server) {
|
|
325
|
+
server = socket.server = createTcpServer(socket);
|
|
326
|
+
const onListening = once(server, "listening");
|
|
327
|
+
server.listen({
|
|
328
|
+
host: family === "ipv6" ? "::" : "0.0.0.0",
|
|
329
|
+
port: 0,
|
|
330
|
+
backlog,
|
|
331
|
+
ipv6Only: family === "ipv6"
|
|
332
|
+
});
|
|
333
|
+
await onListening;
|
|
334
|
+
}
|
|
335
|
+
const addr = server.address();
|
|
336
|
+
if (addr && typeof addr === "object") socket.localAddress = makeIpAddress(family, addr.address, addr.port);
|
|
337
|
+
}
|
|
338
|
+
function createTcpServer(socket) {
|
|
324
339
|
const server = new Server({
|
|
325
340
|
pauseOnConnect: true,
|
|
326
341
|
allowHalfOpen: true
|
|
327
342
|
});
|
|
328
|
-
const onListening = once(server, "listening");
|
|
329
|
-
const onError = once(server, "error").then(([err]) => {
|
|
330
|
-
throw err;
|
|
331
|
-
});
|
|
332
|
-
server.listen(handle, backlog);
|
|
333
|
-
await Promise.race([onListening, onError]);
|
|
334
|
-
const addr = server.address();
|
|
335
|
-
if (addr && typeof addr === "object") socket.localAddress = makeIpAddress(family, addr.address, addr.port);
|
|
336
343
|
server.on("connection", (conn) => {
|
|
344
|
+
const writer = socket.acceptWriter;
|
|
345
|
+
if (!writer) {
|
|
346
|
+
conn.destroy();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
337
349
|
conn.allowHalfOpen = true;
|
|
350
|
+
conn.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
338
351
|
const id = NEXT_SOCKET_ID++;
|
|
339
352
|
sockets.set(id, {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
backlog,
|
|
353
|
+
family: socket.family,
|
|
354
|
+
backlog: socket.backlog,
|
|
343
355
|
tcp: conn,
|
|
344
356
|
server: null,
|
|
345
|
-
|
|
357
|
+
acceptWriter: null,
|
|
358
|
+
localAddress: makeIpAddress(socket.family, conn.localAddress, conn.localPort),
|
|
359
|
+
keepAliveEnabled: socket.keepAliveEnabled,
|
|
360
|
+
keepAliveIdleTime: socket.keepAliveIdleTime,
|
|
346
361
|
disposed: false,
|
|
347
362
|
activeStreams: 0
|
|
348
363
|
});
|
|
349
364
|
writer.write({
|
|
350
|
-
family,
|
|
365
|
+
family: socket.family,
|
|
351
366
|
socketId: id
|
|
352
367
|
});
|
|
353
368
|
});
|
|
354
|
-
|
|
355
|
-
server.on("
|
|
356
|
-
server
|
|
369
|
+
server.on("error", (err) => socket.acceptWriter?.abort(err));
|
|
370
|
+
server.on("close", () => socket.acceptWriter?.close());
|
|
371
|
+
return server;
|
|
372
|
+
}
|
|
373
|
+
function closeTcpServer(socket) {
|
|
374
|
+
const server = socket.server;
|
|
375
|
+
socket.server = null;
|
|
376
|
+
if (!server) return Promise.resolve();
|
|
377
|
+
return new Promise((resolve, reject) => {
|
|
378
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
379
|
+
});
|
|
357
380
|
}
|
|
358
381
|
async function handleTcpSend({ socketId, stream }) {
|
|
359
382
|
const socket = sockets.get(socketId);
|
|
@@ -428,22 +451,19 @@ function cleanupDisposedSocket(socketId, socket) {
|
|
|
428
451
|
if (!socket.disposed || socket.activeStreams > 0) return;
|
|
429
452
|
if (socket.server) socket.server.close();
|
|
430
453
|
if (socket.tcp) socket.tcp.destroy();
|
|
431
|
-
if (socket.handle) socket.handle.close();
|
|
432
454
|
sockets.delete(socketId);
|
|
433
455
|
}
|
|
434
456
|
async function handleGetLocalAddress({ socketId }) {
|
|
435
457
|
const socket = sockets.get(socketId);
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
if (
|
|
439
|
-
|
|
458
|
+
const address = socket.tcp?.address();
|
|
459
|
+
if (address && typeof address !== "string" && "family" in address) return makeIpAddress(address.family.toLowerCase(), address.address, address.port);
|
|
460
|
+
if (socket.localAddress) return socket.localAddress;
|
|
461
|
+
throw new SocketError("invalid-state");
|
|
440
462
|
}
|
|
441
463
|
async function handleGetRemoteAddress({ socketId }) {
|
|
442
464
|
const socket = sockets.get(socketId);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (code !== 0) throw SocketError.from(-code);
|
|
446
|
-
return makeIpAddress(out.family.toLowerCase(), out.address, out.port);
|
|
465
|
+
if (!socket.tcp?.remoteFamily || !socket.tcp.remoteAddress || !socket.tcp.remotePort) throw new SocketError("invalid-state");
|
|
466
|
+
return makeIpAddress(socket.tcp.remoteFamily.toLowerCase(), socket.tcp.remoteAddress, socket.tcp.remotePort);
|
|
447
467
|
}
|
|
448
468
|
function handleTcpSetBacklogSize({ socketId, value }) {
|
|
449
469
|
const socket = sockets.get(socketId);
|
|
@@ -451,9 +471,9 @@ function handleTcpSetBacklogSize({ socketId, value }) {
|
|
|
451
471
|
}
|
|
452
472
|
async function handleTcpSetKeepAlive({ socketId, keepAliveEnabled, keepAliveIdleTime }) {
|
|
453
473
|
const socket = sockets.get(socketId);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
474
|
+
socket.keepAliveEnabled = keepAliveEnabled;
|
|
475
|
+
socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
|
|
476
|
+
socket.tcp?.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
457
477
|
}
|
|
458
478
|
async function handleRecvBufferSize({ socketId }) {
|
|
459
479
|
const socket = sockets.get(socketId);
|
|
@@ -5,8 +5,6 @@ import { once } from "node:events";
|
|
|
5
5
|
import { Router } from "../workers/resource-worker.js";
|
|
6
6
|
import { serializeIpAddress, makeIpAddress, ipAddressConflict } from "../sockets/address.js";
|
|
7
7
|
import { SocketError } from "../sockets/error.js";
|
|
8
|
-
import process from "node:process";
|
|
9
|
-
const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
|
|
10
8
|
// Socket instances stored by ID
|
|
11
9
|
const sockets = new Map();
|
|
12
10
|
// Unique IDs for sockets
|
|
@@ -34,14 +32,15 @@ Router()
|
|
|
34
32
|
// Create a new TCP socket
|
|
35
33
|
function handleTcpCreate({ family }) {
|
|
36
34
|
const socketId = NEXT_SOCKET_ID++;
|
|
37
|
-
const handle = new TCP(TCPConstants.SOCKET);
|
|
38
35
|
sockets.set(socketId, {
|
|
39
|
-
handle,
|
|
40
36
|
family,
|
|
41
37
|
tcp: null,
|
|
42
38
|
server: null,
|
|
39
|
+
acceptWriter: null,
|
|
43
40
|
backlog: 128,
|
|
44
41
|
localAddress: null,
|
|
42
|
+
keepAliveEnabled: false,
|
|
43
|
+
keepAliveIdleTime: 0,
|
|
45
44
|
disposed: false,
|
|
46
45
|
activeStreams: 0,
|
|
47
46
|
});
|
|
@@ -52,89 +51,120 @@ async function handleTcpBind({ socketId, localAddress }) {
|
|
|
52
51
|
const socket = sockets.get(socketId);
|
|
53
52
|
const address = serializeIpAddress(localAddress);
|
|
54
53
|
const port = localAddress.val.port;
|
|
55
|
-
const {
|
|
54
|
+
const { family } = socket;
|
|
56
55
|
const hasConflict = [...sockets].some(([id, { localAddress: boundAddress }]) => id !== socketId && boundAddress && ipAddressConflict(boundAddress, localAddress));
|
|
57
56
|
if (hasConflict) {
|
|
58
57
|
const err = new Error("EADDRINUSE");
|
|
59
58
|
err.code = "EADDRINUSE";
|
|
60
59
|
throw err;
|
|
61
60
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
reject(SocketError.from(-code));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
resolve();
|
|
75
|
-
}
|
|
61
|
+
// node:net has no public standalone TCP bind API. Keep a server open
|
|
62
|
+
// to reserve the endpoint, then reuse it for listen or close it for connect.
|
|
63
|
+
const server = (socket.server = createTcpServer(socket));
|
|
64
|
+
const onListening = once(server, "listening");
|
|
65
|
+
server.listen({
|
|
66
|
+
host: address,
|
|
67
|
+
port,
|
|
68
|
+
backlog: socket.backlog,
|
|
69
|
+
ipv6Only: family === "ipv6",
|
|
76
70
|
});
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
throw SocketError
|
|
71
|
+
await onListening;
|
|
72
|
+
const boundAddress = server.address();
|
|
73
|
+
if (!boundAddress || typeof boundAddress === "string") {
|
|
74
|
+
throw new SocketError("invalid-state");
|
|
81
75
|
}
|
|
82
|
-
socket.localAddress = makeIpAddress(
|
|
76
|
+
socket.localAddress = makeIpAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
83
77
|
}
|
|
84
78
|
// Connect a socket to remote address
|
|
85
79
|
async function handleTcpConnect({ socketId, remoteAddress }) {
|
|
86
80
|
const socket = sockets.get(socketId);
|
|
87
81
|
const host = serializeIpAddress(remoteAddress);
|
|
88
82
|
const port = remoteAddress.val.port;
|
|
83
|
+
const localAddress = socket.localAddress;
|
|
84
|
+
await closeTcpServer(socket);
|
|
89
85
|
const tcp = (socket.tcp = new Socket({
|
|
90
|
-
handle: socket.handle,
|
|
91
|
-
pauseOnCreate: true,
|
|
92
86
|
allowHalfOpen: true,
|
|
93
87
|
}));
|
|
88
|
+
tcp.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
89
|
+
// TODO(tandr): Add lookup
|
|
94
90
|
const onConnect = once(tcp, "connect");
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
tcp.connect({
|
|
92
|
+
port,
|
|
93
|
+
host,
|
|
94
|
+
localAddress: localAddress ? serializeIpAddress(localAddress) : undefined,
|
|
95
|
+
localPort: localAddress?.val.port,
|
|
97
96
|
});
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
// events.once rejects when the emitter produces an error while waiting and
|
|
98
|
+
// removes its temporary listeners after settling. A separate error promise
|
|
99
|
+
// would remain attached after a successful connect and could later reject
|
|
100
|
+
// unobserved when an established connection is reset.
|
|
101
|
+
await onConnect;
|
|
101
102
|
}
|
|
102
103
|
async function handleTcpListen({ socketId, stream }) {
|
|
103
104
|
const writer = stream.getWriter();
|
|
104
105
|
const socket = sockets.get(socketId);
|
|
105
|
-
const {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
106
|
+
const { backlog, family } = socket;
|
|
107
|
+
socket.acceptWriter = writer;
|
|
108
|
+
let server = socket.server;
|
|
109
|
+
if (!server) {
|
|
110
|
+
server = socket.server = createTcpServer(socket);
|
|
111
|
+
const onListening = once(server, "listening");
|
|
112
|
+
server.listen({
|
|
113
|
+
host: family === "ipv6" ? "::" : "0.0.0.0",
|
|
114
|
+
port: 0,
|
|
115
|
+
backlog,
|
|
116
|
+
ipv6Only: family === "ipv6",
|
|
117
|
+
});
|
|
118
|
+
await onListening;
|
|
119
|
+
}
|
|
116
120
|
const addr = server.address();
|
|
117
121
|
if (addr && typeof addr === "object") {
|
|
118
122
|
socket.localAddress = makeIpAddress(family, addr.address, addr.port);
|
|
119
123
|
}
|
|
124
|
+
}
|
|
125
|
+
function createTcpServer(socket) {
|
|
126
|
+
const server = new Server({
|
|
127
|
+
pauseOnConnect: true,
|
|
128
|
+
allowHalfOpen: true,
|
|
129
|
+
});
|
|
120
130
|
server.on("connection", (conn) => {
|
|
131
|
+
const writer = socket.acceptWriter;
|
|
132
|
+
// The reservation is already listening at the OS level, so reject any
|
|
133
|
+
// connections that arrive before the WASI socket enters its listen state.
|
|
134
|
+
if (!writer) {
|
|
135
|
+
conn.destroy();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
121
138
|
conn.allowHalfOpen = true;
|
|
139
|
+
conn.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
122
140
|
const id = NEXT_SOCKET_ID++;
|
|
123
141
|
sockets.set(id, {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
backlog,
|
|
142
|
+
family: socket.family,
|
|
143
|
+
backlog: socket.backlog,
|
|
127
144
|
tcp: conn,
|
|
128
145
|
server: null,
|
|
129
|
-
|
|
146
|
+
acceptWriter: null,
|
|
147
|
+
localAddress: makeIpAddress(socket.family, conn.localAddress, conn.localPort),
|
|
148
|
+
keepAliveEnabled: socket.keepAliveEnabled,
|
|
149
|
+
keepAliveIdleTime: socket.keepAliveIdleTime,
|
|
130
150
|
disposed: false,
|
|
131
151
|
activeStreams: 0,
|
|
132
152
|
});
|
|
133
|
-
writer.write({ family, socketId: id });
|
|
153
|
+
writer.write({ family: socket.family, socketId: id });
|
|
154
|
+
});
|
|
155
|
+
server.on("error", (err) => socket.acceptWriter?.abort(err));
|
|
156
|
+
server.on("close", () => socket.acceptWriter?.close());
|
|
157
|
+
return server;
|
|
158
|
+
}
|
|
159
|
+
function closeTcpServer(socket) {
|
|
160
|
+
const server = socket.server;
|
|
161
|
+
socket.server = null;
|
|
162
|
+
if (!server) {
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
}
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
134
167
|
});
|
|
135
|
-
socket.server = server;
|
|
136
|
-
server.on("error", (err) => stream.abort(err));
|
|
137
|
-
server.on("close", () => writer.close());
|
|
138
168
|
}
|
|
139
169
|
async function handleTcpSend({ socketId, stream }) {
|
|
140
170
|
const socket = sockets.get(socketId);
|
|
@@ -231,28 +261,25 @@ function cleanupDisposedSocket(socketId, socket) {
|
|
|
231
261
|
if (socket.tcp) {
|
|
232
262
|
socket.tcp.destroy();
|
|
233
263
|
}
|
|
234
|
-
if (socket.handle) {
|
|
235
|
-
socket.handle.close();
|
|
236
|
-
}
|
|
237
264
|
sockets.delete(socketId);
|
|
238
265
|
}
|
|
239
266
|
async function handleGetLocalAddress({ socketId }) {
|
|
240
267
|
const socket = sockets.get(socketId);
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
throw SocketError.from(-code);
|
|
268
|
+
const address = socket.tcp?.address();
|
|
269
|
+
if (address && typeof address !== "string" && "family" in address) {
|
|
270
|
+
return makeIpAddress(address.family.toLowerCase(), address.address, address.port);
|
|
245
271
|
}
|
|
246
|
-
|
|
272
|
+
if (socket.localAddress) {
|
|
273
|
+
return socket.localAddress;
|
|
274
|
+
}
|
|
275
|
+
throw new SocketError("invalid-state");
|
|
247
276
|
}
|
|
248
277
|
async function handleGetRemoteAddress({ socketId }) {
|
|
249
278
|
const socket = sockets.get(socketId);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (code !== 0) {
|
|
253
|
-
throw SocketError.from(-code);
|
|
279
|
+
if (!socket.tcp?.remoteFamily || !socket.tcp.remoteAddress || !socket.tcp.remotePort) {
|
|
280
|
+
throw new SocketError("invalid-state");
|
|
254
281
|
}
|
|
255
|
-
return makeIpAddress(
|
|
282
|
+
return makeIpAddress(socket.tcp.remoteFamily.toLowerCase(), socket.tcp.remoteAddress, socket.tcp.remotePort);
|
|
256
283
|
}
|
|
257
284
|
function handleTcpSetBacklogSize({ socketId, value }) {
|
|
258
285
|
const socket = sockets.get(socketId);
|
|
@@ -260,11 +287,9 @@ function handleTcpSetBacklogSize({ socketId, value }) {
|
|
|
260
287
|
}
|
|
261
288
|
async function handleTcpSetKeepAlive({ socketId, keepAliveEnabled, keepAliveIdleTime }) {
|
|
262
289
|
const socket = sockets.get(socketId);
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
throw SocketError.from(-code);
|
|
267
|
-
}
|
|
290
|
+
socket.keepAliveEnabled = keepAliveEnabled;
|
|
291
|
+
socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
|
|
292
|
+
socket.tcp?.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
268
293
|
}
|
|
269
294
|
async function handleRecvBufferSize({ socketId }) {
|
|
270
295
|
const socket = sockets.get(socketId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytecodealliance/preview3-shim",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "WASI Preview3 shim for JS environments",
|
|
5
5
|
"homepage": "https://github.com/bytecodealliance/jco/blob/main/packages/preview3-shim/#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@bytecodealliance/preview2-shim": "^0.
|
|
51
|
+
"@bytecodealliance/preview2-shim": "^0.21.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^24.12.4",
|