@bytecodealliance/preview2-shim 0.22.0 → 0.24.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/README.md +148 -7
- package/dist/browser/cli.d.ts +22 -1
- package/dist/browser/cli.js +66 -34
- package/dist/browser/clocks.js +24 -5
- package/dist/browser/common.d.ts +5 -0
- package/dist/browser/common.js +16 -0
- package/dist/browser/filesystem.d.ts +55 -97
- package/dist/browser/filesystem.js +106 -287
- package/dist/browser/http.d.ts +9 -0
- package/dist/browser/http.js +446 -136
- package/dist/browser/in-memory-filesystem.d.ts +22 -0
- package/dist/browser/in-memory-filesystem.js +530 -0
- package/dist/browser/in-memory-http.d.ts +8 -0
- package/dist/browser/in-memory-http.js +11 -0
- package/dist/browser/in-memory-sockets.d.ts +34 -0
- package/dist/browser/in-memory-sockets.js +294 -0
- package/dist/browser/io.d.ts +11 -2
- package/dist/browser/io.js +200 -39
- package/dist/browser/random.js +5 -3
- package/dist/browser/sockets.d.ts +2 -1
- package/dist/browser/sockets.js +75 -56
- package/dist/common/instantiation.d.ts +4 -1
- package/dist/common/instantiation.js +85 -49
- package/dist/io/worker-socket-tcp.js +14 -12
- package/dist/io/worker-socket-udp.js +3 -4
- package/dist/io/worker-sockets.d.ts +4 -4
- package/dist/io/worker-sockets.js +29 -9
- package/dist/io/worker-thread.bundle.js +28 -13
- package/dist/nodejs/filesystem.d.ts +7 -0
- package/dist/nodejs/filesystem.js +65 -22
- package/dist/nodejs/sockets.d.ts +13 -0
- package/dist/nodejs/sockets.js +20 -0
- package/package.json +1 -1
- package/types/instantiation.d.ts +64 -12
package/dist/browser/random.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { checkedU64AsNumber } from "./common.js";
|
|
1
2
|
const MAX_BYTES = 65536;
|
|
2
3
|
let insecureRandomValue1, insecureRandomValue2;
|
|
3
4
|
export const insecure = {
|
|
@@ -20,11 +21,12 @@ export const insecureSeed = {
|
|
|
20
21
|
};
|
|
21
22
|
export const random = {
|
|
22
23
|
getRandomBytes(len) {
|
|
23
|
-
const
|
|
24
|
-
|
|
24
|
+
const byteLength = checkedU64AsNumber(len, "random byte length");
|
|
25
|
+
const bytes = new Uint8Array(byteLength);
|
|
26
|
+
if (byteLength > MAX_BYTES) {
|
|
25
27
|
// this is the max bytes crypto.getRandomValues
|
|
26
28
|
// can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues
|
|
27
|
-
for (
|
|
29
|
+
for (let generated = 0; generated < byteLength; generated += MAX_BYTES) {
|
|
28
30
|
// buffer.slice automatically checks if the end is past the end of
|
|
29
31
|
// the buffer so we don't have to here
|
|
30
32
|
crypto.getRandomValues(bytes.subarray(generated, generated + MAX_BYTES));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { instanceNetwork as InstanceNetworkNamespace, ipNameLookup as IpNameLookupNamespace, network as NetworkNamespace, tcpCreateSocket as TcpCreateSocketNamespace, tcp as TcpNamespace, udpCreateSocket as UdpCreateSocketNamespace, udp as UdpNamespace } from "../../types/sockets.js";
|
|
2
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "./in-memory-sockets.js";
|
|
2
3
|
export declare const instanceNetwork: typeof InstanceNetworkNamespace;
|
|
3
|
-
export declare const ipNameLookup: typeof IpNameLookupNamespace;
|
|
4
4
|
export declare const network: typeof NetworkNamespace;
|
|
5
|
+
export declare const ipNameLookup: typeof IpNameLookupNamespace;
|
|
5
6
|
export declare const tcpCreateSocket: typeof TcpCreateSocketNamespace;
|
|
6
7
|
export declare const tcp: typeof TcpNamespace;
|
|
7
8
|
export declare const udpCreateSocket: typeof UdpCreateSocketNamespace;
|
package/dist/browser/sockets.js
CHANGED
|
@@ -1,66 +1,85 @@
|
|
|
1
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "./in-memory-sockets.js";
|
|
2
|
+
const unsupported = () => {
|
|
3
|
+
throw "not-supported";
|
|
4
|
+
};
|
|
5
|
+
class Network {
|
|
6
|
+
}
|
|
7
|
+
const defaultNetwork = new Network();
|
|
1
8
|
export const instanceNetwork = {
|
|
2
|
-
instanceNetwork()
|
|
3
|
-
console.log(`[sockets] instance network`);
|
|
4
|
-
},
|
|
9
|
+
instanceNetwork: () => defaultNetwork,
|
|
5
10
|
};
|
|
11
|
+
export const network = { Network };
|
|
12
|
+
class ResolveAddressStream {
|
|
13
|
+
resolveNextAddress = unsupported;
|
|
14
|
+
subscribe = unsupported;
|
|
15
|
+
}
|
|
6
16
|
export const ipNameLookup = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
resolveAddresses() { },
|
|
10
|
-
resolveNextAddress() { },
|
|
11
|
-
nonBlocking() { },
|
|
12
|
-
setNonBlocking() { },
|
|
13
|
-
};
|
|
14
|
-
export const network = {
|
|
15
|
-
dropNetwork() { },
|
|
17
|
+
ResolveAddressStream,
|
|
18
|
+
resolveAddresses: unsupported,
|
|
16
19
|
};
|
|
20
|
+
class TcpSocket {
|
|
21
|
+
startBind = unsupported;
|
|
22
|
+
finishBind = unsupported;
|
|
23
|
+
startConnect = unsupported;
|
|
24
|
+
finishConnect = unsupported;
|
|
25
|
+
startListen = unsupported;
|
|
26
|
+
finishListen = unsupported;
|
|
27
|
+
accept = unsupported;
|
|
28
|
+
localAddress = unsupported;
|
|
29
|
+
remoteAddress = unsupported;
|
|
30
|
+
isListening = unsupported;
|
|
31
|
+
addressFamily = unsupported;
|
|
32
|
+
setListenBacklogSize = unsupported;
|
|
33
|
+
keepAliveEnabled = unsupported;
|
|
34
|
+
setKeepAliveEnabled = unsupported;
|
|
35
|
+
keepAliveIdleTime = unsupported;
|
|
36
|
+
setKeepAliveIdleTime = unsupported;
|
|
37
|
+
keepAliveInterval = unsupported;
|
|
38
|
+
setKeepAliveInterval = unsupported;
|
|
39
|
+
keepAliveCount = unsupported;
|
|
40
|
+
setKeepAliveCount = unsupported;
|
|
41
|
+
hopLimit = unsupported;
|
|
42
|
+
setHopLimit = unsupported;
|
|
43
|
+
receiveBufferSize = unsupported;
|
|
44
|
+
setReceiveBufferSize = unsupported;
|
|
45
|
+
sendBufferSize = unsupported;
|
|
46
|
+
setSendBufferSize = unsupported;
|
|
47
|
+
subscribe = unsupported;
|
|
48
|
+
shutdown = unsupported;
|
|
49
|
+
}
|
|
17
50
|
export const tcpCreateSocket = {
|
|
18
|
-
createTcpSocket
|
|
19
|
-
};
|
|
20
|
-
export const tcp = {
|
|
21
|
-
subscribe() { },
|
|
22
|
-
dropTcpSocket() { },
|
|
23
|
-
bind() { },
|
|
24
|
-
connect() { },
|
|
25
|
-
listen() { },
|
|
26
|
-
accept() { },
|
|
27
|
-
localAddress() { },
|
|
28
|
-
remoteAddress() { },
|
|
29
|
-
addressFamily() { },
|
|
30
|
-
setListenBacklogSize() { },
|
|
31
|
-
keepAlive() { },
|
|
32
|
-
setKeepAlive() { },
|
|
33
|
-
noDelay() { },
|
|
34
|
-
setNoDelay() { },
|
|
35
|
-
unicastHopLimit() { },
|
|
36
|
-
setUnicastHopLimit() { },
|
|
37
|
-
receiveBufferSize() { },
|
|
38
|
-
setReceiveBufferSize() { },
|
|
39
|
-
sendBufferSize() { },
|
|
40
|
-
setSendBufferSize() { },
|
|
41
|
-
nonBlocking() { },
|
|
42
|
-
setNonBlocking() { },
|
|
43
|
-
shutdown() { },
|
|
51
|
+
createTcpSocket: unsupported,
|
|
44
52
|
};
|
|
53
|
+
export const tcp = { TcpSocket };
|
|
54
|
+
class IncomingDatagramStream {
|
|
55
|
+
receive = unsupported;
|
|
56
|
+
subscribe = unsupported;
|
|
57
|
+
}
|
|
58
|
+
class OutgoingDatagramStream {
|
|
59
|
+
checkSend = unsupported;
|
|
60
|
+
send = unsupported;
|
|
61
|
+
subscribe = unsupported;
|
|
62
|
+
}
|
|
63
|
+
class UdpSocket {
|
|
64
|
+
startBind = unsupported;
|
|
65
|
+
finishBind = unsupported;
|
|
66
|
+
stream = unsupported;
|
|
67
|
+
localAddress = unsupported;
|
|
68
|
+
remoteAddress = unsupported;
|
|
69
|
+
addressFamily = unsupported;
|
|
70
|
+
unicastHopLimit = unsupported;
|
|
71
|
+
setUnicastHopLimit = unsupported;
|
|
72
|
+
receiveBufferSize = unsupported;
|
|
73
|
+
setReceiveBufferSize = unsupported;
|
|
74
|
+
sendBufferSize = unsupported;
|
|
75
|
+
setSendBufferSize = unsupported;
|
|
76
|
+
subscribe = unsupported;
|
|
77
|
+
}
|
|
45
78
|
export const udpCreateSocket = {
|
|
46
|
-
createUdpSocket
|
|
79
|
+
createUdpSocket: unsupported,
|
|
47
80
|
};
|
|
48
81
|
export const udp = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
connect() { },
|
|
53
|
-
receive() { },
|
|
54
|
-
send() { },
|
|
55
|
-
localAddress() { },
|
|
56
|
-
remoteAddress() { },
|
|
57
|
-
addressFamily() { },
|
|
58
|
-
unicastHopLimit() { },
|
|
59
|
-
setUnicastHopLimit() { },
|
|
60
|
-
receiveBufferSize() { },
|
|
61
|
-
setReceiveBufferSize() { },
|
|
62
|
-
sendBufferSize() { },
|
|
63
|
-
setSendBufferSize() { },
|
|
64
|
-
nonBlocking() { },
|
|
65
|
-
setNonBlocking() { },
|
|
82
|
+
IncomingDatagramStream,
|
|
83
|
+
OutgoingDatagramStream,
|
|
84
|
+
UdpSocket,
|
|
66
85
|
};
|
|
@@ -59,7 +59,7 @@ import type { WASIShimConfig, GetImportObjectArgs, WASIImportObject } from "../.
|
|
|
59
59
|
* }
|
|
60
60
|
* });
|
|
61
61
|
*
|
|
62
|
-
* //
|
|
62
|
+
* // Node.js only: limited host filesystem access
|
|
63
63
|
* const limitedShim = new WASIShim({
|
|
64
64
|
* sandbox: {
|
|
65
65
|
* preopens: {
|
|
@@ -70,6 +70,9 @@ import type { WASIShimConfig, GetImportObjectArgs, WASIImportObject } from "../.
|
|
|
70
70
|
* });
|
|
71
71
|
* ```
|
|
72
72
|
*
|
|
73
|
+
* Browser applications should inject `filesystem` namespaces or explicitly
|
|
74
|
+
* configure `browserFilesystem`; browser preopens never interpret host paths.
|
|
75
|
+
*
|
|
73
76
|
* Note that this object is similar but not identical to the Node `WASI` object --
|
|
74
77
|
* it is solely concerned with shimming of preview2 when dealing with a WebAssembly
|
|
75
78
|
* component transpiled by Jco. While this object *does* work with Node (and the browser)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as wasi from "@bytecodealliance/preview2-shim";
|
|
2
|
-
import { types, _createPreopenDescriptor } from "@bytecodealliance/preview2-shim/filesystem";
|
|
3
2
|
/**
|
|
4
3
|
* (EXPERIMENTAL) A class that holds WASI shims and can be used to configure
|
|
5
4
|
* an instantiation of a WebAssembly component transpiled with jco
|
|
@@ -60,7 +59,7 @@ import { types, _createPreopenDescriptor } from "@bytecodealliance/preview2-shim
|
|
|
60
59
|
* }
|
|
61
60
|
* });
|
|
62
61
|
*
|
|
63
|
-
* //
|
|
62
|
+
* // Node.js only: limited host filesystem access
|
|
64
63
|
* const limitedShim = new WASIShim({
|
|
65
64
|
* sandbox: {
|
|
66
65
|
* preopens: {
|
|
@@ -71,6 +70,9 @@ import { types, _createPreopenDescriptor } from "@bytecodealliance/preview2-shim
|
|
|
71
70
|
* });
|
|
72
71
|
* ```
|
|
73
72
|
*
|
|
73
|
+
* Browser applications should inject `filesystem` namespaces or explicitly
|
|
74
|
+
* configure `browserFilesystem`; browser preopens never interpret host paths.
|
|
75
|
+
*
|
|
74
76
|
* Note that this object is similar but not identical to the Node `WASI` object --
|
|
75
77
|
* it is solely concerned with shimming of preview2 when dealing with a WebAssembly
|
|
76
78
|
* component transpiled by Jco. While this object *does* work with Node (and the browser)
|
|
@@ -93,8 +95,6 @@ export class WASIShim {
|
|
|
93
95
|
#sockets;
|
|
94
96
|
/** Object that confirms to the shim interface for `wasi:http` */
|
|
95
97
|
#http;
|
|
96
|
-
/** Isolated preopens for this instance */
|
|
97
|
-
#preopens;
|
|
98
98
|
/** Isolated environment for this instance */
|
|
99
99
|
#environment;
|
|
100
100
|
/**
|
|
@@ -105,36 +105,94 @@ export class WASIShim {
|
|
|
105
105
|
constructor(config) {
|
|
106
106
|
// Support both old 'shims' parameter name and new 'config' style
|
|
107
107
|
const shims = config;
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
const sandbox = shims?.sandbox;
|
|
109
|
+
const defaultCli = wasi.cli;
|
|
110
|
+
this.#cli =
|
|
111
|
+
shims?.cli ??
|
|
112
|
+
(defaultCli.createCli &&
|
|
113
|
+
(shims?.environment !== undefined ||
|
|
114
|
+
shims?.arguments !== undefined ||
|
|
115
|
+
shims?.initialCwd !== undefined ||
|
|
116
|
+
shims?.stdin !== undefined ||
|
|
117
|
+
shims?.stdout !== undefined ||
|
|
118
|
+
shims?.stderr !== undefined)
|
|
119
|
+
? defaultCli.createCli({
|
|
120
|
+
environment: shims?.environment,
|
|
121
|
+
arguments: shims?.arguments,
|
|
122
|
+
initialCwd: shims?.initialCwd,
|
|
123
|
+
stdin: shims?.stdin,
|
|
124
|
+
stdout: shims?.stdout,
|
|
125
|
+
stderr: shims?.stderr,
|
|
126
|
+
})
|
|
127
|
+
: defaultCli);
|
|
128
|
+
const defaultFilesystem = wasi.filesystem;
|
|
129
|
+
if (shims?.browserFilesystem && !defaultFilesystem.createFilesystem) {
|
|
130
|
+
throw new TypeError("the selected filesystem does not support browser adapters");
|
|
131
|
+
}
|
|
132
|
+
this.#filesystem = shims?.filesystem ?? defaultFilesystem;
|
|
133
|
+
if (shims?.filesystem && sandbox?.preopens !== undefined) {
|
|
134
|
+
if (!shims.filesystem.createPreopens) {
|
|
135
|
+
throw new TypeError("an application-provided filesystem must implement createPreopens to use sandbox.preopens");
|
|
136
|
+
}
|
|
137
|
+
this.#filesystem = {
|
|
138
|
+
types: shims.filesystem.types,
|
|
139
|
+
preopens: shims.filesystem.createPreopens(sandbox.preopens),
|
|
140
|
+
dispose: shims.filesystem.dispose?.bind(shims.filesystem),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
else if (shims?.browserFilesystem) {
|
|
144
|
+
this.#filesystem = defaultFilesystem.createFilesystem({
|
|
145
|
+
adapter: shims.browserFilesystem.adapter,
|
|
146
|
+
preopens: sandbox?.preopens ?? shims.browserFilesystem.preopens,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
else if (sandbox?.preopens !== undefined) {
|
|
150
|
+
if (!defaultFilesystem.createFilesystem) {
|
|
151
|
+
throw new TypeError("the selected filesystem cannot create isolated preopens");
|
|
152
|
+
}
|
|
153
|
+
this.#filesystem = defaultFilesystem.createFilesystem({
|
|
154
|
+
preopens: sandbox.preopens,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
110
157
|
this.#io = shims?.io ?? wasi.io;
|
|
111
158
|
this.#random = shims?.random ?? wasi.random;
|
|
112
159
|
this.#clocks = shims?.clocks ?? wasi.clocks;
|
|
113
|
-
|
|
160
|
+
const defaultSockets = wasi.sockets;
|
|
161
|
+
this.#sockets =
|
|
162
|
+
shims?.sockets ??
|
|
163
|
+
(defaultSockets.createSockets
|
|
164
|
+
? defaultSockets.createSockets({
|
|
165
|
+
enableNetwork: shims?.sandbox?.enableNetwork,
|
|
166
|
+
})
|
|
167
|
+
: defaultSockets);
|
|
168
|
+
if (shims?.tcpSockets) {
|
|
169
|
+
this.#sockets = {
|
|
170
|
+
...this.#sockets,
|
|
171
|
+
tcp: shims.tcpSockets.tcp,
|
|
172
|
+
tcpCreateSocket: shims.tcpSockets.tcpCreateSocket,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (shims?.udpSockets) {
|
|
176
|
+
this.#sockets = {
|
|
177
|
+
...this.#sockets,
|
|
178
|
+
udp: shims.udpSockets.udp,
|
|
179
|
+
udpCreateSocket: shims.udpSockets.udpCreateSocket,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
114
182
|
this.#http = shims?.http ?? wasi.http;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.#
|
|
183
|
+
if (shims?.incomingHandler) {
|
|
184
|
+
if (!this.#http.createIncomingHandler) {
|
|
185
|
+
throw new TypeError("the selected HTTP shim does not support Web incoming handlers");
|
|
186
|
+
}
|
|
187
|
+
this.#http = {
|
|
188
|
+
...this.#http,
|
|
189
|
+
incomingHandler: this.#http.createIncomingHandler(shims.incomingHandler),
|
|
190
|
+
};
|
|
120
191
|
}
|
|
121
192
|
// Create isolated environment if env or args are configured
|
|
122
193
|
if (sandbox?.env !== undefined || sandbox?.args !== undefined) {
|
|
123
194
|
this.#environment = createIsolatedEnvironment(sandbox?.env, sandbox?.args, this.#cli);
|
|
124
195
|
}
|
|
125
|
-
// Apply network restrictions if disabled
|
|
126
|
-
if (sandbox?.enableNetwork === false) {
|
|
127
|
-
// Use the sockets module's built-in deny functions
|
|
128
|
-
if (this.#sockets._denyTcp) {
|
|
129
|
-
this.#sockets._denyTcp();
|
|
130
|
-
}
|
|
131
|
-
if (this.#sockets._denyUdp) {
|
|
132
|
-
this.#sockets._denyUdp();
|
|
133
|
-
}
|
|
134
|
-
if (this.#sockets._denyDnsLookup) {
|
|
135
|
-
this.#sockets._denyDnsLookup();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
196
|
}
|
|
139
197
|
/**
|
|
140
198
|
* Generate an import object for the shim that can be used with
|
|
@@ -164,8 +222,7 @@ export class WASIShim {
|
|
|
164
222
|
obj[`wasi:sockets/tcp-create-socket${versionSuffix}`] = this.#sockets.tcpCreateSocket;
|
|
165
223
|
obj[`wasi:sockets/udp${versionSuffix}`] = this.#sockets.udp;
|
|
166
224
|
obj[`wasi:sockets/udp-create-socket${versionSuffix}`] = this.#sockets.udpCreateSocket;
|
|
167
|
-
obj[`wasi:filesystem/preopens${versionSuffix}`] =
|
|
168
|
-
this.#preopens ?? this.#filesystem.preopens;
|
|
225
|
+
obj[`wasi:filesystem/preopens${versionSuffix}`] = this.#filesystem.preopens;
|
|
169
226
|
obj[`wasi:filesystem/types${versionSuffix}`] = this.#filesystem.types;
|
|
170
227
|
obj[`wasi:io/error${versionSuffix}`] = this.#io.error;
|
|
171
228
|
obj[`wasi:io/poll${versionSuffix}`] = this.#io.poll;
|
|
@@ -176,32 +233,11 @@ export class WASIShim {
|
|
|
176
233
|
obj[`wasi:clocks/monotonic-clock${versionSuffix}`] = this.#clocks.monotonicClock;
|
|
177
234
|
obj[`wasi:clocks/wall-clock${versionSuffix}`] = this.#clocks.wallClock;
|
|
178
235
|
obj[`wasi:http/types${versionSuffix}`] = this.#http.types;
|
|
236
|
+
obj[`wasi:http/incoming-handler${versionSuffix}`] = this.#http.incomingHandler;
|
|
179
237
|
obj[`wasi:http/outgoing-handler${versionSuffix}`] = this.#http.outgoingHandler;
|
|
180
238
|
return obj;
|
|
181
239
|
}
|
|
182
240
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Create an isolated preopens object with its own preopen entries.
|
|
185
|
-
*
|
|
186
|
-
* @param preopensConfig - Map of virtual paths to host paths
|
|
187
|
-
* @returns A preopens object with Descriptor and getDirectories()
|
|
188
|
-
*/
|
|
189
|
-
function createIsolatedPreopens(preopensConfig) {
|
|
190
|
-
const entries = [];
|
|
191
|
-
// Populate entries using the filesystem's descriptor creation
|
|
192
|
-
if (_createPreopenDescriptor) {
|
|
193
|
-
for (const [virtualPath, hostPath] of Object.entries(preopensConfig)) {
|
|
194
|
-
const descriptor = _createPreopenDescriptor(hostPath);
|
|
195
|
-
entries.push([descriptor, virtualPath]);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
Descriptor: types.Descriptor,
|
|
200
|
-
getDirectories() {
|
|
201
|
-
return entries;
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
241
|
/**
|
|
206
242
|
* Create an isolated CLI environment with its own env and args.
|
|
207
243
|
*
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { createFuture, createReadableStream, createReadableStreamPollState, createWritableStream, futureDispose, futureTakeValue, pollStateReady, verifyPollsDroppedForDrop, } from "./worker-thread.js";
|
|
2
|
-
import process from "node:process";
|
|
3
2
|
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";
|
|
4
3
|
import { Server, Socket as TcpSocket } from "node:net";
|
|
5
|
-
const win = process.platform === "win32";
|
|
6
4
|
export const tcpSockets = new Map();
|
|
7
5
|
let tcpSocketCnt = 0;
|
|
8
6
|
export function createTcpSocket() {
|
|
@@ -37,7 +35,16 @@ export function socketTcpFinish(id, fromState, toState) {
|
|
|
37
35
|
futureDispose(socket.future, false);
|
|
38
36
|
socket.future = null;
|
|
39
37
|
if (tag === "err") {
|
|
40
|
-
socket.
|
|
38
|
+
// A failed bind does not consume the socket. In particular, callers may
|
|
39
|
+
// retry after `address-in-use` with another address or port.
|
|
40
|
+
if (fromState === SOCKET_STATE_BIND) {
|
|
41
|
+
socket.server = null;
|
|
42
|
+
socket.localAddress = null;
|
|
43
|
+
socket.state = SOCKET_STATE_INIT;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
socket.state = SOCKET_STATE_CLOSED;
|
|
47
|
+
}
|
|
41
48
|
throw val;
|
|
42
49
|
}
|
|
43
50
|
else {
|
|
@@ -77,7 +84,7 @@ export function socketTcpBindStart(id, localAddress, family) {
|
|
|
77
84
|
reject("invalid-state");
|
|
78
85
|
return;
|
|
79
86
|
}
|
|
80
|
-
socket.localAddress = ipSocketAddress(boundAddress.family
|
|
87
|
+
socket.localAddress = ipSocketAddress(boundAddress.family, boundAddress.address, boundAddress.port);
|
|
81
88
|
resolve();
|
|
82
89
|
}
|
|
83
90
|
server.once("listening", handleListen);
|
|
@@ -236,7 +243,7 @@ export function socketTcpGetLocalAddress(id) {
|
|
|
236
243
|
const socket = tcpSockets.get(id);
|
|
237
244
|
const address = socket.tcpSocket?.address();
|
|
238
245
|
if (address && typeof address !== "string" && "family" in address) {
|
|
239
|
-
return ipSocketAddress(address.family
|
|
246
|
+
return ipSocketAddress(address.family, address.address, address.port);
|
|
240
247
|
}
|
|
241
248
|
if (socket.localAddress) {
|
|
242
249
|
return socket.localAddress;
|
|
@@ -248,19 +255,14 @@ export function socketTcpGetRemoteAddress(id) {
|
|
|
248
255
|
if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) {
|
|
249
256
|
throw "invalid-state";
|
|
250
257
|
}
|
|
251
|
-
return ipSocketAddress(tcpSocket.remoteFamily
|
|
258
|
+
return ipSocketAddress(tcpSocket.remoteFamily, tcpSocket.remoteAddress, tcpSocket.remotePort);
|
|
252
259
|
}
|
|
253
260
|
export function socketTcpShutdown(id, _shutdownType) {
|
|
254
261
|
const socket = tcpSockets.get(id);
|
|
255
262
|
if (socket.state !== SOCKET_STATE_CONNECTION) {
|
|
256
263
|
throw "invalid-state";
|
|
257
264
|
}
|
|
258
|
-
|
|
259
|
-
socket.tcpSocket.destroySoon();
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
socket.tcpSocket?.destroy();
|
|
263
|
-
}
|
|
265
|
+
socket.tcpSocket?.destroy();
|
|
264
266
|
}
|
|
265
267
|
export function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
|
|
266
268
|
const socket = tcpSockets.get(id);
|
|
@@ -61,10 +61,9 @@ function createIncomingDatagramStream(socket) {
|
|
|
61
61
|
udpSocket.off("error", onError);
|
|
62
62
|
}
|
|
63
63
|
function onMessage(data, rinfo) {
|
|
64
|
-
const family = rinfo.family.toLowerCase();
|
|
65
64
|
datagramStream.queue?.push({
|
|
66
65
|
data,
|
|
67
|
-
remoteAddress: ipSocketAddress(family, rinfo.address, rinfo.port),
|
|
66
|
+
remoteAddress: ipSocketAddress(rinfo.family, rinfo.address, rinfo.port),
|
|
68
67
|
});
|
|
69
68
|
if (!pollState.ready) {
|
|
70
69
|
pollStateReady(pollState);
|
|
@@ -168,7 +167,7 @@ export function socketUdpGetLocalAddress(id) {
|
|
|
168
167
|
catch (err) {
|
|
169
168
|
throw convertSocketError(err);
|
|
170
169
|
}
|
|
171
|
-
return ipSocketAddress(family
|
|
170
|
+
return ipSocketAddress(family, address, port);
|
|
172
171
|
}
|
|
173
172
|
/**
|
|
174
173
|
* @param {number} id
|
|
@@ -183,7 +182,7 @@ export function socketUdpGetRemoteAddress(id) {
|
|
|
183
182
|
catch (err) {
|
|
184
183
|
throw convertSocketError(err);
|
|
185
184
|
}
|
|
186
|
-
return ipSocketAddress(family
|
|
185
|
+
return ipSocketAddress(family, address, port);
|
|
187
186
|
}
|
|
188
187
|
export function socketUdpStream(id, remoteAddress) {
|
|
189
188
|
const socket = udpSockets.get(id);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IpSocketAddress, Ipv4Address, Ipv6Address } from "../../types/interfaces/wasi-sockets-network.js";
|
|
2
2
|
export declare const SOCKET_STATE_INIT: number;
|
|
3
3
|
export declare const SOCKET_STATE_BIND: number;
|
|
4
4
|
export declare const SOCKET_STATE_BOUND: number;
|
|
@@ -15,8 +15,8 @@ export declare function socketResolveAddress(name: string): Promise<{
|
|
|
15
15
|
tag: string;
|
|
16
16
|
val: Ipv4Address | Ipv6Address;
|
|
17
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";
|
|
18
|
+
export declare function convertSocketError(err: any): "access-denied" | "address-in-use" | "address-not-bindable" | "concurrency-conflict" | "connection-aborted" | "connection-refused" | "connection-reset" | "datagram-too-large" | "invalid-argument" | "invalid-state" | "new-socket-limit" | "not-supported" | "out-of-memory" | "remote-unreachable" | "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" | "datagram-too-large" | "invalid-argument" | "invalid-state" | "new-socket-limit" | "not-supported" | "out-of-memory" | "remote-unreachable" | "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;
|
|
@@ -27,6 +27,6 @@ export declare function isBroadcastIpAddress(ipSocketAddress: IpSocketAddress):
|
|
|
27
27
|
export declare function serializeIpAddress(addr: IpSocketAddress): string | null;
|
|
28
28
|
export declare function ipv6ToTuple(ipv6: string): Ipv6Address;
|
|
29
29
|
export declare function ipv4ToTuple(ipv4: string): Ipv4Address;
|
|
30
|
-
export declare function ipSocketAddress(family:
|
|
30
|
+
export declare function ipSocketAddress(family: string | number, addr: string, port: number): IpSocketAddress;
|
|
31
31
|
export declare function getDefaultSendBufferSize(): Promise<bigint>;
|
|
32
32
|
export declare function getDefaultReceiveBufferSize(): Promise<bigint>;
|
|
@@ -2,7 +2,7 @@ import { isIP } from "node:net";
|
|
|
2
2
|
import { lookup } from "node:dns/promises";
|
|
3
3
|
import { createSocket } from "node:dgram";
|
|
4
4
|
import { ALL, BADFAMILY, CANCELLED, CONNREFUSED, NODATA, NOMEM, NONAME, NOTFOUND, REFUSED, SERVFAIL, TIMEOUT, V4MAPPED, } from "node:dns";
|
|
5
|
-
import { EACCES, EADDRINUSE, EADDRNOTAVAIL, EALREADY, EBADF, ECONNABORTED, ECONNREFUSED, ECONNRESET, EINVAL, ENOBUFS, ENOMEM, ENOTCONN, ENOTSUP, EPERM, EWOULDBLOCK, } from "node:constants";
|
|
5
|
+
import { EACCES, EADDRINUSE, EADDRNOTAVAIL, EALREADY, EBADF, ECONNABORTED, ECONNREFUSED, ECONNRESET, EHOSTUNREACH, EINVAL, EMFILE, EMSGSIZE, ENETDOWN, ENETUNREACH, ENFILE, ENOBUFS, ENOMEM, ENOTCONN, ENOTSUP, EPERM, EWOULDBLOCK, } from "node:constants";
|
|
6
6
|
let stateCnt = 0;
|
|
7
7
|
export const SOCKET_STATE_INIT = ++stateCnt;
|
|
8
8
|
export const SOCKET_STATE_BIND = ++stateCnt;
|
|
@@ -98,18 +98,26 @@ export function convertSocketError(err) {
|
|
|
98
98
|
return "concurrency-conflict";
|
|
99
99
|
case "EWOULDBLOCK":
|
|
100
100
|
return "would-block";
|
|
101
|
-
|
|
101
|
+
case "EMFILE":
|
|
102
|
+
case "ENFILE":
|
|
103
|
+
return "new-socket-limit";
|
|
102
104
|
case "EADDRNOTAVAIL":
|
|
103
105
|
return "address-not-bindable";
|
|
104
106
|
case "EADDRINUSE":
|
|
105
107
|
return "address-in-use";
|
|
106
|
-
|
|
108
|
+
case "EHOSTUNREACH":
|
|
109
|
+
case "EHOSTDOWN":
|
|
110
|
+
case "ENETUNREACH":
|
|
111
|
+
case "ENETDOWN":
|
|
112
|
+
return "remote-unreachable";
|
|
107
113
|
case "ECONNREFUSED":
|
|
108
114
|
return "connection-refused";
|
|
109
115
|
case "ECONNRESET":
|
|
110
116
|
return "connection-reset";
|
|
111
117
|
case "ECONNABORTED":
|
|
112
118
|
return "connection-aborted";
|
|
119
|
+
case "EMSGSIZE":
|
|
120
|
+
return "datagram-too-large";
|
|
113
121
|
default:
|
|
114
122
|
return "unknown";
|
|
115
123
|
}
|
|
@@ -135,23 +143,30 @@ export function convertSocketErrorCode(code) {
|
|
|
135
143
|
return "concurrency-conflict";
|
|
136
144
|
case EWOULDBLOCK:
|
|
137
145
|
return "would-block";
|
|
138
|
-
|
|
146
|
+
case EMFILE:
|
|
147
|
+
case ENFILE:
|
|
148
|
+
return "new-socket-limit";
|
|
139
149
|
case 4090: // windows
|
|
140
150
|
case EADDRNOTAVAIL:
|
|
141
151
|
return "address-not-bindable";
|
|
142
152
|
case 4091: // windows
|
|
143
153
|
case EADDRINUSE:
|
|
144
154
|
return "address-in-use";
|
|
145
|
-
|
|
155
|
+
case EHOSTUNREACH:
|
|
156
|
+
case ENETUNREACH:
|
|
157
|
+
case ENETDOWN:
|
|
158
|
+
return "remote-unreachable";
|
|
146
159
|
case ECONNREFUSED:
|
|
147
160
|
return "connection-refused";
|
|
148
161
|
case ECONNRESET:
|
|
149
162
|
return "connection-reset";
|
|
150
163
|
case ECONNABORTED:
|
|
151
164
|
return "connection-aborted";
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
//
|
|
165
|
+
case EMSGSIZE:
|
|
166
|
+
return "datagram-too-large";
|
|
167
|
+
// name-unresolvable/temporary-resolver-failure are DNS lookup errors,
|
|
168
|
+
// which only surface here as string error codes (see
|
|
169
|
+
// socketResolveAddress above), never as a raw platform errno.
|
|
155
170
|
default:
|
|
156
171
|
// process._rawDebug('unknown error code', code);
|
|
157
172
|
return "unknown";
|
|
@@ -219,7 +234,9 @@ export function ipv4ToTuple(ipv4) {
|
|
|
219
234
|
return ipv4.split(".").map((segment) => parseInt(segment, 10));
|
|
220
235
|
}
|
|
221
236
|
export function ipSocketAddress(family, addr, port) {
|
|
222
|
-
|
|
237
|
+
// Node uses "IPv4"/"IPv6", while Deno's Node compatibility layer can
|
|
238
|
+
// return 4/6. Accept both, as well as the WASI address-family tags.
|
|
239
|
+
if (family === "ipv4" || family === "IPv4" || family === 4) {
|
|
223
240
|
return {
|
|
224
241
|
tag: "ipv4",
|
|
225
242
|
val: {
|
|
@@ -228,6 +245,9 @@ export function ipSocketAddress(family, addr, port) {
|
|
|
228
245
|
},
|
|
229
246
|
};
|
|
230
247
|
}
|
|
248
|
+
if (family !== "ipv6" && family !== "IPv6" && family !== 6) {
|
|
249
|
+
throw "invalid-argument";
|
|
250
|
+
}
|
|
231
251
|
return {
|
|
232
252
|
tag: "ipv6",
|
|
233
253
|
val: {
|