@bytecodealliance/preview3-shim 0.6.0 → 0.7.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 +9 -2
- package/dist/browser/cli/environment.d.ts +1 -0
- package/dist/browser/cli/environment.js +9 -4
- package/dist/browser/cli/exit.js +4 -3
- package/dist/browser/cli/run.d.ts +2 -0
- package/dist/browser/cli/run.js +11 -3
- package/dist/browser/cli/stderr.js +10 -3
- package/dist/browser/cli/stdin.js +3 -2
- package/dist/browser/cli/stdout.js +10 -3
- package/dist/browser/cli/terminal-stderr.js +2 -2
- package/dist/browser/cli/terminal-stdin.js +2 -2
- package/dist/browser/cli/terminal-stdout.js +2 -2
- package/dist/browser/cli.d.ts +3 -0
- package/dist/browser/cli.js +3 -0
- package/dist/browser/clocks/monotonic-clock.js +7 -7
- package/dist/browser/clocks/system-clock.js +4 -3
- package/dist/browser/filesystem/preopens.js +3 -2
- package/dist/browser/filesystem/types.d.ts +44 -1
- package/dist/browser/filesystem/types.js +139 -50
- package/dist/browser/filesystem.d.ts +13 -0
- package/dist/browser/filesystem.js +20 -0
- package/dist/browser/http/client.d.ts +2 -2
- package/dist/browser/http/client.js +138 -3
- package/dist/browser/http/handler.d.ts +4 -0
- package/dist/browser/http/handler.js +11 -3
- package/dist/browser/http/types.d.ts +44 -22
- package/dist/browser/http/types.js +192 -50
- package/dist/browser/http.d.ts +3 -0
- package/dist/browser/http.js +3 -0
- package/dist/browser/sockets/ip-name-lookup.js +34 -3
- package/dist/browser/sockets/types.d.ts +15 -0
- package/dist/browser/sockets/types.js +241 -49
- package/dist/browser/sockets.d.ts +2 -0
- package/dist/browser/sockets.js +2 -0
- package/dist/browser/streams.d.ts +20 -0
- package/dist/browser/streams.js +106 -0
- package/dist/nodejs/workers/tcp-worker.bundle.js +34 -6
- package/dist/nodejs/workers/tcp-worker.js +45 -15
- package/package.json +5 -5
- package/dist/common/errors.d.ts +0 -3
- package/dist/common/errors.js +0 -5
|
@@ -1,126 +1,318 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as preview2Sockets from "@bytecodealliance/preview2-shim/sockets";
|
|
2
|
+
import { readableFromPreview2Input, writeToPreview2Output } from "../streams.js";
|
|
3
|
+
let tcpProvider = preview2Sockets;
|
|
4
|
+
let udpProvider = preview2Sockets;
|
|
5
|
+
class SocketError extends Error {
|
|
6
|
+
payload;
|
|
7
|
+
constructor(payload) {
|
|
8
|
+
super(payload.tag);
|
|
9
|
+
this.name = "SocketError";
|
|
10
|
+
this.payload = payload;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function socketErrorCode(error) {
|
|
14
|
+
let tag = typeof error === "string" ? error : error?.tag;
|
|
15
|
+
if (!tag && error instanceof Error) {
|
|
16
|
+
tag = error.message;
|
|
17
|
+
}
|
|
18
|
+
if (tag === "would-block" || tag === "not-in-progress") {
|
|
19
|
+
return { tag: "invalid-state" };
|
|
20
|
+
}
|
|
21
|
+
if (tag) {
|
|
22
|
+
return { tag };
|
|
23
|
+
}
|
|
24
|
+
return { tag: "other", val: String(error) };
|
|
25
|
+
}
|
|
26
|
+
function socketError(error) {
|
|
27
|
+
throw error instanceof SocketError ? error : new SocketError(socketErrorCode(error));
|
|
28
|
+
}
|
|
29
|
+
function socketCall(operation) {
|
|
30
|
+
try {
|
|
31
|
+
return operation();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
socketError(error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function _setTcpProvider(provider) {
|
|
38
|
+
const previous = tcpProvider;
|
|
39
|
+
tcpProvider = provider;
|
|
40
|
+
return previous;
|
|
41
|
+
}
|
|
42
|
+
export function _setUdpProvider(provider) {
|
|
43
|
+
const previous = udpProvider;
|
|
44
|
+
udpProvider = provider;
|
|
45
|
+
return previous;
|
|
46
|
+
}
|
|
2
47
|
export class TcpSocket {
|
|
48
|
+
#socket;
|
|
49
|
+
#input;
|
|
50
|
+
#output;
|
|
3
51
|
static create(addressFamily) {
|
|
4
|
-
|
|
52
|
+
try {
|
|
53
|
+
return TcpSocket._wrap(tcpProvider.tcpCreateSocket.createTcpSocket(addressFamily));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
socketError(error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
static _wrap(socket, input, output) {
|
|
60
|
+
const wrapped = new TcpSocket();
|
|
61
|
+
wrapped.#socket = socket;
|
|
62
|
+
wrapped.#input = input;
|
|
63
|
+
wrapped.#output = output;
|
|
64
|
+
return wrapped;
|
|
5
65
|
}
|
|
6
66
|
bind(localAddress) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
67
|
+
try {
|
|
68
|
+
this.#socket.startBind({}, localAddress);
|
|
69
|
+
this.#socket.finishBind();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
socketError(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async connect(remoteAddress) {
|
|
76
|
+
try {
|
|
77
|
+
this.#socket.startConnect({}, remoteAddress);
|
|
78
|
+
const connected = this.#socket.finishConnect();
|
|
79
|
+
if (Array.isArray(connected)) {
|
|
80
|
+
[this.#input, this.#output] = connected;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
socketError(error);
|
|
85
|
+
}
|
|
11
86
|
}
|
|
12
87
|
listen() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
88
|
+
try {
|
|
89
|
+
this.#socket.startListen();
|
|
90
|
+
this.#socket.finishListen();
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
socketError(error);
|
|
94
|
+
}
|
|
95
|
+
return new ReadableStream({
|
|
96
|
+
pull: async (controller) => {
|
|
97
|
+
while (true) {
|
|
98
|
+
try {
|
|
99
|
+
const [socket, input, output] = this.#socket.accept();
|
|
100
|
+
controller.enqueue(TcpSocket._wrap(socket, input, output));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error === "would-block") {
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
controller.error(new SocketError(socketErrorCode(error)));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async send(data) {
|
|
116
|
+
if (!this.#output) {
|
|
117
|
+
return { tag: "err", val: { tag: "invalid-state" } };
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await writeToPreview2Output(data, this.#output);
|
|
121
|
+
return { tag: "ok", val: undefined };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return { tag: "err", val: socketErrorCode(error) };
|
|
125
|
+
}
|
|
17
126
|
}
|
|
18
127
|
receive() {
|
|
19
|
-
|
|
128
|
+
if (!this.#input) {
|
|
129
|
+
const error = { tag: "invalid-state" };
|
|
130
|
+
const stream = new ReadableStream({
|
|
131
|
+
start(controller) {
|
|
132
|
+
controller.error(new SocketError(error));
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
return [stream, Promise.resolve({ tag: "err", val: error })];
|
|
136
|
+
}
|
|
137
|
+
return readableFromPreview2Input(this.#input, socketErrorCode);
|
|
20
138
|
}
|
|
21
139
|
getLocalAddress() {
|
|
22
|
-
|
|
140
|
+
try {
|
|
141
|
+
return this.#socket.localAddress();
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
socketError(error);
|
|
145
|
+
}
|
|
23
146
|
}
|
|
24
147
|
getRemoteAddress() {
|
|
25
|
-
|
|
148
|
+
try {
|
|
149
|
+
return this.#socket.remoteAddress();
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
socketError(error);
|
|
153
|
+
}
|
|
26
154
|
}
|
|
27
155
|
getIsListening() {
|
|
28
|
-
|
|
156
|
+
return socketCall(() => this.#socket.isListening());
|
|
29
157
|
}
|
|
30
158
|
getAddressFamily() {
|
|
31
|
-
|
|
159
|
+
return socketCall(() => this.#socket.addressFamily());
|
|
32
160
|
}
|
|
33
161
|
setListenBacklogSize(value) {
|
|
34
|
-
|
|
162
|
+
socketCall(() => this.#socket.setListenBacklogSize(value));
|
|
35
163
|
}
|
|
36
164
|
getKeepAliveEnabled() {
|
|
37
|
-
|
|
165
|
+
return socketCall(() => this.#socket.keepAliveEnabled());
|
|
38
166
|
}
|
|
39
167
|
setKeepAliveEnabled(value) {
|
|
40
|
-
|
|
168
|
+
socketCall(() => this.#socket.setKeepAliveEnabled(value));
|
|
41
169
|
}
|
|
42
170
|
getKeepAliveIdleTime() {
|
|
43
|
-
|
|
171
|
+
return socketCall(() => this.#socket.keepAliveIdleTime());
|
|
44
172
|
}
|
|
45
173
|
setKeepAliveIdleTime(value) {
|
|
46
|
-
|
|
174
|
+
socketCall(() => this.#socket.setKeepAliveIdleTime(value));
|
|
47
175
|
}
|
|
48
176
|
getKeepAliveInterval() {
|
|
49
|
-
|
|
177
|
+
return socketCall(() => this.#socket.keepAliveInterval());
|
|
50
178
|
}
|
|
51
179
|
setKeepAliveInterval(value) {
|
|
52
|
-
|
|
180
|
+
socketCall(() => this.#socket.setKeepAliveInterval(value));
|
|
53
181
|
}
|
|
54
182
|
getKeepAliveCount() {
|
|
55
|
-
|
|
183
|
+
return socketCall(() => this.#socket.keepAliveCount());
|
|
56
184
|
}
|
|
57
185
|
setKeepAliveCount(value) {
|
|
58
|
-
|
|
186
|
+
socketCall(() => this.#socket.setKeepAliveCount(value));
|
|
59
187
|
}
|
|
60
188
|
getHopLimit() {
|
|
61
|
-
|
|
189
|
+
return socketCall(() => this.#socket.hopLimit());
|
|
62
190
|
}
|
|
63
191
|
setHopLimit(value) {
|
|
64
|
-
|
|
192
|
+
socketCall(() => this.#socket.setHopLimit(value));
|
|
65
193
|
}
|
|
66
194
|
getReceiveBufferSize() {
|
|
67
|
-
|
|
195
|
+
return socketCall(() => this.#socket.receiveBufferSize());
|
|
68
196
|
}
|
|
69
197
|
setReceiveBufferSize(value) {
|
|
70
|
-
|
|
198
|
+
socketCall(() => this.#socket.setReceiveBufferSize(value));
|
|
71
199
|
}
|
|
72
200
|
getSendBufferSize() {
|
|
73
|
-
|
|
201
|
+
return socketCall(() => this.#socket.sendBufferSize());
|
|
74
202
|
}
|
|
75
203
|
setSendBufferSize(value) {
|
|
76
|
-
|
|
204
|
+
socketCall(() => this.#socket.setSendBufferSize(value));
|
|
77
205
|
}
|
|
78
206
|
}
|
|
79
207
|
export class UdpSocket {
|
|
208
|
+
#socket;
|
|
209
|
+
#incoming;
|
|
210
|
+
#outgoing;
|
|
80
211
|
static create(addressFamily) {
|
|
81
|
-
|
|
212
|
+
try {
|
|
213
|
+
const wrapped = new UdpSocket();
|
|
214
|
+
wrapped.#socket = udpProvider.udpCreateSocket.createUdpSocket(addressFamily);
|
|
215
|
+
return wrapped;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
socketError(error);
|
|
219
|
+
}
|
|
82
220
|
}
|
|
83
221
|
bind(localAddress) {
|
|
84
|
-
|
|
222
|
+
try {
|
|
223
|
+
this.#socket.startBind({}, localAddress);
|
|
224
|
+
this.#socket.finishBind();
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
socketError(error);
|
|
228
|
+
}
|
|
85
229
|
}
|
|
86
230
|
connect(remoteAddress) {
|
|
87
|
-
|
|
231
|
+
try {
|
|
232
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(remoteAddress);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
socketError(error);
|
|
236
|
+
}
|
|
88
237
|
}
|
|
89
238
|
disconnect() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
239
|
+
try {
|
|
240
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(undefined);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
socketError(error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async send(data, remoteAddress) {
|
|
247
|
+
try {
|
|
248
|
+
if (!this.#outgoing) {
|
|
249
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(remoteAddress);
|
|
250
|
+
}
|
|
251
|
+
this.#outgoing.checkSend();
|
|
252
|
+
this.#outgoing.send([{ data, remoteAddress }]);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
socketError(error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async receive() {
|
|
259
|
+
if (!this.#incoming) {
|
|
260
|
+
try {
|
|
261
|
+
[this.#incoming, this.#outgoing] = this.#socket.stream(undefined);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
socketError(error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
while (true) {
|
|
268
|
+
try {
|
|
269
|
+
const [datagram] = this.#incoming.receive(1n);
|
|
270
|
+
if (datagram) {
|
|
271
|
+
return [datagram.data, datagram.remoteAddress];
|
|
272
|
+
}
|
|
273
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
socketError(error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
97
279
|
}
|
|
98
280
|
getLocalAddress() {
|
|
99
|
-
|
|
281
|
+
try {
|
|
282
|
+
return this.#socket.localAddress();
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
socketError(error);
|
|
286
|
+
}
|
|
100
287
|
}
|
|
101
288
|
getRemoteAddress() {
|
|
102
|
-
|
|
289
|
+
try {
|
|
290
|
+
return this.#socket.remoteAddress();
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
socketError(error);
|
|
294
|
+
}
|
|
103
295
|
}
|
|
104
296
|
getAddressFamily() {
|
|
105
|
-
|
|
297
|
+
return socketCall(() => this.#socket.addressFamily());
|
|
106
298
|
}
|
|
107
299
|
getUnicastHopLimit() {
|
|
108
|
-
|
|
300
|
+
return socketCall(() => this.#socket.unicastHopLimit());
|
|
109
301
|
}
|
|
110
302
|
setUnicastHopLimit(value) {
|
|
111
|
-
|
|
303
|
+
socketCall(() => this.#socket.setUnicastHopLimit(value));
|
|
112
304
|
}
|
|
113
305
|
getReceiveBufferSize() {
|
|
114
|
-
|
|
306
|
+
return socketCall(() => this.#socket.receiveBufferSize());
|
|
115
307
|
}
|
|
116
308
|
setReceiveBufferSize(value) {
|
|
117
|
-
|
|
309
|
+
socketCall(() => this.#socket.setReceiveBufferSize(value));
|
|
118
310
|
}
|
|
119
311
|
getSendBufferSize() {
|
|
120
|
-
|
|
312
|
+
return socketCall(() => this.#socket.sendBufferSize());
|
|
121
313
|
}
|
|
122
314
|
setSendBufferSize(value) {
|
|
123
|
-
|
|
315
|
+
socketCall(() => this.#socket.setSendBufferSize(value));
|
|
124
316
|
}
|
|
125
317
|
}
|
|
126
318
|
export default {
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as ipNameLookup } from "./sockets/ip-name-lookup.js";
|
|
2
2
|
export { default as types } from "./sockets/types.js";
|
|
3
|
+
export { _setTcpProvider, _setUdpProvider } from "./sockets/types.js";
|
|
4
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "@bytecodealliance/preview2-shim/sockets";
|
package/dist/browser/sockets.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as ipNameLookup } from "./sockets/ip-name-lookup.js";
|
|
2
2
|
export { default as types } from "./sockets/types.js";
|
|
3
|
+
export { _setTcpProvider, _setUdpProvider } from "./sockets/types.js";
|
|
4
|
+
export { InMemoryTcpClient, InMemoryTcpSockets, InMemoryUdpClient, InMemoryUdpSockets, } from "@bytecodealliance/preview2-shim/sockets";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface Preview2InputStream {
|
|
2
|
+
blockingRead(length: bigint): Uint8Array | Promise<Uint8Array>;
|
|
3
|
+
subscribe(): {
|
|
4
|
+
block(): void | Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
interface Preview2OutputStream {
|
|
8
|
+
blockingWriteAndFlush(contents: Uint8Array): void;
|
|
9
|
+
blockingFlush(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function writeToPreview2Output(data: ReadableStream<number>, output: Preview2OutputStream): Promise<void>;
|
|
12
|
+
export declare function readableFromPreview2Input<E>(input: Preview2InputStream, errorCode: (error: unknown) => E): [ReadableStream<number>, Promise<{
|
|
13
|
+
tag: "ok";
|
|
14
|
+
val: void;
|
|
15
|
+
} | {
|
|
16
|
+
tag: "err";
|
|
17
|
+
val: E;
|
|
18
|
+
}>];
|
|
19
|
+
export declare function preview2StreamErrorCode(error: unknown): "io" | "illegal-byte-sequence" | "pipe";
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const symbolDispose = Symbol.dispose ?? Symbol.for("dispose");
|
|
2
|
+
const BYTE_CHUNK_SIZE = 64 * 1024;
|
|
3
|
+
const P2_WRITE_CHUNK_SIZE = 4096;
|
|
4
|
+
function byteChunk(value) {
|
|
5
|
+
if (value instanceof Uint8Array) {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
if (value instanceof ArrayBuffer) {
|
|
9
|
+
return new Uint8Array(value);
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return Uint8Array.from(value);
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "number") {
|
|
15
|
+
if (!Number.isInteger(value) || value < 0 || value > 255) {
|
|
16
|
+
throw new RangeError(`invalid byte stream value: ${value}`);
|
|
17
|
+
}
|
|
18
|
+
return Uint8Array.of(value);
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
return new TextEncoder().encode(value);
|
|
22
|
+
}
|
|
23
|
+
throw new TypeError("byte stream values must be bytes or byte arrays");
|
|
24
|
+
}
|
|
25
|
+
function isIteratorResult(value) {
|
|
26
|
+
return value !== null && typeof value === "object" && "done" in value;
|
|
27
|
+
}
|
|
28
|
+
async function* byteChunks(data) {
|
|
29
|
+
const stream = data;
|
|
30
|
+
if (typeof stream.read === "function") {
|
|
31
|
+
while (true) {
|
|
32
|
+
const value = await stream.read({ count: BYTE_CHUNK_SIZE });
|
|
33
|
+
if (isIteratorResult(value)) {
|
|
34
|
+
if (value.done) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
yield byteChunk(value.value);
|
|
38
|
+
}
|
|
39
|
+
else if (value === null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
yield byteChunk(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (typeof stream[Symbol.asyncIterator] === "function") {
|
|
48
|
+
for await (const value of stream) {
|
|
49
|
+
yield byteChunk(value);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw new TypeError("byte stream must provide read() or [Symbol.asyncIterator]()");
|
|
54
|
+
}
|
|
55
|
+
export async function writeToPreview2Output(data, output) {
|
|
56
|
+
for await (const bytes of byteChunks(data)) {
|
|
57
|
+
for (let offset = 0; offset < bytes.byteLength; offset += P2_WRITE_CHUNK_SIZE) {
|
|
58
|
+
output.blockingWriteAndFlush(bytes.subarray(offset, offset + P2_WRITE_CHUNK_SIZE));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
output.blockingFlush();
|
|
62
|
+
}
|
|
63
|
+
export function readableFromPreview2Input(input, errorCode) {
|
|
64
|
+
let resolveResult = () => { };
|
|
65
|
+
const result = new Promise((resolve) => (resolveResult = resolve));
|
|
66
|
+
let settled = false;
|
|
67
|
+
const settle = (value) => {
|
|
68
|
+
if (!settled) {
|
|
69
|
+
settled = true;
|
|
70
|
+
resolveResult(value);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const readable = new ReadableStream({
|
|
74
|
+
async pull(controller) {
|
|
75
|
+
try {
|
|
76
|
+
const bytes = await input.blockingRead(BigInt(BYTE_CHUNK_SIZE));
|
|
77
|
+
for (const byte of bytes) {
|
|
78
|
+
controller.enqueue(byte);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error?.tag === "closed") {
|
|
83
|
+
controller.close();
|
|
84
|
+
settle({ tag: "ok", val: undefined });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
settle({ tag: "err", val: errorCode(error) });
|
|
88
|
+
controller.error(error);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
cancel() {
|
|
92
|
+
settle({ tag: "ok", val: undefined });
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return [readable, result];
|
|
96
|
+
}
|
|
97
|
+
export function preview2StreamErrorCode(error) {
|
|
98
|
+
const code = error?.code;
|
|
99
|
+
if (code === "EPIPE" || code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
100
|
+
return "pipe";
|
|
101
|
+
}
|
|
102
|
+
if (code === "ERR_ENCODING_INVALID_ENCODED_DATA") {
|
|
103
|
+
return "illegal-byte-sequence";
|
|
104
|
+
}
|
|
105
|
+
return "io";
|
|
106
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Server, Socket } from "node:net";
|
|
1
|
+
import net, { Server, Socket } from "node:net";
|
|
2
2
|
import { once } from "node:events";
|
|
3
3
|
import { isMainThread, parentPort } from "worker_threads";
|
|
4
4
|
//#region dist/nodejs/workers/resource-worker.js
|
|
@@ -252,6 +252,7 @@ var SocketError = class SocketError extends Error {
|
|
|
252
252
|
};
|
|
253
253
|
//#endregion
|
|
254
254
|
//#region dist/nodejs/workers/tcp-worker.js
|
|
255
|
+
const BoundSocket = net.BoundSocket;
|
|
255
256
|
const sockets = /* @__PURE__ */ new Map();
|
|
256
257
|
let NEXT_SOCKET_ID = 0n;
|
|
257
258
|
Router().beforeAll((msg) => {
|
|
@@ -263,6 +264,7 @@ function handleTcpCreate({ family }) {
|
|
|
263
264
|
family,
|
|
264
265
|
tcp: null,
|
|
265
266
|
server: null,
|
|
267
|
+
bound: null,
|
|
266
268
|
acceptWriter: null,
|
|
267
269
|
backlog: 128,
|
|
268
270
|
localAddress: null,
|
|
@@ -283,6 +285,17 @@ async function handleTcpBind({ socketId, localAddress }) {
|
|
|
283
285
|
err.code = "EADDRINUSE";
|
|
284
286
|
throw err;
|
|
285
287
|
}
|
|
288
|
+
if (BoundSocket) {
|
|
289
|
+
const bound = new BoundSocket({
|
|
290
|
+
host: address,
|
|
291
|
+
port,
|
|
292
|
+
ipv6Only: family === "ipv6"
|
|
293
|
+
});
|
|
294
|
+
const boundAddress = bound.address();
|
|
295
|
+
socket.bound = bound;
|
|
296
|
+
socket.localAddress = makeIpAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
286
299
|
const server = socket.server = createTcpServer(socket);
|
|
287
300
|
const onListening = once(server, "listening");
|
|
288
301
|
server.listen({
|
|
@@ -300,16 +313,20 @@ async function handleTcpConnect({ socketId, remoteAddress }) {
|
|
|
300
313
|
const socket = sockets.get(socketId);
|
|
301
314
|
const host = serializeIpAddress(remoteAddress);
|
|
302
315
|
const port = remoteAddress.val.port;
|
|
303
|
-
const localAddress = socket
|
|
316
|
+
const { bound, localAddress } = socket;
|
|
317
|
+
socket.bound = null;
|
|
304
318
|
await closeTcpServer(socket);
|
|
305
|
-
const tcp = socket.tcp = new Socket({
|
|
319
|
+
const tcp = socket.tcp = new Socket({
|
|
320
|
+
handle: bound ?? void 0,
|
|
321
|
+
allowHalfOpen: true
|
|
322
|
+
});
|
|
306
323
|
tcp.setKeepAlive(socket.keepAliveEnabled, socket.keepAliveIdleTime);
|
|
307
324
|
const onConnect = once(tcp, "connect");
|
|
308
325
|
tcp.connect({
|
|
309
326
|
port,
|
|
310
327
|
host,
|
|
311
|
-
localAddress: localAddress ? serializeIpAddress(localAddress) : void 0,
|
|
312
|
-
localPort: localAddress?.val.port
|
|
328
|
+
localAddress: !bound && localAddress ? serializeIpAddress(localAddress) : void 0,
|
|
329
|
+
localPort: !bound ? localAddress?.val.port : void 0
|
|
313
330
|
});
|
|
314
331
|
await onConnect;
|
|
315
332
|
}
|
|
@@ -322,7 +339,13 @@ async function handleTcpListen({ socketId, stream }) {
|
|
|
322
339
|
if (!server) {
|
|
323
340
|
server = socket.server = createTcpServer(socket);
|
|
324
341
|
const onListening = once(server, "listening");
|
|
325
|
-
|
|
342
|
+
const bound = socket.bound;
|
|
343
|
+
socket.bound = null;
|
|
344
|
+
if (bound) server.listen({
|
|
345
|
+
handle: bound,
|
|
346
|
+
backlog
|
|
347
|
+
});
|
|
348
|
+
else server.listen({
|
|
326
349
|
host: family === "ipv6" ? "::" : "0.0.0.0",
|
|
327
350
|
port: 0,
|
|
328
351
|
backlog,
|
|
@@ -352,6 +375,7 @@ function createTcpServer(socket) {
|
|
|
352
375
|
backlog: socket.backlog,
|
|
353
376
|
tcp: conn,
|
|
354
377
|
server: null,
|
|
378
|
+
bound: null,
|
|
355
379
|
acceptWriter: null,
|
|
356
380
|
localAddress: makeIpAddress(socket.family, conn.localAddress, conn.localPort),
|
|
357
381
|
keepAliveEnabled: socket.keepAliveEnabled,
|
|
@@ -466,6 +490,10 @@ async function handleTcpReceive({ socketId, stream }) {
|
|
|
466
490
|
}
|
|
467
491
|
function cleanupDisposedSocket(socketId, socket) {
|
|
468
492
|
if (!socket.disposed || socket.activeStreams > 0) return;
|
|
493
|
+
if (socket.bound) {
|
|
494
|
+
socket.bound.close();
|
|
495
|
+
socket.bound = null;
|
|
496
|
+
}
|
|
469
497
|
if (socket.server) socket.server.close();
|
|
470
498
|
if (socket.tcp) socket.tcp.destroy();
|
|
471
499
|
sockets.delete(socketId);
|