@bytecodealliance/preview2-shim 0.17.8 → 0.17.9
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 +18 -18
- package/lib/browser/cli.js +71 -71
- package/lib/browser/clocks.js +32 -32
- package/lib/browser/config.js +3 -3
- package/lib/browser/environment.js +15 -15
- package/lib/browser/filesystem.js +382 -396
- package/lib/browser/http.js +578 -576
- package/lib/browser/index.js +7 -7
- package/lib/browser/io.js +187 -181
- package/lib/browser/random.js +42 -44
- package/lib/browser/sockets.js +52 -52
- package/lib/common/instantiation.js +147 -151
- package/lib/io/calls.js +7 -9
- package/lib/io/worker-http.js +167 -178
- package/lib/io/worker-io.js +338 -406
- package/lib/io/worker-socket-tcp.js +259 -271
- package/lib/io/worker-socket-udp.js +437 -491
- package/lib/io/worker-sockets.js +230 -251
- package/lib/io/worker-thread.js +774 -822
- package/lib/nodejs/cli.js +53 -65
- package/lib/nodejs/clocks.js +49 -52
- package/lib/nodejs/filesystem.js +726 -785
- package/lib/nodejs/http.js +651 -697
- package/lib/nodejs/index.js +7 -7
- package/lib/nodejs/io.js +1 -1
- package/lib/nodejs/random.js +32 -32
- package/lib/nodejs/sockets.js +468 -538
- package/lib/synckit/index.js +85 -94
- package/package.json +11 -10
package/lib/nodejs/http.js
CHANGED
|
@@ -1,192 +1,183 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from
|
|
2
|
+
FUTURE_DISPOSE,
|
|
3
|
+
FUTURE_SUBSCRIBE,
|
|
4
|
+
FUTURE_TAKE_VALUE,
|
|
5
|
+
HTTP_CREATE_REQUEST,
|
|
6
|
+
HTTP_OUTGOING_BODY_DISPOSE,
|
|
7
|
+
HTTP_OUTPUT_STREAM_FINISH,
|
|
8
|
+
HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
|
|
9
|
+
HTTP_SERVER_SET_OUTGOING_RESPONSE,
|
|
10
|
+
HTTP_SERVER_START,
|
|
11
|
+
HTTP_SERVER_STOP,
|
|
12
|
+
HTTP_SERVER_GET_ADDRESS,
|
|
13
|
+
OUTPUT_STREAM_CREATE,
|
|
14
|
+
OUTPUT_STREAM_DISPOSE,
|
|
15
|
+
} from "../io/calls.js";
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from
|
|
25
|
-
import { HTTP } from
|
|
26
|
-
|
|
27
|
-
import * as http from
|
|
17
|
+
earlyDispose,
|
|
18
|
+
inputStreamCreate,
|
|
19
|
+
ioCall,
|
|
20
|
+
outputStreamCreate,
|
|
21
|
+
pollableCreate,
|
|
22
|
+
registerDispose,
|
|
23
|
+
registerIncomingHttpHandler,
|
|
24
|
+
} from "../io/worker-io.js";
|
|
25
|
+
import { HTTP } from "../io/calls.js";
|
|
26
|
+
|
|
27
|
+
import * as http from "node:http";
|
|
28
28
|
const { validateHeaderName = () => {}, validateHeaderValue = () => {} } = http;
|
|
29
29
|
|
|
30
|
-
const symbolDispose = Symbol.dispose || Symbol.for(
|
|
31
|
-
export const _forbiddenHeaders = new Set([
|
|
30
|
+
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
31
|
+
export const _forbiddenHeaders = new Set(["connection", "keep-alive", "host"]);
|
|
32
32
|
|
|
33
33
|
class IncomingBody {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
34
|
+
#finished = false;
|
|
35
|
+
#stream = undefined;
|
|
36
|
+
stream() {
|
|
37
|
+
if (!this.#stream) {
|
|
38
|
+
throw undefined;
|
|
39
|
+
}
|
|
40
|
+
const stream = this.#stream;
|
|
41
|
+
this.#stream = null;
|
|
42
|
+
return stream;
|
|
43
|
+
}
|
|
44
|
+
static finish(incomingBody) {
|
|
45
|
+
if (incomingBody.#finished) {
|
|
46
|
+
throw new Error("incoming body already finished");
|
|
47
|
+
}
|
|
48
|
+
incomingBody.#finished = true;
|
|
49
|
+
return futureTrailersCreate();
|
|
50
|
+
}
|
|
51
|
+
[symbolDispose]() {}
|
|
52
|
+
static _create(streamId) {
|
|
53
|
+
const incomingBody = new IncomingBody();
|
|
54
|
+
incomingBody.#stream = inputStreamCreate(HTTP, streamId);
|
|
55
|
+
return incomingBody;
|
|
56
|
+
}
|
|
57
57
|
}
|
|
58
58
|
const incomingBodyCreate = IncomingBody._create;
|
|
59
59
|
delete IncomingBody._create;
|
|
60
60
|
|
|
61
61
|
class IncomingRequest {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
incomingRequest.#pathWithQuery = pathWithQuery;
|
|
98
|
-
incomingRequest.#scheme = scheme;
|
|
99
|
-
incomingRequest.#authority = authority;
|
|
100
|
-
incomingRequest.#headers = headers;
|
|
101
|
-
incomingRequest.#streamId = streamId;
|
|
102
|
-
return incomingRequest;
|
|
103
|
-
}
|
|
62
|
+
#method;
|
|
63
|
+
#pathWithQuery;
|
|
64
|
+
#scheme;
|
|
65
|
+
#authority;
|
|
66
|
+
#headers;
|
|
67
|
+
#streamId;
|
|
68
|
+
method() {
|
|
69
|
+
return this.#method;
|
|
70
|
+
}
|
|
71
|
+
pathWithQuery() {
|
|
72
|
+
return this.#pathWithQuery;
|
|
73
|
+
}
|
|
74
|
+
scheme() {
|
|
75
|
+
return this.#scheme;
|
|
76
|
+
}
|
|
77
|
+
authority() {
|
|
78
|
+
return this.#authority;
|
|
79
|
+
}
|
|
80
|
+
headers() {
|
|
81
|
+
return this.#headers;
|
|
82
|
+
}
|
|
83
|
+
consume() {
|
|
84
|
+
return incomingBodyCreate(this.#streamId);
|
|
85
|
+
}
|
|
86
|
+
[symbolDispose]() {}
|
|
87
|
+
static _create(method, pathWithQuery, scheme, authority, headers, streamId) {
|
|
88
|
+
const incomingRequest = new IncomingRequest();
|
|
89
|
+
incomingRequest.#method = method;
|
|
90
|
+
incomingRequest.#pathWithQuery = pathWithQuery;
|
|
91
|
+
incomingRequest.#scheme = scheme;
|
|
92
|
+
incomingRequest.#authority = authority;
|
|
93
|
+
incomingRequest.#headers = headers;
|
|
94
|
+
incomingRequest.#streamId = streamId;
|
|
95
|
+
return incomingRequest;
|
|
96
|
+
}
|
|
104
97
|
}
|
|
105
98
|
const incomingRequestCreate = IncomingRequest._create;
|
|
106
99
|
delete IncomingRequest._create;
|
|
107
100
|
|
|
108
101
|
class FutureTrailers {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
102
|
+
#requested = false;
|
|
103
|
+
subscribe() {
|
|
104
|
+
return pollableCreate(0, this);
|
|
105
|
+
}
|
|
106
|
+
get() {
|
|
107
|
+
if (this.#requested) {
|
|
108
|
+
return { tag: "err" };
|
|
109
|
+
}
|
|
110
|
+
this.#requested = true;
|
|
111
|
+
return {
|
|
112
|
+
tag: "ok",
|
|
113
|
+
val: {
|
|
114
|
+
tag: "ok",
|
|
115
|
+
val: undefined,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
static _create() {
|
|
120
|
+
const res = new FutureTrailers();
|
|
121
|
+
return res;
|
|
122
|
+
}
|
|
130
123
|
}
|
|
131
124
|
const futureTrailersCreate = FutureTrailers._create;
|
|
132
125
|
delete FutureTrailers._create;
|
|
133
126
|
|
|
134
127
|
class OutgoingResponse {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return outgoingResponse.#body;
|
|
174
|
-
}
|
|
128
|
+
#body;
|
|
129
|
+
/** @type {number} */ #statusCode = 200;
|
|
130
|
+
/** @type {Fields} */ #headers;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {number} statusCode
|
|
134
|
+
* @param {Fields} headers
|
|
135
|
+
*/
|
|
136
|
+
constructor(headers) {
|
|
137
|
+
fieldsLock(headers);
|
|
138
|
+
this.#headers = headers;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
statusCode() {
|
|
142
|
+
return this.#statusCode;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setStatusCode(statusCode) {
|
|
146
|
+
this.#statusCode = statusCode;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
headers() {
|
|
150
|
+
return this.#headers;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
body() {
|
|
154
|
+
let contentLengthValues = this.#headers.get("content-length");
|
|
155
|
+
let contentLength;
|
|
156
|
+
if (contentLengthValues.length > 0) {
|
|
157
|
+
contentLength = Number(new TextDecoder().decode(contentLengthValues[0]));
|
|
158
|
+
}
|
|
159
|
+
this.#body = outgoingBodyCreate(contentLength);
|
|
160
|
+
return this.#body;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static _body(outgoingResponse) {
|
|
164
|
+
return outgoingResponse.#body;
|
|
165
|
+
}
|
|
175
166
|
}
|
|
176
167
|
|
|
177
168
|
const outgoingResponseBody = OutgoingResponse._body;
|
|
178
169
|
delete OutgoingResponse._body;
|
|
179
170
|
|
|
180
171
|
class ResponseOutparam {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
172
|
+
#setListener;
|
|
173
|
+
static set(param, response) {
|
|
174
|
+
param.#setListener(response);
|
|
175
|
+
}
|
|
176
|
+
static _create(setListener) {
|
|
177
|
+
const responseOutparam = new ResponseOutparam();
|
|
178
|
+
responseOutparam.#setListener = setListener;
|
|
179
|
+
return responseOutparam;
|
|
180
|
+
}
|
|
190
181
|
}
|
|
191
182
|
const responseOutparamCreate = ResponseOutparam._create;
|
|
192
183
|
delete ResponseOutparam._create;
|
|
@@ -194,201 +185,192 @@ delete ResponseOutparam._create;
|
|
|
194
185
|
const defaultHttpTimeout = 600_000_000_000n;
|
|
195
186
|
|
|
196
187
|
class RequestOptions {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
188
|
+
#connectTimeout = defaultHttpTimeout;
|
|
189
|
+
#firstByteTimeout = defaultHttpTimeout;
|
|
190
|
+
#betweenBytesTimeout = defaultHttpTimeout;
|
|
191
|
+
connectTimeout() {
|
|
192
|
+
return this.#connectTimeout;
|
|
193
|
+
}
|
|
194
|
+
setConnectTimeout(duration) {
|
|
195
|
+
this.#connectTimeout = duration;
|
|
196
|
+
}
|
|
197
|
+
firstByteTimeout() {
|
|
198
|
+
return this.#firstByteTimeout;
|
|
199
|
+
}
|
|
200
|
+
setFirstByteTimeout(duration) {
|
|
201
|
+
this.#firstByteTimeout = duration;
|
|
202
|
+
}
|
|
203
|
+
betweenBytesTimeout() {
|
|
204
|
+
return this.#betweenBytesTimeout;
|
|
205
|
+
}
|
|
206
|
+
setBetweenBytesTimeout(duration) {
|
|
207
|
+
this.#betweenBytesTimeout = duration;
|
|
208
|
+
}
|
|
218
209
|
}
|
|
219
210
|
|
|
220
211
|
class OutgoingRequest {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
outgoingBodyOutputStreamId(request.#body),
|
|
323
|
-
connectTimeout,
|
|
324
|
-
betweenBytesTimeout,
|
|
325
|
-
firstByteTimeout
|
|
326
|
-
);
|
|
327
|
-
}
|
|
212
|
+
/** @type {Method} */ #method = { tag: "get" };
|
|
213
|
+
/** @type {Scheme | undefined} */ #scheme = undefined;
|
|
214
|
+
/** @type {string | undefined} */ #pathWithQuery = undefined;
|
|
215
|
+
/** @type {string | undefined} */ #authority = undefined;
|
|
216
|
+
/** @type {Fields} */ #headers;
|
|
217
|
+
/** @type {OutgoingBody} */ #body;
|
|
218
|
+
#bodyRequested = false;
|
|
219
|
+
constructor(headers) {
|
|
220
|
+
fieldsLock(headers);
|
|
221
|
+
this.#headers = headers;
|
|
222
|
+
let contentLengthValues = this.#headers.get("content-length");
|
|
223
|
+
if (contentLengthValues.length === 0) {
|
|
224
|
+
contentLengthValues = this.#headers.get("Content-Length");
|
|
225
|
+
}
|
|
226
|
+
let contentLength;
|
|
227
|
+
if (contentLengthValues.length > 0) {
|
|
228
|
+
contentLength = Number(new TextDecoder().decode(contentLengthValues[0]));
|
|
229
|
+
}
|
|
230
|
+
this.#body = outgoingBodyCreate(contentLength);
|
|
231
|
+
}
|
|
232
|
+
body() {
|
|
233
|
+
if (this.#bodyRequested) {
|
|
234
|
+
throw new Error("Body already requested");
|
|
235
|
+
}
|
|
236
|
+
this.#bodyRequested = true;
|
|
237
|
+
return this.#body;
|
|
238
|
+
}
|
|
239
|
+
method() {
|
|
240
|
+
return this.#method;
|
|
241
|
+
}
|
|
242
|
+
setMethod(method) {
|
|
243
|
+
if (method.tag === "other" && !method.val.match(/^[a-zA-Z-]+$/)) {
|
|
244
|
+
throw undefined;
|
|
245
|
+
}
|
|
246
|
+
this.#method = method;
|
|
247
|
+
}
|
|
248
|
+
pathWithQuery() {
|
|
249
|
+
return this.#pathWithQuery;
|
|
250
|
+
}
|
|
251
|
+
setPathWithQuery(pathWithQuery) {
|
|
252
|
+
if (pathWithQuery && !pathWithQuery.match(/^[a-zA-Z0-9.\-_~!$&'()*+,;=:@%?/]+$/)) {
|
|
253
|
+
throw undefined;
|
|
254
|
+
}
|
|
255
|
+
this.#pathWithQuery = pathWithQuery;
|
|
256
|
+
}
|
|
257
|
+
scheme() {
|
|
258
|
+
return this.#scheme;
|
|
259
|
+
}
|
|
260
|
+
setScheme(scheme) {
|
|
261
|
+
if (scheme?.tag === "other" && !scheme.val.match(/^[a-zA-Z]+$/)) {
|
|
262
|
+
throw undefined;
|
|
263
|
+
}
|
|
264
|
+
this.#scheme = scheme;
|
|
265
|
+
}
|
|
266
|
+
authority() {
|
|
267
|
+
return this.#authority;
|
|
268
|
+
}
|
|
269
|
+
setAuthority(authority) {
|
|
270
|
+
if (authority) {
|
|
271
|
+
const [host, port, ...extra] = authority.split(":");
|
|
272
|
+
const portNum = Number(port);
|
|
273
|
+
if (
|
|
274
|
+
extra.length ||
|
|
275
|
+
(port !== undefined && (portNum.toString() !== port || portNum > 65535)) ||
|
|
276
|
+
!host.match(/^[a-zA-Z0-9-.]+$/)
|
|
277
|
+
) {
|
|
278
|
+
throw undefined;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
this.#authority = authority;
|
|
282
|
+
}
|
|
283
|
+
headers() {
|
|
284
|
+
return this.#headers;
|
|
285
|
+
}
|
|
286
|
+
[symbolDispose]() {}
|
|
287
|
+
static _handle(request, options) {
|
|
288
|
+
const connectTimeout = options?.connectTimeout();
|
|
289
|
+
const betweenBytesTimeout = options?.betweenBytesTimeout();
|
|
290
|
+
const firstByteTimeout = options?.firstByteTimeout();
|
|
291
|
+
const scheme = schemeString(request.#scheme);
|
|
292
|
+
// note: host header is automatically added by Node.js
|
|
293
|
+
const headers = [];
|
|
294
|
+
const decoder = new TextDecoder();
|
|
295
|
+
for (const [key, value] of request.#headers.entries()) {
|
|
296
|
+
headers.push([key, decoder.decode(value)]);
|
|
297
|
+
}
|
|
298
|
+
if (!request.#pathWithQuery) {
|
|
299
|
+
throw { tag: "HTTP-request-URI-invalid" };
|
|
300
|
+
}
|
|
301
|
+
return futureIncomingResponseCreate(
|
|
302
|
+
request.#method.val || request.#method.tag,
|
|
303
|
+
scheme,
|
|
304
|
+
request.#authority,
|
|
305
|
+
request.#pathWithQuery,
|
|
306
|
+
headers,
|
|
307
|
+
outgoingBodyOutputStreamId(request.#body),
|
|
308
|
+
connectTimeout,
|
|
309
|
+
betweenBytesTimeout,
|
|
310
|
+
firstByteTimeout,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
328
313
|
}
|
|
329
314
|
|
|
330
315
|
const outgoingRequestHandle = OutgoingRequest._handle;
|
|
331
316
|
delete OutgoingRequest._handle;
|
|
332
317
|
|
|
333
318
|
class OutgoingBody {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
this.#finalizer = null;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
319
|
+
#outputStream = null;
|
|
320
|
+
#outputStreamId = null;
|
|
321
|
+
#contentLength = undefined;
|
|
322
|
+
#finalizer;
|
|
323
|
+
write() {
|
|
324
|
+
// can only call write once
|
|
325
|
+
const outputStream = this.#outputStream;
|
|
326
|
+
if (outputStream === null) {
|
|
327
|
+
throw undefined;
|
|
328
|
+
}
|
|
329
|
+
this.#outputStream = null;
|
|
330
|
+
return outputStream;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* @param {OutgoingBody} body
|
|
334
|
+
* @param {Fields | undefined} trailers
|
|
335
|
+
*/
|
|
336
|
+
static finish(body, trailers) {
|
|
337
|
+
if (trailers) {
|
|
338
|
+
throw { tag: "internal-error", val: "trailers unsupported" };
|
|
339
|
+
}
|
|
340
|
+
// this will verify content length, and also verify not already finished
|
|
341
|
+
// throwing errors as appropriate
|
|
342
|
+
ioCall(HTTP_OUTPUT_STREAM_FINISH, body.#outputStreamId, null);
|
|
343
|
+
}
|
|
344
|
+
static _outputStreamId(outgoingBody) {
|
|
345
|
+
return outgoingBody.#outputStreamId;
|
|
346
|
+
}
|
|
347
|
+
static _create(contentLength) {
|
|
348
|
+
const outgoingBody = new OutgoingBody();
|
|
349
|
+
outgoingBody.#contentLength = contentLength;
|
|
350
|
+
outgoingBody.#outputStreamId = ioCall(
|
|
351
|
+
OUTPUT_STREAM_CREATE | HTTP,
|
|
352
|
+
null,
|
|
353
|
+
outgoingBody.#contentLength,
|
|
354
|
+
);
|
|
355
|
+
outgoingBody.#outputStream = outputStreamCreate(HTTP, outgoingBody.#outputStreamId);
|
|
356
|
+
outgoingBody.#finalizer = registerDispose(
|
|
357
|
+
outgoingBody,
|
|
358
|
+
null,
|
|
359
|
+
outgoingBody.#outputStreamId,
|
|
360
|
+
outgoingBodyDispose,
|
|
361
|
+
);
|
|
362
|
+
return outgoingBody;
|
|
363
|
+
}
|
|
364
|
+
[symbolDispose]() {
|
|
365
|
+
if (this.#finalizer) {
|
|
366
|
+
earlyDispose(this.#finalizer);
|
|
367
|
+
this.#finalizer = null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
388
370
|
}
|
|
389
371
|
|
|
390
372
|
function outgoingBodyDispose(id) {
|
|
391
|
-
|
|
373
|
+
ioCall(HTTP_OUTGOING_BODY_DISPOSE, id, null);
|
|
392
374
|
}
|
|
393
375
|
|
|
394
376
|
const outgoingBodyOutputStreamId = OutgoingBody._outputStreamId;
|
|
@@ -398,237 +380,223 @@ const outgoingBodyCreate = OutgoingBody._create;
|
|
|
398
380
|
delete OutgoingBody._create;
|
|
399
381
|
|
|
400
382
|
class IncomingResponse {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
383
|
+
/** @type {Fields} */ #headers = undefined;
|
|
384
|
+
#status = 0;
|
|
385
|
+
/** @type {number} */ #bodyStream;
|
|
386
|
+
status() {
|
|
387
|
+
return this.#status;
|
|
388
|
+
}
|
|
389
|
+
headers() {
|
|
390
|
+
return this.#headers;
|
|
391
|
+
}
|
|
392
|
+
consume() {
|
|
393
|
+
if (this.#bodyStream === undefined) {
|
|
394
|
+
throw undefined;
|
|
395
|
+
}
|
|
396
|
+
const bodyStream = this.#bodyStream;
|
|
397
|
+
this.#bodyStream = undefined;
|
|
398
|
+
return bodyStream;
|
|
399
|
+
}
|
|
400
|
+
[symbolDispose]() {
|
|
401
|
+
if (this.#bodyStream) {
|
|
402
|
+
this.#bodyStream[symbolDispose]();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
static _create(status, headers, bodyStreamId) {
|
|
406
|
+
const res = new IncomingResponse();
|
|
407
|
+
res.#status = status;
|
|
408
|
+
res.#headers = headers;
|
|
409
|
+
res.#bodyStream = incomingBodyCreate(bodyStreamId);
|
|
410
|
+
return res;
|
|
411
|
+
}
|
|
430
412
|
}
|
|
431
413
|
|
|
432
414
|
const incomingResponseCreate = IncomingResponse._create;
|
|
433
415
|
delete IncomingResponse._create;
|
|
434
416
|
|
|
435
417
|
class FutureIncomingResponse {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
futureIncomingResponseDispose
|
|
490
|
-
);
|
|
491
|
-
return res;
|
|
492
|
-
}
|
|
493
|
-
[symbolDispose]() {
|
|
494
|
-
if (this.#finalizer) {
|
|
495
|
-
earlyDispose(this.#finalizer);
|
|
496
|
-
this.#finalizer = null;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
418
|
+
#id;
|
|
419
|
+
#finalizer;
|
|
420
|
+
subscribe() {
|
|
421
|
+
return pollableCreate(ioCall(FUTURE_SUBSCRIBE | HTTP, this.#id, null), this);
|
|
422
|
+
}
|
|
423
|
+
get() {
|
|
424
|
+
const ret = ioCall(FUTURE_TAKE_VALUE | HTTP, this.#id, null);
|
|
425
|
+
if (ret === undefined) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
if (ret.tag === "ok" && ret.val.tag === "ok") {
|
|
429
|
+
const textEncoder = new TextEncoder();
|
|
430
|
+
const { status, headers, bodyStreamId } = ret.val.val;
|
|
431
|
+
ret.val.val = incomingResponseCreate(
|
|
432
|
+
status,
|
|
433
|
+
fieldsFromEntriesChecked(headers.map(([key, val]) => [key, textEncoder.encode(val)])),
|
|
434
|
+
bodyStreamId,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return ret;
|
|
438
|
+
}
|
|
439
|
+
static _create(
|
|
440
|
+
method,
|
|
441
|
+
scheme,
|
|
442
|
+
authority,
|
|
443
|
+
pathWithQuery,
|
|
444
|
+
headers,
|
|
445
|
+
body,
|
|
446
|
+
connectTimeout,
|
|
447
|
+
betweenBytesTimeout,
|
|
448
|
+
firstByteTimeout,
|
|
449
|
+
) {
|
|
450
|
+
const res = new FutureIncomingResponse();
|
|
451
|
+
res.#id = ioCall(HTTP_CREATE_REQUEST, null, {
|
|
452
|
+
method,
|
|
453
|
+
scheme,
|
|
454
|
+
authority,
|
|
455
|
+
pathWithQuery,
|
|
456
|
+
headers,
|
|
457
|
+
body,
|
|
458
|
+
connectTimeout,
|
|
459
|
+
betweenBytesTimeout,
|
|
460
|
+
firstByteTimeout,
|
|
461
|
+
});
|
|
462
|
+
res.#finalizer = registerDispose(res, null, res.#id, futureIncomingResponseDispose);
|
|
463
|
+
return res;
|
|
464
|
+
}
|
|
465
|
+
[symbolDispose]() {
|
|
466
|
+
if (this.#finalizer) {
|
|
467
|
+
earlyDispose(this.#finalizer);
|
|
468
|
+
this.#finalizer = null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
499
471
|
}
|
|
500
472
|
|
|
501
473
|
function futureIncomingResponseDispose(id) {
|
|
502
|
-
|
|
474
|
+
ioCall(FUTURE_DISPOSE | HTTP, id, null);
|
|
503
475
|
}
|
|
504
476
|
|
|
505
477
|
const futureIncomingResponseCreate = FutureIncomingResponse._create;
|
|
506
478
|
delete FutureIncomingResponse._create;
|
|
507
479
|
|
|
508
480
|
class Fields {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
entries
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
return fields;
|
|
631
|
-
}
|
|
481
|
+
#immutable = false;
|
|
482
|
+
/** @type {[string, Uint8Array[]][]} */ #entries = [];
|
|
483
|
+
/** @type {Map<string, [string, Uint8Array[]][]>} */ #table = new Map();
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @param {[string, Uint8Array[][]][]} entries
|
|
487
|
+
*/
|
|
488
|
+
static fromList(entries) {
|
|
489
|
+
const fields = new Fields();
|
|
490
|
+
for (const [key, value] of entries) {
|
|
491
|
+
fields.append(key, value);
|
|
492
|
+
}
|
|
493
|
+
return fields;
|
|
494
|
+
}
|
|
495
|
+
get(name) {
|
|
496
|
+
const tableEntries = this.#table.get(name.toLowerCase());
|
|
497
|
+
if (!tableEntries) {
|
|
498
|
+
return [];
|
|
499
|
+
}
|
|
500
|
+
return tableEntries.map(([, v]) => v);
|
|
501
|
+
}
|
|
502
|
+
set(name, values) {
|
|
503
|
+
if (this.#immutable) {
|
|
504
|
+
throw { tag: "immutable" };
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
validateHeaderName(name);
|
|
508
|
+
} catch {
|
|
509
|
+
throw { tag: "invalid-syntax" };
|
|
510
|
+
}
|
|
511
|
+
for (const value of values) {
|
|
512
|
+
try {
|
|
513
|
+
validateHeaderValue(name, new TextDecoder().decode(value));
|
|
514
|
+
} catch {
|
|
515
|
+
throw { tag: "invalid-syntax" };
|
|
516
|
+
}
|
|
517
|
+
throw { tag: "invalid-syntax" };
|
|
518
|
+
}
|
|
519
|
+
const lowercased = name.toLowerCase();
|
|
520
|
+
if (_forbiddenHeaders.has(lowercased)) {
|
|
521
|
+
throw { tag: "forbidden" };
|
|
522
|
+
}
|
|
523
|
+
const tableEntries = this.#table.get(lowercased);
|
|
524
|
+
if (tableEntries) {
|
|
525
|
+
this.#entries = this.#entries.filter((entry) => !tableEntries.includes(entry));
|
|
526
|
+
}
|
|
527
|
+
tableEntries.splice(0, tableEntries.length);
|
|
528
|
+
for (const value of values) {
|
|
529
|
+
const entry = [name, value];
|
|
530
|
+
this.#entries.push(entry);
|
|
531
|
+
tableEntries.push(entry);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
has(name) {
|
|
535
|
+
return this.#table.has(name.toLowerCase());
|
|
536
|
+
}
|
|
537
|
+
delete(name) {
|
|
538
|
+
if (this.#immutable) {
|
|
539
|
+
throw { tag: "immutable" };
|
|
540
|
+
}
|
|
541
|
+
const lowercased = name.toLowerCase();
|
|
542
|
+
const tableEntries = this.#table.get(lowercased);
|
|
543
|
+
if (tableEntries) {
|
|
544
|
+
this.#entries = this.#entries.filter((entry) => !tableEntries.includes(entry));
|
|
545
|
+
this.#table.delete(lowercased);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
append(name, value) {
|
|
549
|
+
if (this.#immutable) {
|
|
550
|
+
throw { tag: "immutable" };
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
validateHeaderName(name);
|
|
554
|
+
} catch {
|
|
555
|
+
throw { tag: "invalid-syntax" };
|
|
556
|
+
}
|
|
557
|
+
try {
|
|
558
|
+
validateHeaderValue(name, new TextDecoder().decode(value));
|
|
559
|
+
} catch {
|
|
560
|
+
throw { tag: "invalid-syntax" };
|
|
561
|
+
}
|
|
562
|
+
const lowercased = name.toLowerCase();
|
|
563
|
+
if (_forbiddenHeaders.has(lowercased)) {
|
|
564
|
+
throw { tag: "forbidden" };
|
|
565
|
+
}
|
|
566
|
+
const entry = [name, value];
|
|
567
|
+
this.#entries.push(entry);
|
|
568
|
+
const tableEntries = this.#table.get(lowercased);
|
|
569
|
+
if (tableEntries) {
|
|
570
|
+
tableEntries.push(entry);
|
|
571
|
+
} else {
|
|
572
|
+
this.#table.set(lowercased, [entry]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
entries() {
|
|
576
|
+
return this.#entries;
|
|
577
|
+
}
|
|
578
|
+
clone() {
|
|
579
|
+
return fieldsFromEntriesChecked(this.#entries);
|
|
580
|
+
}
|
|
581
|
+
static _lock(fields) {
|
|
582
|
+
fields.#immutable = true;
|
|
583
|
+
return fields;
|
|
584
|
+
}
|
|
585
|
+
// assumes entries are already validated
|
|
586
|
+
static _fromEntriesChecked(entries) {
|
|
587
|
+
const fields = new Fields();
|
|
588
|
+
fields.#entries = entries;
|
|
589
|
+
for (const entry of entries) {
|
|
590
|
+
const lowercase = entry[0].toLowerCase();
|
|
591
|
+
const existing = fields.#table.get(lowercase);
|
|
592
|
+
if (existing) {
|
|
593
|
+
existing.push(entry);
|
|
594
|
+
} else {
|
|
595
|
+
fields.#table.set(lowercase, [entry]);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return fields;
|
|
599
|
+
}
|
|
632
600
|
}
|
|
633
601
|
const fieldsLock = Fields._lock;
|
|
634
602
|
delete Fields._lock;
|
|
@@ -636,160 +604,146 @@ const fieldsFromEntriesChecked = Fields._fromEntriesChecked;
|
|
|
636
604
|
delete Fields._fromEntriesChecked;
|
|
637
605
|
|
|
638
606
|
export const outgoingHandler = {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
607
|
+
/**
|
|
608
|
+
* @param {OutgoingRequest} request
|
|
609
|
+
* @param {RequestOptions | undefined} options
|
|
610
|
+
* @returns {FutureIncomingResponse}
|
|
611
|
+
*/
|
|
612
|
+
handle: outgoingRequestHandle,
|
|
645
613
|
};
|
|
646
614
|
|
|
647
615
|
function httpErrorCode(err) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
616
|
+
if (err.payload) {
|
|
617
|
+
return err.payload;
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
tag: "internal-error",
|
|
621
|
+
val: err.message,
|
|
622
|
+
};
|
|
655
623
|
}
|
|
656
624
|
|
|
657
625
|
export const types = {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
626
|
+
Fields,
|
|
627
|
+
FutureIncomingResponse,
|
|
628
|
+
FutureTrailers,
|
|
629
|
+
IncomingBody,
|
|
630
|
+
IncomingRequest,
|
|
631
|
+
IncomingResponse,
|
|
632
|
+
OutgoingBody,
|
|
633
|
+
OutgoingRequest,
|
|
634
|
+
OutgoingResponse,
|
|
635
|
+
ResponseOutparam,
|
|
636
|
+
RequestOptions,
|
|
637
|
+
httpErrorCode,
|
|
670
638
|
};
|
|
671
639
|
|
|
672
640
|
function schemeString(scheme) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
case
|
|
678
|
-
|
|
679
|
-
case
|
|
680
|
-
|
|
681
|
-
case
|
|
682
|
-
|
|
683
|
-
|
|
641
|
+
if (!scheme) {
|
|
642
|
+
return "https:";
|
|
643
|
+
}
|
|
644
|
+
switch (scheme.tag) {
|
|
645
|
+
case "HTTP":
|
|
646
|
+
return "http:";
|
|
647
|
+
case "HTTPS":
|
|
648
|
+
return "https:";
|
|
649
|
+
case "other":
|
|
650
|
+
return scheme.val.toLowerCase() + ":";
|
|
651
|
+
}
|
|
684
652
|
}
|
|
685
653
|
|
|
686
654
|
const supportedMethods = [
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
655
|
+
"get",
|
|
656
|
+
"head",
|
|
657
|
+
"post",
|
|
658
|
+
"put",
|
|
659
|
+
"delete",
|
|
660
|
+
"connect",
|
|
661
|
+
"options",
|
|
662
|
+
"trace",
|
|
663
|
+
"patch",
|
|
696
664
|
];
|
|
697
665
|
function parseMethod(method) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
666
|
+
const lowercase = method.toLowerCase();
|
|
667
|
+
if (supportedMethods.includes(method.toLowerCase())) {
|
|
668
|
+
return { tag: lowercase };
|
|
669
|
+
}
|
|
670
|
+
return { tag: "other", val: lowercase };
|
|
703
671
|
}
|
|
704
672
|
|
|
705
673
|
const httpServers = new Map();
|
|
706
674
|
let httpServerCnt = 0;
|
|
707
675
|
export class HTTPServer {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
fieldsFromEntriesChecked(
|
|
735
|
-
headers
|
|
736
|
-
.filter(([key]) => !_forbiddenHeaders.has(key))
|
|
737
|
-
.map(([key, val]) => [
|
|
738
|
-
key,
|
|
739
|
-
textEncoder.encode(val),
|
|
740
|
-
])
|
|
741
|
-
)
|
|
742
|
-
),
|
|
743
|
-
streamId
|
|
744
|
-
);
|
|
745
|
-
let outgoingBodyStreamId;
|
|
746
|
-
const responseOutparam = responseOutparamCreate((response) => {
|
|
747
|
-
if (response.tag === 'ok') {
|
|
748
|
-
const outgoingResponse = response.val;
|
|
749
|
-
const statusCode = outgoingResponse.statusCode();
|
|
750
|
-
const headers = outgoingResponse.headers().entries();
|
|
751
|
-
const body = outgoingResponseBody(outgoingResponse);
|
|
752
|
-
outgoingBodyStreamId = outgoingBodyOutputStreamId(body);
|
|
753
|
-
ioCall(HTTP_SERVER_SET_OUTGOING_RESPONSE, responseId, {
|
|
754
|
-
statusCode,
|
|
755
|
-
headers,
|
|
756
|
-
streamId: outgoingBodyStreamId,
|
|
757
|
-
});
|
|
758
|
-
} else {
|
|
759
|
-
ioCall(
|
|
760
|
-
HTTP_SERVER_CLEAR_OUTGOING_RESPONSE,
|
|
761
|
-
responseId,
|
|
762
|
-
null
|
|
763
|
-
);
|
|
764
|
-
console.error(response.val);
|
|
765
|
-
process.exit(1);
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
incomingHandler.handle(request, responseOutparam);
|
|
769
|
-
if (outgoingBodyStreamId) {
|
|
770
|
-
ioCall(OUTPUT_STREAM_DISPOSE, outgoingBodyStreamId, null);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
676
|
+
#id = ++httpServerCnt;
|
|
677
|
+
#stopped = false;
|
|
678
|
+
#liveEventLoopInterval;
|
|
679
|
+
constructor(incomingHandler) {
|
|
680
|
+
httpServers.set(this.#id, this);
|
|
681
|
+
if (typeof incomingHandler?.handle !== "function") {
|
|
682
|
+
console.error("Not a valid HTTP server component to execute.");
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
registerIncomingHttpHandler(
|
|
686
|
+
this.#id,
|
|
687
|
+
({ method, pathWithQuery, host, headers, responseId, streamId }) => {
|
|
688
|
+
const textEncoder = new TextEncoder();
|
|
689
|
+
const request = incomingRequestCreate(
|
|
690
|
+
parseMethod(method),
|
|
691
|
+
pathWithQuery,
|
|
692
|
+
{ tag: "HTTP" },
|
|
693
|
+
host,
|
|
694
|
+
fieldsLock(
|
|
695
|
+
fieldsFromEntriesChecked(
|
|
696
|
+
headers
|
|
697
|
+
.filter(([key]) => !_forbiddenHeaders.has(key))
|
|
698
|
+
.map(([key, val]) => [key, textEncoder.encode(val)]),
|
|
699
|
+
),
|
|
700
|
+
),
|
|
701
|
+
streamId,
|
|
773
702
|
);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
703
|
+
let outgoingBodyStreamId;
|
|
704
|
+
const responseOutparam = responseOutparamCreate((response) => {
|
|
705
|
+
if (response.tag === "ok") {
|
|
706
|
+
const outgoingResponse = response.val;
|
|
707
|
+
const statusCode = outgoingResponse.statusCode();
|
|
708
|
+
const headers = outgoingResponse.headers().entries();
|
|
709
|
+
const body = outgoingResponseBody(outgoingResponse);
|
|
710
|
+
outgoingBodyStreamId = outgoingBodyOutputStreamId(body);
|
|
711
|
+
ioCall(HTTP_SERVER_SET_OUTGOING_RESPONSE, responseId, {
|
|
712
|
+
statusCode,
|
|
713
|
+
headers,
|
|
714
|
+
streamId: outgoingBodyStreamId,
|
|
715
|
+
});
|
|
716
|
+
} else {
|
|
717
|
+
ioCall(HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, responseId, null);
|
|
718
|
+
console.error(response.val);
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
incomingHandler.handle(request, responseOutparam);
|
|
723
|
+
if (outgoingBodyStreamId) {
|
|
724
|
+
ioCall(OUTPUT_STREAM_DISPOSE, outgoingBodyStreamId, null);
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
listen(port, host) {
|
|
730
|
+
// set a dummy interval, to keep the process alive since the server is off-thread
|
|
731
|
+
this.#liveEventLoopInterval = setInterval(() => {}, 10_000);
|
|
732
|
+
ioCall(HTTP_SERVER_START, this.#id, { port, host });
|
|
733
|
+
}
|
|
734
|
+
address() {
|
|
735
|
+
return ioCall(HTTP_SERVER_GET_ADDRESS, this.#id);
|
|
736
|
+
}
|
|
737
|
+
stop() {
|
|
738
|
+
if (this.#stopped) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
clearInterval(this.#liveEventLoopInterval);
|
|
742
|
+
ioCall(HTTP_SERVER_STOP, this.#id, null);
|
|
743
|
+
httpServers.delete(this.#id);
|
|
744
|
+
this.#stopped = true;
|
|
745
|
+
}
|
|
746
|
+
close() {
|
|
747
|
+
this.stop();
|
|
748
|
+
}
|
|
795
749
|
}
|