@bytecodealliance/preview2-shim 0.20.1 → 0.21.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/dist/browser/filesystem.d.ts +1 -1
- package/dist/io/calls.d.ts +11 -0
- package/dist/io/calls.js +12 -0
- package/dist/io/worker-filesystem.d.ts +29 -0
- package/dist/io/worker-filesystem.js +116 -0
- package/dist/io/worker-http.d.ts +2 -2
- package/dist/io/worker-socket-tcp.d.ts +5 -2
- package/dist/io/worker-socket-tcp.js +121 -95
- package/dist/io/worker-sockets.d.ts +4 -4
- package/dist/io/worker-thread.bundle.js +283 -97
- package/dist/io/worker-thread.d.ts +1 -0
- package/dist/io/worker-thread.js +44 -7
- package/dist/nodejs/filesystem.d.ts +6 -9
- package/dist/nodejs/filesystem.js +37 -26
- package/package.json +6 -5
|
@@ -38,7 +38,7 @@ declare class Descriptor implements TypesNamespace.Descriptor {
|
|
|
38
38
|
advise(offset: Filesize, length: Filesize, advice: TypesNamespace.Advice): void;
|
|
39
39
|
syncData(): void;
|
|
40
40
|
getFlags(): TypesNamespace.DescriptorFlags;
|
|
41
|
-
getType(): "directory" | "
|
|
41
|
+
getType(): "directory" | "fifo" | "regular-file" | "unknown";
|
|
42
42
|
setSize(size: bigint): void;
|
|
43
43
|
setTimes(dataAccessTimestamp: any, dataModificationTimestamp: any): void;
|
|
44
44
|
read(length: bigint, offset: bigint): [Uint8Array<ArrayBufferLike>, boolean];
|
package/dist/io/calls.d.ts
CHANGED
|
@@ -29,6 +29,17 @@ export declare const OUTPUT_STREAM_BLOCKING_SPLICE: number;
|
|
|
29
29
|
export declare const OUTPUT_STREAM_SUBSCRIBE: number;
|
|
30
30
|
export declare const OUTPUT_STREAM_DISPOSE: number;
|
|
31
31
|
export declare const OUTPUT_STREAM_GET_TOTAL_BYTES: number;
|
|
32
|
+
export declare const FILESYSTEM_DESCRIPTOR_OPEN: number;
|
|
33
|
+
export declare const FILESYSTEM_DESCRIPTOR_CLOSE: number;
|
|
34
|
+
export declare const FILESYSTEM_DESCRIPTOR_SYNC_DATA: number;
|
|
35
|
+
export declare const FILESYSTEM_DESCRIPTOR_GET_TYPE: number;
|
|
36
|
+
export declare const FILESYSTEM_DESCRIPTOR_SET_SIZE: number;
|
|
37
|
+
export declare const FILESYSTEM_DESCRIPTOR_SET_TIMES: number;
|
|
38
|
+
export declare const FILESYSTEM_DESCRIPTOR_READ: number;
|
|
39
|
+
export declare const FILESYSTEM_DESCRIPTOR_WRITE: number;
|
|
40
|
+
export declare const FILESYSTEM_DESCRIPTOR_SYNC: number;
|
|
41
|
+
export declare const FILESYSTEM_DESCRIPTOR_STAT: number;
|
|
42
|
+
export declare const FILESYSTEM_DESCRIPTOR_METADATA_HASH: number;
|
|
32
43
|
export declare const POLL_POLLABLE_READY: number;
|
|
33
44
|
export declare const POLL_POLLABLE_BLOCK: number;
|
|
34
45
|
export declare const POLL_POLLABLE_DISPOSE: number;
|
package/dist/io/calls.js
CHANGED
|
@@ -35,6 +35,18 @@ export const OUTPUT_STREAM_BLOCKING_SPLICE = ++call_id << CALL_SHIFT;
|
|
|
35
35
|
export const OUTPUT_STREAM_SUBSCRIBE = ++call_id << CALL_SHIFT;
|
|
36
36
|
export const OUTPUT_STREAM_DISPOSE = ++call_id << CALL_SHIFT;
|
|
37
37
|
export const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << CALL_SHIFT;
|
|
38
|
+
// Filesystem descriptors
|
|
39
|
+
export const FILESYSTEM_DESCRIPTOR_OPEN = ++call_id << CALL_SHIFT;
|
|
40
|
+
export const FILESYSTEM_DESCRIPTOR_CLOSE = ++call_id << CALL_SHIFT;
|
|
41
|
+
export const FILESYSTEM_DESCRIPTOR_SYNC_DATA = ++call_id << CALL_SHIFT;
|
|
42
|
+
export const FILESYSTEM_DESCRIPTOR_GET_TYPE = ++call_id << CALL_SHIFT;
|
|
43
|
+
export const FILESYSTEM_DESCRIPTOR_SET_SIZE = ++call_id << CALL_SHIFT;
|
|
44
|
+
export const FILESYSTEM_DESCRIPTOR_SET_TIMES = ++call_id << CALL_SHIFT;
|
|
45
|
+
export const FILESYSTEM_DESCRIPTOR_READ = ++call_id << CALL_SHIFT;
|
|
46
|
+
export const FILESYSTEM_DESCRIPTOR_WRITE = ++call_id << CALL_SHIFT;
|
|
47
|
+
export const FILESYSTEM_DESCRIPTOR_SYNC = ++call_id << CALL_SHIFT;
|
|
48
|
+
export const FILESYSTEM_DESCRIPTOR_STAT = ++call_id << CALL_SHIFT;
|
|
49
|
+
export const FILESYSTEM_DESCRIPTOR_METADATA_HASH = ++call_id << CALL_SHIFT;
|
|
38
50
|
// Io Poll
|
|
39
51
|
export const POLL_POLLABLE_READY = ++call_id << CALL_SHIFT;
|
|
40
52
|
export const POLL_POLLABLE_BLOCK = ++call_id << CALL_SHIFT;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare function openFileResource(path: string, flags: number): Promise<{
|
|
2
|
+
id: number;
|
|
3
|
+
type: any;
|
|
4
|
+
}>;
|
|
5
|
+
export declare function retainFileResource(id: number): number;
|
|
6
|
+
export declare function releaseFileResource(id: number): Promise<void>;
|
|
7
|
+
export declare function fileResourceFd(id: number): number;
|
|
8
|
+
export declare function syncFileResourceData(id: number): Promise<void>;
|
|
9
|
+
export declare function getFileResourceType(id: number): Promise<"block-device" | "character-device" | "directory" | "fifo" | "regular-file" | "socket" | "symbolic-link" | "unknown">;
|
|
10
|
+
export declare function setFileResourceSize(id: number, size: number): Promise<void>;
|
|
11
|
+
export declare function setFileResourceTimes(id: number, atime: number, mtime: number): Promise<void>;
|
|
12
|
+
export declare function readFileResource(id: number, length: number, offset: number): Promise<{
|
|
13
|
+
bytes: Uint8Array<ArrayBuffer>;
|
|
14
|
+
eof: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function writeFileResource(id: number, buffer: Uint8Array, offset: number): Promise<number>;
|
|
17
|
+
export declare function syncFileResource(id: number): Promise<void>;
|
|
18
|
+
export declare function statFileResource(id: number): Promise<{
|
|
19
|
+
type: string;
|
|
20
|
+
nlink: bigint;
|
|
21
|
+
size: bigint;
|
|
22
|
+
atimeNs: bigint;
|
|
23
|
+
mtimeNs: bigint;
|
|
24
|
+
ctimeNs: bigint;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function metadataHashFileResource(id: number): Promise<{
|
|
27
|
+
upper: bigint;
|
|
28
|
+
lower: bigint;
|
|
29
|
+
}>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const closeAsync = promisify(fs.close);
|
|
4
|
+
const fdatasyncAsync = promisify(fs.fdatasync);
|
|
5
|
+
const fstatAsync = promisify(fs.fstat);
|
|
6
|
+
const fsyncAsync = promisify(fs.fsync);
|
|
7
|
+
const ftruncateAsync = promisify(fs.ftruncate);
|
|
8
|
+
const futimesAsync = promisify(fs.futimes);
|
|
9
|
+
const openAsync = promisify(fs.open);
|
|
10
|
+
const readAsync = promisify(fs.read);
|
|
11
|
+
const writeAsync = promisify(fs.write);
|
|
12
|
+
let nextFileResourceId = 0;
|
|
13
|
+
const fileResources = new Map();
|
|
14
|
+
function lookupType(obj) {
|
|
15
|
+
if (obj.isFile()) {
|
|
16
|
+
return "regular-file";
|
|
17
|
+
}
|
|
18
|
+
if (obj.isSocket()) {
|
|
19
|
+
return "socket";
|
|
20
|
+
}
|
|
21
|
+
if (obj.isSymbolicLink()) {
|
|
22
|
+
return "symbolic-link";
|
|
23
|
+
}
|
|
24
|
+
if (obj.isFIFO()) {
|
|
25
|
+
return "fifo";
|
|
26
|
+
}
|
|
27
|
+
if (obj.isDirectory()) {
|
|
28
|
+
return "directory";
|
|
29
|
+
}
|
|
30
|
+
if (obj.isCharacterDevice()) {
|
|
31
|
+
return "character-device";
|
|
32
|
+
}
|
|
33
|
+
if (obj.isBlockDevice()) {
|
|
34
|
+
return "block-device";
|
|
35
|
+
}
|
|
36
|
+
return "unknown";
|
|
37
|
+
}
|
|
38
|
+
function getFileResource(id) {
|
|
39
|
+
const resource = fileResources.get(id);
|
|
40
|
+
if (!resource) {
|
|
41
|
+
const error = new Error(`Invalid filesystem descriptor resource: ${id}`);
|
|
42
|
+
error.code = "EBADF";
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
return resource;
|
|
46
|
+
}
|
|
47
|
+
export async function openFileResource(path, flags) {
|
|
48
|
+
const fd = await openAsync(path, flags);
|
|
49
|
+
let type;
|
|
50
|
+
try {
|
|
51
|
+
type = lookupType(await fstatAsync(fd, { bigint: true }));
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
await closeAsync(fd);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
const id = ++nextFileResourceId;
|
|
58
|
+
fileResources.set(id, { fd, refs: 1 });
|
|
59
|
+
return { id, type };
|
|
60
|
+
}
|
|
61
|
+
export function retainFileResource(id) {
|
|
62
|
+
getFileResource(id).refs++;
|
|
63
|
+
return id;
|
|
64
|
+
}
|
|
65
|
+
export async function releaseFileResource(id) {
|
|
66
|
+
const resource = getFileResource(id);
|
|
67
|
+
if (--resource.refs === 0) {
|
|
68
|
+
fileResources.delete(id);
|
|
69
|
+
await closeAsync(resource.fd);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function fileResourceFd(id) {
|
|
73
|
+
return getFileResource(id).fd;
|
|
74
|
+
}
|
|
75
|
+
export async function syncFileResourceData(id) {
|
|
76
|
+
await fdatasyncAsync(fileResourceFd(id));
|
|
77
|
+
}
|
|
78
|
+
export async function getFileResourceType(id) {
|
|
79
|
+
return lookupType(await fstatAsync(fileResourceFd(id), { bigint: true }));
|
|
80
|
+
}
|
|
81
|
+
export async function setFileResourceSize(id, size) {
|
|
82
|
+
await ftruncateAsync(fileResourceFd(id), size);
|
|
83
|
+
}
|
|
84
|
+
export async function setFileResourceTimes(id, atime, mtime) {
|
|
85
|
+
await futimesAsync(fileResourceFd(id), atime, mtime);
|
|
86
|
+
}
|
|
87
|
+
export async function readFileResource(id, length, offset) {
|
|
88
|
+
const buffer = new Uint8Array(length);
|
|
89
|
+
const result = await readAsync(fileResourceFd(id), buffer, 0, length, offset);
|
|
90
|
+
// Deno v1 returns the byte count directly, while Node returns an object
|
|
91
|
+
// containing the byte count and buffer through util.promisify's custom args.
|
|
92
|
+
const bytesRead = typeof result === "number" ? result : result.bytesRead;
|
|
93
|
+
return { bytes: buffer.subarray(0, bytesRead), eof: bytesRead === 0 };
|
|
94
|
+
}
|
|
95
|
+
export async function writeFileResource(id, buffer, offset) {
|
|
96
|
+
const result = await writeAsync(fileResourceFd(id), buffer, 0, buffer.byteLength, offset);
|
|
97
|
+
return typeof result === "number" ? result : result.bytesWritten;
|
|
98
|
+
}
|
|
99
|
+
export async function syncFileResource(id) {
|
|
100
|
+
await fsyncAsync(fileResourceFd(id));
|
|
101
|
+
}
|
|
102
|
+
export async function statFileResource(id) {
|
|
103
|
+
const stats = await fstatAsync(fileResourceFd(id), { bigint: true });
|
|
104
|
+
return {
|
|
105
|
+
type: lookupType(stats),
|
|
106
|
+
nlink: stats.nlink,
|
|
107
|
+
size: stats.size,
|
|
108
|
+
atimeNs: stats.atimeNs,
|
|
109
|
+
mtimeNs: stats.mtimeNs,
|
|
110
|
+
ctimeNs: stats.ctimeNs,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export async function metadataHashFileResource(id) {
|
|
114
|
+
const stats = await fstatAsync(fileResourceFd(id), { bigint: true });
|
|
115
|
+
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
116
|
+
}
|
package/dist/io/worker-http.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ export declare function stopHttpServer(id: any): Promise<void>;
|
|
|
2
2
|
export declare function clearOutgoingResponse(id: any): void;
|
|
3
3
|
export declare function getHttpServerAddress(id: any): Promise<any>;
|
|
4
4
|
export declare function setOutgoingResponse(id: any, { statusCode, headers, streamId }: {
|
|
5
|
-
statusCode: any;
|
|
6
5
|
headers: any;
|
|
6
|
+
statusCode: any;
|
|
7
7
|
streamId: any;
|
|
8
8
|
}): Promise<void>;
|
|
9
9
|
export declare function startHttpServer(id: number, { port, host }: {
|
|
10
|
-
port: any;
|
|
11
10
|
host: any;
|
|
11
|
+
port: any;
|
|
12
12
|
}): Promise<void>;
|
|
13
13
|
export declare function createHttpRequest(method: any, scheme: any, authority: any, pathWithQuery: any, headers: any, bodyId: any, connectTimeout: any, betweenBytesTimeout: any, firstByteTimeout: any): Promise<{
|
|
14
14
|
status: number | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PollState } from "./worker-thread.js";
|
|
2
|
-
import { Socket as TcpSocket } from "node:net";
|
|
2
|
+
import { Server, Socket as TcpSocket } from "node:net";
|
|
3
3
|
import { IpSocketAddress } from "../../types/interfaces/wasi-sockets-network.js";
|
|
4
4
|
interface PendingAccept {
|
|
5
5
|
tcpSocket: TcpSocket | null;
|
|
@@ -10,8 +10,11 @@ interface TcpSocketRecord {
|
|
|
10
10
|
state: number;
|
|
11
11
|
future: number | null;
|
|
12
12
|
tcpSocket: TcpSocket | null;
|
|
13
|
+
server: Server | null;
|
|
14
|
+
localAddress: IpSocketAddress | null;
|
|
13
15
|
listenBacklogSize: number;
|
|
14
|
-
|
|
16
|
+
keepAlive: boolean;
|
|
17
|
+
keepAliveIdleTime: number;
|
|
15
18
|
pendingAccepts: PendingAccept[];
|
|
16
19
|
pollState: PollState | null;
|
|
17
20
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { createFuture, createReadableStream, createReadableStreamPollState, createWritableStream, futureDispose, futureTakeValue, pollStateReady, verifyPollsDroppedForDrop, } from "./worker-thread.js";
|
|
2
2
|
import process from "node:process";
|
|
3
|
-
|
|
4
|
-
const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
|
|
5
|
-
import { convertSocketError, convertSocketErrorCode, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
|
|
3
|
+
import { convertSocketError, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
|
|
6
4
|
import { Server, Socket as TcpSocket } from "node:net";
|
|
7
5
|
const win = process.platform === "win32";
|
|
8
6
|
export const tcpSockets = new Map();
|
|
9
7
|
let tcpSocketCnt = 0;
|
|
10
8
|
export function createTcpSocket() {
|
|
11
|
-
const handle = new TCP(TCPConstants.SOCKET);
|
|
12
9
|
tcpSockets.set(++tcpSocketCnt, {
|
|
13
10
|
state: SOCKET_STATE_INIT,
|
|
14
11
|
future: null,
|
|
15
12
|
tcpSocket: null,
|
|
13
|
+
server: null,
|
|
14
|
+
localAddress: null,
|
|
16
15
|
listenBacklogSize: 128,
|
|
17
|
-
|
|
16
|
+
keepAlive: false,
|
|
17
|
+
keepAliveIdleTime: 0,
|
|
18
18
|
pendingAccepts: [],
|
|
19
19
|
pollState: {
|
|
20
20
|
ready: true,
|
|
@@ -44,7 +44,7 @@ export function socketTcpFinish(id, fromState, toState) {
|
|
|
44
44
|
socket.state = toState;
|
|
45
45
|
// for the listener, we must immediately transition back to unresolved
|
|
46
46
|
if (toState === SOCKET_STATE_LISTENER) {
|
|
47
|
-
socket.pollState.ready =
|
|
47
|
+
socket.pollState.ready = socket.pendingAccepts.length > 0;
|
|
48
48
|
}
|
|
49
49
|
return val;
|
|
50
50
|
}
|
|
@@ -60,26 +60,77 @@ export function socketTcpBindStart(id, localAddress, family) {
|
|
|
60
60
|
throw "invalid-argument";
|
|
61
61
|
}
|
|
62
62
|
socket.state = SOCKET_STATE_BIND;
|
|
63
|
-
|
|
64
|
-
socket.future = createFuture((async () => {
|
|
63
|
+
socket.future = createFuture(new Promise((resolve, reject) => {
|
|
65
64
|
const address = serializeIpAddress(localAddress);
|
|
66
65
|
const port = localAddress.val.port;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
// node:net has no public standalone TCP bind API. Keep a server open
|
|
67
|
+
// to reserve the endpoint, then reuse it for listen or close it for connect.
|
|
68
|
+
const server = (socket.server = createTcpServer(socket));
|
|
69
|
+
function handleErr(err) {
|
|
70
|
+
server.off("listening", handleListen);
|
|
71
|
+
reject(convertSocketError(err));
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw convertSocketErrorCode(-code);
|
|
73
|
+
function handleListen() {
|
|
74
|
+
server.off("error", handleErr);
|
|
75
|
+
const boundAddress = server.address();
|
|
76
|
+
if (!boundAddress || typeof boundAddress === "string") {
|
|
77
|
+
reject("invalid-state");
|
|
78
|
+
return;
|
|
80
79
|
}
|
|
80
|
+
socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
81
|
+
resolve();
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
+
server.once("listening", handleListen);
|
|
84
|
+
server.once("error", handleErr);
|
|
85
|
+
server.listen({
|
|
86
|
+
host: address ?? undefined,
|
|
87
|
+
port,
|
|
88
|
+
backlog: socket.listenBacklogSize,
|
|
89
|
+
ipv6Only: family === "ipv6",
|
|
90
|
+
});
|
|
91
|
+
}), socket.pollState);
|
|
92
|
+
}
|
|
93
|
+
function createTcpServer(socket) {
|
|
94
|
+
const server = new Server({
|
|
95
|
+
pauseOnConnect: true,
|
|
96
|
+
allowHalfOpen: true,
|
|
97
|
+
});
|
|
98
|
+
server.on("connection", (tcpSocket) => {
|
|
99
|
+
// The reservation is already listening at the OS level, so reject any
|
|
100
|
+
// connections that arrive before the WASI socket enters its listen state.
|
|
101
|
+
if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
|
|
102
|
+
tcpSocket.destroy();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
pollStateReady(socket.pollState);
|
|
106
|
+
const pollState = createReadableStreamPollState(tcpSocket);
|
|
107
|
+
socket.pendingAccepts.push({
|
|
108
|
+
tcpSocket,
|
|
109
|
+
err: null,
|
|
110
|
+
pollState,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
server.on("error", (err) => {
|
|
114
|
+
if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
|
|
115
|
+
pollStateReady(socket.pollState);
|
|
116
|
+
socket.pendingAccepts.push({
|
|
117
|
+
tcpSocket: null,
|
|
118
|
+
err,
|
|
119
|
+
pollState: null,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return server;
|
|
124
|
+
}
|
|
125
|
+
function closeTcpServer(socket) {
|
|
126
|
+
const server = socket.server;
|
|
127
|
+
socket.server = null;
|
|
128
|
+
if (!server) {
|
|
129
|
+
return Promise.resolve();
|
|
130
|
+
}
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
133
|
+
});
|
|
83
134
|
}
|
|
84
135
|
export function socketTcpConnectStart(id, remoteAddress, family) {
|
|
85
136
|
const socket = tcpSockets.get(id);
|
|
@@ -95,71 +146,44 @@ export function socketTcpConnectStart(id, remoteAddress, family) {
|
|
|
95
146
|
throw "invalid-argument";
|
|
96
147
|
}
|
|
97
148
|
socket.state = SOCKET_STATE_CONNECT;
|
|
98
|
-
socket.future = createFuture(
|
|
149
|
+
socket.future = createFuture((async () => {
|
|
150
|
+
const localAddress = socket.localAddress;
|
|
151
|
+
await closeTcpServer(socket);
|
|
99
152
|
const tcpSocket = (socket.tcpSocket = new TcpSocket({
|
|
100
|
-
// @ts-expect-error Not present in type definition
|
|
101
|
-
handle: socket.handle,
|
|
102
|
-
pauseOnCreate: true,
|
|
103
153
|
allowHalfOpen: true,
|
|
104
154
|
}));
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
function handleErr(err) {
|
|
158
|
+
tcpSocket.off("connect", handleConnect);
|
|
159
|
+
reject(convertSocketError(err));
|
|
160
|
+
}
|
|
161
|
+
function handleConnect() {
|
|
162
|
+
tcpSocket.off("error", handleErr);
|
|
163
|
+
resolve();
|
|
164
|
+
}
|
|
165
|
+
tcpSocket.once("connect", handleConnect);
|
|
166
|
+
tcpSocket.once("error", handleErr);
|
|
167
|
+
tcpSocket.connect({
|
|
168
|
+
port: remoteAddress.val.port,
|
|
169
|
+
host: serializeIpAddress(remoteAddress) ?? undefined,
|
|
170
|
+
lookup: noLookup,
|
|
171
|
+
localAddress: localAddress
|
|
172
|
+
? (serializeIpAddress(localAddress) ?? undefined)
|
|
173
|
+
: undefined,
|
|
174
|
+
localPort: localAddress?.val.port,
|
|
175
|
+
});
|
|
119
176
|
});
|
|
120
|
-
|
|
177
|
+
return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
|
|
178
|
+
})(), socket.pollState);
|
|
121
179
|
}
|
|
122
180
|
export function socketTcpListenStart(id) {
|
|
123
181
|
const socket = tcpSockets.get(id);
|
|
124
182
|
if (socket.state !== SOCKET_STATE_BOUND) {
|
|
125
183
|
throw "invalid-state";
|
|
126
184
|
}
|
|
127
|
-
const { handle } = socket;
|
|
128
185
|
socket.state = SOCKET_STATE_LISTEN;
|
|
129
|
-
socket.future = createFuture(
|
|
130
|
-
const server = new Server({
|
|
131
|
-
pauseOnConnect: true,
|
|
132
|
-
allowHalfOpen: true,
|
|
133
|
-
});
|
|
134
|
-
function handleErr(err) {
|
|
135
|
-
server.off("listening", handleListen);
|
|
136
|
-
reject(convertSocketError(err));
|
|
137
|
-
}
|
|
138
|
-
function handleListen() {
|
|
139
|
-
server.off("error", handleErr);
|
|
140
|
-
server.on("connection", (tcpSocket) => {
|
|
141
|
-
pollStateReady(socket.pollState);
|
|
142
|
-
const pollState = createReadableStreamPollState(tcpSocket);
|
|
143
|
-
socket.pendingAccepts.push({
|
|
144
|
-
tcpSocket,
|
|
145
|
-
err: null,
|
|
146
|
-
pollState,
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
server.on("error", (err) => {
|
|
150
|
-
pollStateReady(socket.pollState);
|
|
151
|
-
socket.pendingAccepts.push({
|
|
152
|
-
tcpSocket: null,
|
|
153
|
-
err,
|
|
154
|
-
pollState: null,
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
resolve();
|
|
158
|
-
}
|
|
159
|
-
server.once("listening", handleListen);
|
|
160
|
-
server.once("error", handleErr);
|
|
161
|
-
server.listen(handle, socket.listenBacklogSize);
|
|
162
|
-
}), socket.pollState);
|
|
186
|
+
socket.future = createFuture(Promise.resolve(), socket.pollState);
|
|
163
187
|
}
|
|
164
188
|
export function socketTcpAccept(id) {
|
|
165
189
|
const socket = tcpSockets.get(id);
|
|
@@ -181,9 +205,11 @@ export function socketTcpAccept(id) {
|
|
|
181
205
|
state: SOCKET_STATE_CONNECTION,
|
|
182
206
|
future: null,
|
|
183
207
|
tcpSocket: accept.tcpSocket,
|
|
208
|
+
server: null,
|
|
209
|
+
localAddress: null,
|
|
184
210
|
listenBacklogSize: 128,
|
|
185
|
-
|
|
186
|
-
|
|
211
|
+
keepAlive: false,
|
|
212
|
+
keepAliveIdleTime: 0,
|
|
187
213
|
pendingAccepts: [],
|
|
188
214
|
pollState: accept.pollState,
|
|
189
215
|
});
|
|
@@ -207,22 +233,22 @@ export function socketTcpSetListenBacklogSize(id, backlogSize) {
|
|
|
207
233
|
socket.listenBacklogSize = Number(backlogSize);
|
|
208
234
|
}
|
|
209
235
|
export function socketTcpGetLocalAddress(id) {
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
236
|
+
const socket = tcpSockets.get(id);
|
|
237
|
+
const address = socket.tcpSocket?.address();
|
|
238
|
+
if (address && typeof address !== "string" && "family" in address) {
|
|
239
|
+
return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
|
|
240
|
+
}
|
|
241
|
+
if (socket.localAddress) {
|
|
242
|
+
return socket.localAddress;
|
|
215
243
|
}
|
|
216
|
-
|
|
244
|
+
throw "invalid-state";
|
|
217
245
|
}
|
|
218
246
|
export function socketTcpGetRemoteAddress(id) {
|
|
219
|
-
const {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (code !== 0) {
|
|
223
|
-
throw convertSocketErrorCode(-code);
|
|
247
|
+
const { tcpSocket } = tcpSockets.get(id);
|
|
248
|
+
if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) {
|
|
249
|
+
throw "invalid-state";
|
|
224
250
|
}
|
|
225
|
-
return ipSocketAddress(
|
|
251
|
+
return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
|
|
226
252
|
}
|
|
227
253
|
export function socketTcpShutdown(id, _shutdownType) {
|
|
228
254
|
const socket = tcpSockets.get(id);
|
|
@@ -237,15 +263,15 @@ export function socketTcpShutdown(id, _shutdownType) {
|
|
|
237
263
|
}
|
|
238
264
|
}
|
|
239
265
|
export function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
266
|
+
const socket = tcpSockets.get(id);
|
|
267
|
+
socket.keepAlive = keepAlive;
|
|
268
|
+
socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
|
|
269
|
+
socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
245
270
|
}
|
|
246
271
|
export function socketTcpDispose(id) {
|
|
247
272
|
const socket = tcpSockets.get(id);
|
|
248
273
|
verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
|
|
249
|
-
socket.
|
|
274
|
+
socket.server?.close();
|
|
275
|
+
socket.tcpSocket?.destroy();
|
|
250
276
|
tcpSockets.delete(id);
|
|
251
277
|
}
|
|
@@ -11,12 +11,12 @@ export declare function noLookup(ip: any, _opts: any, cb: any): void;
|
|
|
11
11
|
export declare function socketResolveAddress(name: string): Promise<{
|
|
12
12
|
tag: string;
|
|
13
13
|
val: Ipv4Address | Ipv6Address;
|
|
14
|
-
}[]> | Promise<{
|
|
14
|
+
}[][]> | Promise<{
|
|
15
15
|
tag: string;
|
|
16
16
|
val: Ipv4Address | Ipv6Address;
|
|
17
|
-
}[]
|
|
18
|
-
export declare function convertSocketError(err: any): "
|
|
19
|
-
export declare function convertSocketErrorCode(code: any): "
|
|
17
|
+
}[]>;
|
|
18
|
+
export declare function convertSocketError(err: any): "access-denied" | "address-in-use" | "address-not-bindable" | "concurrency-conflict" | "connection-aborted" | "connection-refused" | "connection-reset" | "invalid-argument" | "invalid-state" | "not-supported" | "out-of-memory" | "unknown" | "would-block";
|
|
19
|
+
export declare function convertSocketErrorCode(code: any): "access-denied" | "address-in-use" | "address-not-bindable" | "concurrency-conflict" | "connection-aborted" | "connection-refused" | "connection-reset" | "invalid-argument" | "invalid-state" | "not-supported" | "out-of-memory" | "unknown" | "would-block";
|
|
20
20
|
export declare function tupleToIPv6(arr: Ipv6Address): string | null;
|
|
21
21
|
export declare function tupleToIpv4(arr: Ipv4Address): string | null;
|
|
22
22
|
export declare function isMulticastIpAddress(ipSocketAddress: IpSocketAddress): boolean;
|