@bytecodealliance/preview2-shim 0.14.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/sockets.js +0 -14
- package/lib/io/calls.js +72 -135
- package/lib/io/worker-http.js +21 -18
- package/lib/io/worker-io.js +186 -44
- package/lib/io/worker-socket-tcp.js +250 -96
- package/lib/io/worker-socket-udp.js +524 -167
- package/lib/io/worker-sockets.js +371 -0
- package/lib/io/worker-thread.js +677 -489
- package/lib/nodejs/cli.js +3 -3
- package/lib/nodejs/clocks.js +9 -6
- package/lib/nodejs/filesystem.js +87 -25
- package/lib/nodejs/http.js +106 -96
- package/lib/nodejs/index.js +0 -2
- package/lib/nodejs/sockets.js +563 -17
- package/lib/synckit/index.js +4 -3
- package/package.json +2 -2
- package/lib/common/assert.js +0 -7
- package/lib/nodejs/sockets/socket-common.js +0 -129
- package/lib/nodejs/sockets/socketopts-bindings.js +0 -94
- package/lib/nodejs/sockets/tcp-socket-impl.js +0 -885
- package/lib/nodejs/sockets/udp-socket-impl.js +0 -768
- package/lib/nodejs/sockets/wasi-sockets.js +0 -341
- package/lib/synckit/index.d.ts +0 -71
package/lib/nodejs/cli.js
CHANGED
|
@@ -51,8 +51,8 @@ export const exit = {
|
|
|
51
51
|
|
|
52
52
|
// Stdin is created as a FILE descriptor
|
|
53
53
|
let stdinStream;
|
|
54
|
-
let stdoutStream = outputStreamCreate(STDOUT,
|
|
55
|
-
let stderrStream = outputStreamCreate(STDERR,
|
|
54
|
+
let stdoutStream = outputStreamCreate(STDOUT, 1);
|
|
55
|
+
let stderrStream = outputStreamCreate(STDERR, 2);
|
|
56
56
|
|
|
57
57
|
export const stdin = {
|
|
58
58
|
InputStream,
|
|
@@ -60,7 +60,7 @@ export const stdin = {
|
|
|
60
60
|
if (!stdinStream)
|
|
61
61
|
stdinStream = inputStreamCreate(
|
|
62
62
|
STDIN,
|
|
63
|
-
ioCall(INPUT_STREAM_CREATE | STDIN, null, null)
|
|
63
|
+
ioCall(INPUT_STREAM_CREATE | STDIN, null, null),
|
|
64
64
|
);
|
|
65
65
|
return stdinStream;
|
|
66
66
|
},
|
package/lib/nodejs/clocks.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { ioCall, createPoll
|
|
2
|
-
import
|
|
1
|
+
import { ioCall, createPoll } from "../io/worker-io.js";
|
|
2
|
+
import {
|
|
3
|
+
CLOCKS_NOW,
|
|
4
|
+
CLOCKS_INSTANT_SUBSCRIBE,
|
|
5
|
+
CLOCKS_DURATION_SUBSCRIBE,
|
|
6
|
+
} from "../io/calls.js";
|
|
3
7
|
|
|
4
8
|
export const monotonicClock = {
|
|
5
9
|
resolution() {
|
|
6
10
|
return 1n;
|
|
7
11
|
},
|
|
8
12
|
now() {
|
|
9
|
-
return ioCall(
|
|
13
|
+
return ioCall(CLOCKS_NOW, null, null);
|
|
10
14
|
},
|
|
11
15
|
subscribeInstant(instant) {
|
|
12
|
-
return createPoll(
|
|
16
|
+
return createPoll(CLOCKS_INSTANT_SUBSCRIBE, null, instant);
|
|
13
17
|
},
|
|
14
18
|
subscribeDuration(duration) {
|
|
15
19
|
duration = BigInt(duration);
|
|
16
|
-
|
|
17
|
-
return createPoll(calls.CLOCKS_DURATION_SUBSCRIBE, null, duration);
|
|
20
|
+
return createPoll(CLOCKS_DURATION_SUBSCRIBE, null, duration);
|
|
18
21
|
},
|
|
19
22
|
};
|
|
20
23
|
|
package/lib/nodejs/filesystem.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
earlyDispose,
|
|
3
3
|
inputStreamCreate,
|
|
4
|
+
ioCall,
|
|
4
5
|
outputStreamCreate,
|
|
6
|
+
registerDispose,
|
|
5
7
|
} from "../io/worker-io.js";
|
|
6
8
|
import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from "../io/calls.js";
|
|
7
9
|
import { FILE } from "../io/calls.js";
|
|
8
|
-
// import { environment } from "./cli.js";
|
|
9
10
|
import {
|
|
10
11
|
closeSync,
|
|
11
12
|
constants,
|
|
@@ -35,6 +36,7 @@ import { platform } from "node:process";
|
|
|
35
36
|
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
36
37
|
|
|
37
38
|
const isWindows = platform === "win32";
|
|
39
|
+
const isMac = platform === "darwin";
|
|
38
40
|
|
|
39
41
|
const nsMagnitude = 1_000_000_000_000n;
|
|
40
42
|
function nsToDateTime(ns) {
|
|
@@ -54,16 +56,17 @@ function lookupType(obj) {
|
|
|
54
56
|
return "unknown";
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
// Note: This should implement per-segment semantics of openAt, but we cannot
|
|
58
|
-
// due to the lack of support for openat() in Node.js.
|
|
59
|
+
// Note: This should implement per-segment semantics of openAt, but we cannot
|
|
60
|
+
// currently due to the lack of support for openat() in Node.js.
|
|
59
61
|
// Tracking issue: https://github.com/libuv/libuv/issues/4167
|
|
62
|
+
|
|
60
63
|
/**
|
|
61
64
|
* @implements {DescriptorProps}
|
|
62
65
|
*/
|
|
63
|
-
let descriptorCnt = 3;
|
|
64
66
|
class Descriptor {
|
|
65
67
|
#hostPreopen;
|
|
66
68
|
#fd;
|
|
69
|
+
#finalizer;
|
|
67
70
|
#mode;
|
|
68
71
|
#fullPath;
|
|
69
72
|
|
|
@@ -83,14 +86,17 @@ class Descriptor {
|
|
|
83
86
|
static _create(fd, mode, fullPath) {
|
|
84
87
|
const descriptor = new Descriptor();
|
|
85
88
|
descriptor.#fd = fd;
|
|
89
|
+
descriptor.#finalizer = registerDispose(descriptor, null, fd, closeSync);
|
|
86
90
|
descriptor.#mode = mode;
|
|
87
91
|
descriptor.#fullPath = fullPath;
|
|
88
92
|
return descriptor;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
[symbolDispose]() {
|
|
96
|
+
if (this.#finalizer) {
|
|
97
|
+
earlyDispose(this.#finalizer);
|
|
98
|
+
this.#finalizer = null;
|
|
99
|
+
}
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
readViaStream(offset) {
|
|
@@ -125,6 +131,7 @@ class Descriptor {
|
|
|
125
131
|
try {
|
|
126
132
|
fdatasyncSync(this.#fd);
|
|
127
133
|
} catch (e) {
|
|
134
|
+
if (e.code === "EPERM") return;
|
|
128
135
|
throw convertFsError(e);
|
|
129
136
|
}
|
|
130
137
|
}
|
|
@@ -219,6 +226,7 @@ class Descriptor {
|
|
|
219
226
|
try {
|
|
220
227
|
fsyncSync(this.#fd);
|
|
221
228
|
} catch (e) {
|
|
229
|
+
if (e.code === "EPERM") return;
|
|
222
230
|
throw convertFsError(e);
|
|
223
231
|
}
|
|
224
232
|
}
|
|
@@ -327,10 +335,31 @@ class Descriptor {
|
|
|
327
335
|
if (descriptorFlags.fileIntegritySync) fsOpenFlags |= constants.O_SYNC;
|
|
328
336
|
if (descriptorFlags.dataIntegritySync) fsOpenFlags |= constants.O_DSYNC;
|
|
329
337
|
if (!pathFlags.symlinkFollow) fsOpenFlags |= constants.O_NOFOLLOW;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
|
|
338
|
+
if (descriptorFlags.requestedWriteSync || descriptorFlags.mutateDirectory)
|
|
339
|
+
throw "unsupported";
|
|
340
|
+
// Currently throw to match Wasmtime
|
|
341
|
+
if (descriptorFlags.fileIntegritySync || descriptorFlags.dataIntegritySync)
|
|
342
|
+
throw "unsupported";
|
|
343
|
+
if (isWindows) {
|
|
344
|
+
if (!pathFlags.symlinkFollow && !openFlags.create) {
|
|
345
|
+
let isSymlink = false;
|
|
346
|
+
try {
|
|
347
|
+
isSymlink = lstatSync(fullPath).isSymbolicLink();
|
|
348
|
+
} catch (e) {
|
|
349
|
+
//
|
|
350
|
+
}
|
|
351
|
+
if (isSymlink) throw openFlags.directory ? "not-directory" : "loop";
|
|
352
|
+
}
|
|
353
|
+
if (pathFlags.symlinkFollow && openFlags.directory) {
|
|
354
|
+
let isFile = false;
|
|
355
|
+
try {
|
|
356
|
+
isFile = !statSync(fullPath).isDirectory();
|
|
357
|
+
} catch (e) {
|
|
358
|
+
//
|
|
359
|
+
}
|
|
360
|
+
if (isFile) throw "not-directory";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
334
363
|
try {
|
|
335
364
|
const fd = openSync(fullPath, fsOpenFlags);
|
|
336
365
|
const descriptor = descriptorCreate(
|
|
@@ -339,13 +368,17 @@ class Descriptor {
|
|
|
339
368
|
fullPath,
|
|
340
369
|
preopenEntries
|
|
341
370
|
);
|
|
342
|
-
if (fullPath.endsWith("/")) {
|
|
371
|
+
if (fullPath.endsWith("/") && isWindows) {
|
|
343
372
|
// check if its a directory
|
|
344
|
-
if (
|
|
373
|
+
if (descriptor.getType() !== "directory") {
|
|
374
|
+
descriptor[symbolDispose]();
|
|
375
|
+
throw "not-directory";
|
|
376
|
+
}
|
|
345
377
|
}
|
|
346
378
|
return descriptor;
|
|
347
379
|
} catch (e) {
|
|
348
|
-
if (e.code === "ERR_INVALID_ARG_VALUE")
|
|
380
|
+
if (e.code === "ERR_INVALID_ARG_VALUE")
|
|
381
|
+
throw isWindows ? "no-entry" : "invalid";
|
|
349
382
|
throw convertFsError(e);
|
|
350
383
|
}
|
|
351
384
|
}
|
|
@@ -386,8 +419,18 @@ class Descriptor {
|
|
|
386
419
|
try {
|
|
387
420
|
symlinkSync(target, fullPath);
|
|
388
421
|
} catch (e) {
|
|
389
|
-
if (
|
|
390
|
-
|
|
422
|
+
if (fullPath.endsWith("/") && e.code === "EEXIST") {
|
|
423
|
+
let isDir = false;
|
|
424
|
+
try {
|
|
425
|
+
isDir = statSync(fullPath).isDirectory();
|
|
426
|
+
} catch (_) {
|
|
427
|
+
//
|
|
428
|
+
}
|
|
429
|
+
if (!isDir) throw isWindows ? "no-entry" : "not-directory";
|
|
430
|
+
}
|
|
431
|
+
if (isWindows) {
|
|
432
|
+
if (e.code === "EPERM" || e.code === "EEXIST") throw "no-entry";
|
|
433
|
+
}
|
|
391
434
|
throw convertFsError(e);
|
|
392
435
|
}
|
|
393
436
|
}
|
|
@@ -395,8 +438,18 @@ class Descriptor {
|
|
|
395
438
|
unlinkFileAt(path) {
|
|
396
439
|
const fullPath = this.#getFullPath(path, false);
|
|
397
440
|
try {
|
|
441
|
+
if (fullPath.endsWith("/")) {
|
|
442
|
+
let isDir = false;
|
|
443
|
+
try {
|
|
444
|
+
isDir = statSync(fullPath).isDirectory();
|
|
445
|
+
} catch (e) {
|
|
446
|
+
//
|
|
447
|
+
}
|
|
448
|
+
throw isDir ? (isWindows ? "access" : (isMac ? "not-permitted" : "is-directory")) : "not-directory";
|
|
449
|
+
}
|
|
398
450
|
unlinkSync(fullPath);
|
|
399
451
|
} catch (e) {
|
|
452
|
+
if (isWindows && e.code === "EPERM") throw "access";
|
|
400
453
|
throw convertFsError(e);
|
|
401
454
|
}
|
|
402
455
|
}
|
|
@@ -484,10 +537,6 @@ class Descriptor {
|
|
|
484
537
|
);
|
|
485
538
|
return descriptor.#fullPath + (subpath.length > 0 ? "/" : "") + subpath;
|
|
486
539
|
}
|
|
487
|
-
|
|
488
|
-
[symbolDispose]() {
|
|
489
|
-
if (this.#fd) closeSync(this.#fd);
|
|
490
|
-
}
|
|
491
540
|
}
|
|
492
541
|
const descriptorCreatePreopen = Descriptor._createPreopen;
|
|
493
542
|
delete Descriptor._createPreopen;
|
|
@@ -496,6 +545,7 @@ delete Descriptor._create;
|
|
|
496
545
|
|
|
497
546
|
class DirectoryEntryStream {
|
|
498
547
|
#dir;
|
|
548
|
+
#finalizer;
|
|
499
549
|
readDirectoryEntry() {
|
|
500
550
|
let entry;
|
|
501
551
|
try {
|
|
@@ -510,15 +560,23 @@ class DirectoryEntryStream {
|
|
|
510
560
|
const type = lookupType(entry);
|
|
511
561
|
return { name, type };
|
|
512
562
|
}
|
|
513
|
-
[symbolDispose]() {
|
|
514
|
-
this.#dir.closeSync();
|
|
515
|
-
}
|
|
516
|
-
|
|
517
563
|
static _create(dir) {
|
|
518
564
|
const dirStream = new DirectoryEntryStream();
|
|
565
|
+
dirStream.#finalizer = registerDispose(
|
|
566
|
+
dirStream,
|
|
567
|
+
null,
|
|
568
|
+
null,
|
|
569
|
+
dir.closeSync.bind(dir)
|
|
570
|
+
);
|
|
519
571
|
dirStream.#dir = dir;
|
|
520
572
|
return dirStream;
|
|
521
573
|
}
|
|
574
|
+
[symbolDispose]() {
|
|
575
|
+
if (this.#finalizer) {
|
|
576
|
+
earlyDispose(this.#finalizer);
|
|
577
|
+
this.#finalizer = null;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
522
580
|
}
|
|
523
581
|
const directoryEntryStreamCreate = DirectoryEntryStream._create;
|
|
524
582
|
delete DirectoryEntryStream._create;
|
|
@@ -615,6 +673,10 @@ function convertFsError(e) {
|
|
|
615
673
|
return "unsupported";
|
|
616
674
|
case "ENOTTY":
|
|
617
675
|
return "no-tty";
|
|
676
|
+
// windows gives this error for badly structured `//` reads
|
|
677
|
+
// this seems like a slightly better error than unknown given
|
|
678
|
+
// that it's a common footgun
|
|
679
|
+
case -4094:
|
|
618
680
|
case "ENXIO":
|
|
619
681
|
return "no-such-device";
|
|
620
682
|
case "EOVERFLOW":
|
package/lib/nodejs/http.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
FUTURE_DISPOSE,
|
|
3
|
+
FUTURE_SUBSCRIBE,
|
|
4
|
+
FUTURE_TAKE_VALUE,
|
|
3
5
|
HTTP_CREATE_REQUEST,
|
|
6
|
+
HTTP_OUTGOING_BODY_DISPOSE,
|
|
4
7
|
HTTP_OUTPUT_STREAM_FINISH,
|
|
8
|
+
HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
|
|
9
|
+
HTTP_SERVER_SET_OUTGOING_RESPONSE,
|
|
5
10
|
HTTP_SERVER_START,
|
|
6
11
|
HTTP_SERVER_STOP,
|
|
7
12
|
OUTPUT_STREAM_CREATE,
|
|
8
|
-
|
|
9
|
-
FUTURE_DISPOSE,
|
|
10
|
-
HTTP_SERVER_SET_OUTGOING_RESPONSE,
|
|
11
|
-
HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
|
|
13
|
+
OUTPUT_STREAM_DISPOSE,
|
|
12
14
|
} from "../io/calls.js";
|
|
13
15
|
import {
|
|
14
|
-
|
|
15
|
-
pollableCreate,
|
|
16
|
+
earlyDispose,
|
|
16
17
|
inputStreamCreate,
|
|
18
|
+
ioCall,
|
|
17
19
|
outputStreamCreate,
|
|
20
|
+
pollableCreate,
|
|
21
|
+
registerDispose,
|
|
18
22
|
registerIncomingHttpHandler,
|
|
19
23
|
} from "../io/worker-io.js";
|
|
20
24
|
import { validateHeaderName, validateHeaderValue } from "node:http";
|
|
@@ -23,19 +27,14 @@ import { HTTP } from "../io/calls.js";
|
|
|
23
27
|
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
24
28
|
export const _forbiddenHeaders = new Set(["connection", "keep-alive"]);
|
|
25
29
|
|
|
26
|
-
let requestCnt = 1;
|
|
27
|
-
let responseCnt = 1;
|
|
28
|
-
let fieldsCnt = 1;
|
|
29
|
-
let futureCnt = 1;
|
|
30
|
-
|
|
31
30
|
class IncomingBody {
|
|
32
31
|
#finished = false;
|
|
33
|
-
#
|
|
34
|
-
#streamId = undefined;
|
|
32
|
+
#stream = undefined;
|
|
35
33
|
stream() {
|
|
36
|
-
if (this.#
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
if (!this.#stream) throw undefined;
|
|
35
|
+
const stream = this.#stream;
|
|
36
|
+
this.#stream = null;
|
|
37
|
+
return stream;
|
|
39
38
|
}
|
|
40
39
|
static finish(incomingBody) {
|
|
41
40
|
if (incomingBody.#finished)
|
|
@@ -43,15 +42,10 @@ class IncomingBody {
|
|
|
43
42
|
incomingBody.#finished = true;
|
|
44
43
|
return futureTrailersCreate();
|
|
45
44
|
}
|
|
46
|
-
[symbolDispose]() {
|
|
47
|
-
if (!this.#finished) {
|
|
48
|
-
ioCall(INPUT_STREAM_DISPOSE | HTTP, this.#streamId);
|
|
49
|
-
this.#streamId = undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
45
|
+
[symbolDispose]() {}
|
|
52
46
|
static _create(streamId) {
|
|
53
47
|
const incomingBody = new IncomingBody();
|
|
54
|
-
incomingBody.#
|
|
48
|
+
incomingBody.#stream = inputStreamCreate(HTTP, streamId);
|
|
55
49
|
return incomingBody;
|
|
56
50
|
}
|
|
57
51
|
}
|
|
@@ -83,6 +77,7 @@ class IncomingRequest {
|
|
|
83
77
|
consume() {
|
|
84
78
|
return incomingBodyCreate(this.#streamId);
|
|
85
79
|
}
|
|
80
|
+
[symbolDispose]() {}
|
|
86
81
|
static _create(method, pathWithQuery, scheme, authority, headers, streamId) {
|
|
87
82
|
const incomingRequest = new IncomingRequest();
|
|
88
83
|
incomingRequest.#method = method;
|
|
@@ -98,21 +93,19 @@ const incomingRequestCreate = IncomingRequest._create;
|
|
|
98
93
|
delete IncomingRequest._create;
|
|
99
94
|
|
|
100
95
|
class FutureTrailers {
|
|
101
|
-
_id = futureCnt++;
|
|
102
96
|
#requested = false;
|
|
103
97
|
subscribe() {
|
|
104
|
-
return pollableCreate(0);
|
|
98
|
+
return pollableCreate(0, this);
|
|
105
99
|
}
|
|
106
100
|
get() {
|
|
107
|
-
if (this.#requested)
|
|
108
|
-
return { tag: "err" };
|
|
101
|
+
if (this.#requested) return { tag: "err" };
|
|
109
102
|
this.#requested = true;
|
|
110
103
|
return {
|
|
111
104
|
tag: "ok",
|
|
112
105
|
val: {
|
|
113
106
|
tag: "ok",
|
|
114
107
|
val: undefined,
|
|
115
|
-
}
|
|
108
|
+
},
|
|
116
109
|
};
|
|
117
110
|
}
|
|
118
111
|
static _create() {
|
|
@@ -124,7 +117,6 @@ const futureTrailersCreate = FutureTrailers._create;
|
|
|
124
117
|
delete FutureTrailers._create;
|
|
125
118
|
|
|
126
119
|
class OutgoingResponse {
|
|
127
|
-
_id = responseCnt++;
|
|
128
120
|
#body;
|
|
129
121
|
/** @type {number} */ #statusCode = 200;
|
|
130
122
|
/** @type {Fields} */ #headers;
|
|
@@ -155,7 +147,7 @@ class OutgoingResponse {
|
|
|
155
147
|
let contentLength;
|
|
156
148
|
if (contentLengthValues.length > 0)
|
|
157
149
|
contentLength = Number(new TextDecoder().decode(contentLengthValues[0]));
|
|
158
|
-
this.#body = outgoingBodyCreate(contentLength
|
|
150
|
+
this.#body = outgoingBodyCreate(contentLength);
|
|
159
151
|
return this.#body;
|
|
160
152
|
}
|
|
161
153
|
|
|
@@ -206,7 +198,6 @@ class RequestOptions {
|
|
|
206
198
|
}
|
|
207
199
|
|
|
208
200
|
class OutgoingRequest {
|
|
209
|
-
_id = requestCnt++;
|
|
210
201
|
/** @type {Method} */ #method = { tag: "get" };
|
|
211
202
|
/** @type {Scheme | undefined} */ #scheme = undefined;
|
|
212
203
|
/** @type {string | undefined} */ #pathWithQuery = undefined;
|
|
@@ -223,7 +214,7 @@ class OutgoingRequest {
|
|
|
223
214
|
let contentLength;
|
|
224
215
|
if (contentLengthValues.length > 0)
|
|
225
216
|
contentLength = Number(new TextDecoder().decode(contentLengthValues[0]));
|
|
226
|
-
this.#body = outgoingBodyCreate(contentLength
|
|
217
|
+
this.#body = outgoingBodyCreate(contentLength);
|
|
227
218
|
}
|
|
228
219
|
body() {
|
|
229
220
|
if (this.#bodyRequested) throw new Error("Body already requested");
|
|
@@ -310,25 +301,14 @@ class OutgoingBody {
|
|
|
310
301
|
#outputStream = null;
|
|
311
302
|
#outputStreamId = null;
|
|
312
303
|
#contentLength = undefined;
|
|
304
|
+
#finalizer;
|
|
313
305
|
write() {
|
|
314
|
-
if (!this.#outputStreamId) this.#createOutputStream();
|
|
315
306
|
// can only call write once
|
|
316
307
|
const outputStream = this.#outputStream;
|
|
317
308
|
if (outputStream === null) throw undefined;
|
|
318
309
|
this.#outputStream = null;
|
|
319
310
|
return outputStream;
|
|
320
311
|
}
|
|
321
|
-
#createOutputStream() {
|
|
322
|
-
this.#outputStream = outputStreamCreate(
|
|
323
|
-
HTTP,
|
|
324
|
-
(this.#outputStreamId = ioCall(
|
|
325
|
-
OUTPUT_STREAM_CREATE | HTTP,
|
|
326
|
-
null,
|
|
327
|
-
this.#contentLength
|
|
328
|
-
))
|
|
329
|
-
);
|
|
330
|
-
this.#outputStream[symbolDispose] = () => {};
|
|
331
|
-
}
|
|
332
312
|
/**
|
|
333
313
|
* @param {OutgoingBody} body
|
|
334
314
|
* @param {Fields | undefined} trailers
|
|
@@ -337,19 +317,43 @@ class OutgoingBody {
|
|
|
337
317
|
if (trailers) throw { tag: "internal-error", val: "trailers unsupported" };
|
|
338
318
|
// this will verify content length, and also verify not already finished
|
|
339
319
|
// throwing errors as appropriate
|
|
340
|
-
|
|
341
|
-
ioCall(HTTP_OUTPUT_STREAM_FINISH, body.#outputStreamId, null);
|
|
320
|
+
ioCall(HTTP_OUTPUT_STREAM_FINISH, body.#outputStreamId, null);
|
|
342
321
|
}
|
|
343
322
|
static _outputStreamId(outgoingBody) {
|
|
344
323
|
return outgoingBody.#outputStreamId;
|
|
345
324
|
}
|
|
346
|
-
static _create(contentLength
|
|
325
|
+
static _create(contentLength) {
|
|
347
326
|
const outgoingBody = new OutgoingBody();
|
|
348
327
|
outgoingBody.#contentLength = contentLength;
|
|
349
|
-
|
|
328
|
+
outgoingBody.#outputStreamId = ioCall(
|
|
329
|
+
OUTPUT_STREAM_CREATE | HTTP,
|
|
330
|
+
null,
|
|
331
|
+
outgoingBody.#contentLength
|
|
332
|
+
);
|
|
333
|
+
outgoingBody.#outputStream = outputStreamCreate(
|
|
334
|
+
HTTP,
|
|
335
|
+
outgoingBody.#outputStreamId
|
|
336
|
+
);
|
|
337
|
+
outgoingBody.#finalizer = registerDispose(
|
|
338
|
+
outgoingBody,
|
|
339
|
+
null,
|
|
340
|
+
outgoingBody.#outputStreamId,
|
|
341
|
+
outgoingBodyDispose
|
|
342
|
+
);
|
|
350
343
|
return outgoingBody;
|
|
351
344
|
}
|
|
345
|
+
[symbolDispose]() {
|
|
346
|
+
if (this.#finalizer) {
|
|
347
|
+
earlyDispose(this.#finalizer);
|
|
348
|
+
this.#finalizer = null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
352
351
|
}
|
|
352
|
+
|
|
353
|
+
function outgoingBodyDispose(id) {
|
|
354
|
+
ioCall(HTTP_OUTGOING_BODY_DISPOSE, id, null);
|
|
355
|
+
}
|
|
356
|
+
|
|
353
357
|
const outgoingBodyOutputStreamId = OutgoingBody._outputStreamId;
|
|
354
358
|
delete OutgoingBody._outputStreamId;
|
|
355
359
|
|
|
@@ -357,10 +361,9 @@ const outgoingBodyCreate = OutgoingBody._create;
|
|
|
357
361
|
delete OutgoingBody._create;
|
|
358
362
|
|
|
359
363
|
class IncomingResponse {
|
|
360
|
-
_id = responseCnt++;
|
|
361
364
|
/** @type {Fields} */ #headers = undefined;
|
|
362
365
|
#status = 0;
|
|
363
|
-
/** @type {number} */ #
|
|
366
|
+
/** @type {number} */ #bodyStream;
|
|
364
367
|
status() {
|
|
365
368
|
return this.#status;
|
|
366
369
|
}
|
|
@@ -368,22 +371,19 @@ class IncomingResponse {
|
|
|
368
371
|
return this.#headers;
|
|
369
372
|
}
|
|
370
373
|
consume() {
|
|
371
|
-
if (this.#
|
|
372
|
-
const
|
|
373
|
-
this.#
|
|
374
|
-
return
|
|
374
|
+
if (this.#bodyStream === undefined) throw undefined;
|
|
375
|
+
const bodyStream = this.#bodyStream;
|
|
376
|
+
this.#bodyStream = undefined;
|
|
377
|
+
return bodyStream;
|
|
375
378
|
}
|
|
376
379
|
[symbolDispose]() {
|
|
377
|
-
if (this.#
|
|
378
|
-
ioCall(INPUT_STREAM_DISPOSE | HTTP, this.#bodyStreamId);
|
|
379
|
-
this.#bodyStreamId = undefined;
|
|
380
|
-
}
|
|
380
|
+
if (this.#bodyStream) this.#bodyStream[symbolDispose]();
|
|
381
381
|
}
|
|
382
382
|
static _create(status, headers, bodyStreamId) {
|
|
383
383
|
const res = new IncomingResponse();
|
|
384
384
|
res.#status = status;
|
|
385
385
|
res.#headers = headers;
|
|
386
|
-
res.#
|
|
386
|
+
res.#bodyStream = incomingBodyCreate(bodyStreamId);
|
|
387
387
|
return res;
|
|
388
388
|
}
|
|
389
389
|
}
|
|
@@ -392,38 +392,29 @@ const incomingResponseCreate = IncomingResponse._create;
|
|
|
392
392
|
delete IncomingResponse._create;
|
|
393
393
|
|
|
394
394
|
class FutureIncomingResponse {
|
|
395
|
-
|
|
396
|
-
#
|
|
395
|
+
#id;
|
|
396
|
+
#finalizer;
|
|
397
397
|
subscribe() {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
398
|
+
return pollableCreate(
|
|
399
|
+
ioCall(FUTURE_SUBSCRIBE | HTTP, this.#id, null),
|
|
400
|
+
this
|
|
401
|
+
);
|
|
401
402
|
}
|
|
402
403
|
get() {
|
|
403
|
-
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
tag: "ok",
|
|
413
|
-
val: {
|
|
414
|
-
tag: "ok",
|
|
415
|
-
val: incomingResponseCreate(
|
|
416
|
-
status,
|
|
417
|
-
fieldsFromEntriesChecked(
|
|
418
|
-
headers.map(([key, val]) => [key, textEncoder.encode(val)])
|
|
419
|
-
),
|
|
420
|
-
bodyStreamId
|
|
404
|
+
const ret = ioCall(FUTURE_TAKE_VALUE | HTTP, this.#id, null);
|
|
405
|
+
if (ret === undefined) return undefined;
|
|
406
|
+
if (ret.tag === "ok" && ret.val.tag === "ok") {
|
|
407
|
+
const textEncoder = new TextEncoder();
|
|
408
|
+
const { status, headers, bodyStreamId } = ret.val.val;
|
|
409
|
+
ret.val.val = incomingResponseCreate(
|
|
410
|
+
status,
|
|
411
|
+
fieldsFromEntriesChecked(
|
|
412
|
+
headers.map(([key, val]) => [key, textEncoder.encode(val)])
|
|
421
413
|
),
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
if (this.#pollId) ioCall(FUTURE_DISPOSE | HTTP, this.#pollId);
|
|
414
|
+
bodyStreamId
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
return ret;
|
|
427
418
|
}
|
|
428
419
|
static _create(
|
|
429
420
|
method,
|
|
@@ -437,7 +428,7 @@ class FutureIncomingResponse {
|
|
|
437
428
|
firstByteTimeout
|
|
438
429
|
) {
|
|
439
430
|
const res = new FutureIncomingResponse();
|
|
440
|
-
res.#
|
|
431
|
+
res.#id = ioCall(HTTP_CREATE_REQUEST, null, {
|
|
441
432
|
method,
|
|
442
433
|
scheme,
|
|
443
434
|
authority,
|
|
@@ -448,15 +439,30 @@ class FutureIncomingResponse {
|
|
|
448
439
|
betweenBytesTimeout,
|
|
449
440
|
firstByteTimeout,
|
|
450
441
|
});
|
|
442
|
+
res.#finalizer = registerDispose(
|
|
443
|
+
res,
|
|
444
|
+
null,
|
|
445
|
+
res.#id,
|
|
446
|
+
futureIncomingResponseDispose
|
|
447
|
+
);
|
|
451
448
|
return res;
|
|
452
449
|
}
|
|
450
|
+
[symbolDispose]() {
|
|
451
|
+
if (this.#finalizer) {
|
|
452
|
+
earlyDispose(this.#finalizer);
|
|
453
|
+
this.#finalizer = null;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function futureIncomingResponseDispose(id) {
|
|
459
|
+
ioCall(FUTURE_DISPOSE | HTTP, id, null);
|
|
453
460
|
}
|
|
454
461
|
|
|
455
462
|
const futureIncomingResponseCreate = FutureIncomingResponse._create;
|
|
456
463
|
delete FutureIncomingResponse._create;
|
|
457
464
|
|
|
458
465
|
class Fields {
|
|
459
|
-
_id = fieldsCnt++;
|
|
460
466
|
#immutable = false;
|
|
461
467
|
/** @type {[string, Uint8Array[]][]} */ #entries = [];
|
|
462
468
|
/** @type {Map<string, [string, Uint8Array[]][]>} */ #table = new Map();
|
|
@@ -664,26 +670,29 @@ export class HTTPServer {
|
|
|
664
670
|
),
|
|
665
671
|
streamId
|
|
666
672
|
);
|
|
673
|
+
let outgoingBodyStreamId;
|
|
667
674
|
const responseOutparam = responseOutparamCreate((response) => {
|
|
668
675
|
if (response.tag === "ok") {
|
|
669
676
|
const outgoingResponse = response.val;
|
|
670
677
|
const statusCode = outgoingResponse.statusCode();
|
|
671
678
|
const headers = outgoingResponse.headers().entries();
|
|
672
679
|
const body = outgoingResponseBody(outgoingResponse);
|
|
673
|
-
|
|
680
|
+
outgoingBodyStreamId = outgoingBodyOutputStreamId(body);
|
|
674
681
|
ioCall(HTTP_SERVER_SET_OUTGOING_RESPONSE, responseId, {
|
|
675
682
|
statusCode,
|
|
676
683
|
headers,
|
|
677
|
-
streamId,
|
|
684
|
+
streamId: outgoingBodyStreamId,
|
|
678
685
|
});
|
|
679
686
|
} else {
|
|
680
|
-
ioCall(HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, responseId);
|
|
681
|
-
console.error(
|
|
682
|
-
console.error(response);
|
|
687
|
+
ioCall(HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, responseId, null);
|
|
688
|
+
console.error(response.val);
|
|
683
689
|
process.exit(1);
|
|
684
690
|
}
|
|
685
691
|
});
|
|
686
692
|
incomingHandler.handle(request, responseOutparam);
|
|
693
|
+
if (outgoingBodyStreamId) {
|
|
694
|
+
ioCall(OUTPUT_STREAM_DISPOSE, outgoingBodyStreamId, null);
|
|
695
|
+
}
|
|
687
696
|
}
|
|
688
697
|
);
|
|
689
698
|
}
|
|
@@ -694,6 +703,7 @@ export class HTTPServer {
|
|
|
694
703
|
}
|
|
695
704
|
stop() {
|
|
696
705
|
clearInterval(this.#liveEventLoopInterval);
|
|
697
|
-
ioCall(HTTP_SERVER_STOP, this.#id);
|
|
706
|
+
ioCall(HTTP_SERVER_STOP, this.#id, null);
|
|
707
|
+
httpServers.delete(this.#id);
|
|
698
708
|
}
|
|
699
709
|
}
|