@bytecodealliance/preview2-shim 0.17.6 → 0.17.8
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 +48 -0
- package/lib/browser/clocks.js +10 -6
- package/lib/browser/filesystem.js +72 -0
- package/lib/browser/http.js +677 -140
- package/lib/browser/io.js +68 -8
- package/lib/common/instantiation.js +126 -3
- package/lib/nodejs/filesystem.js +39 -0
- package/package.json +1 -1
- package/types/filesystem.d.ts +27 -0
- package/types/instantiation.d.ts +64 -1
package/lib/browser/http.js
CHANGED
|
@@ -1,155 +1,692 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { streams, poll } from './io.js';
|
|
2
|
+
|
|
3
|
+
const { InputStream, OutputStream } = streams;
|
|
4
|
+
const { Pollable } = poll;
|
|
5
|
+
|
|
6
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
7
|
+
const utf8Decoder = new TextDecoder();
|
|
8
|
+
const forbiddenHeaders = new Set(['connection', 'keep-alive', 'host']);
|
|
9
|
+
const DEFAULT_HTTP_TIMEOUT_NS = 600_000_000_000n;
|
|
10
|
+
|
|
11
|
+
// RFC 9110 compliant header validation
|
|
12
|
+
const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
13
|
+
const FIELD_VALUE_RE = /^[\t\x20-\x7E\x80-\xFF]*$/;
|
|
14
|
+
|
|
15
|
+
function validateHeaderName(name) {
|
|
16
|
+
if (!TOKEN_RE.test(name)) {
|
|
17
|
+
throw { tag: 'invalid-syntax' };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function validateHeaderValue(value) {
|
|
22
|
+
const str = typeof value === 'string' ? value : utf8Decoder.decode(value);
|
|
23
|
+
if (!FIELD_VALUE_RE.test(str)) {
|
|
24
|
+
throw { tag: 'invalid-syntax' };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class Fields {
|
|
29
|
+
#immutable = false;
|
|
30
|
+
/** @type {[string, Uint8Array][]} */ #entries = [];
|
|
31
|
+
/** @type {Map<string, [string, Uint8Array][]>} */ #table = new Map();
|
|
32
|
+
|
|
33
|
+
static fromList(entries) {
|
|
34
|
+
const fields = new Fields();
|
|
35
|
+
for (const [key, value] of entries) {
|
|
36
|
+
fields.append(key, value);
|
|
37
|
+
}
|
|
38
|
+
return fields;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get(name) {
|
|
42
|
+
const tableEntries = this.#table.get(name.toLowerCase());
|
|
43
|
+
if (!tableEntries) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
return tableEntries.map(([, v]) => v);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set(name, values) {
|
|
50
|
+
if (this.#immutable) {
|
|
51
|
+
throw { tag: 'immutable' };
|
|
52
|
+
}
|
|
53
|
+
validateHeaderName(name);
|
|
54
|
+
for (const value of values) {
|
|
55
|
+
validateHeaderValue(value);
|
|
56
|
+
}
|
|
57
|
+
const lowercased = name.toLowerCase();
|
|
58
|
+
if (forbiddenHeaders.has(lowercased)) {
|
|
59
|
+
throw { tag: 'forbidden' };
|
|
60
|
+
}
|
|
61
|
+
const tableEntries = this.#table.get(lowercased);
|
|
62
|
+
if (tableEntries) {
|
|
63
|
+
this.#entries = this.#entries.filter(
|
|
64
|
+
(entry) => !tableEntries.includes(entry)
|
|
65
|
+
);
|
|
66
|
+
tableEntries.splice(0, tableEntries.length);
|
|
67
|
+
} else {
|
|
68
|
+
this.#table.set(lowercased, []);
|
|
69
|
+
}
|
|
70
|
+
const newTableEntries = this.#table.get(lowercased);
|
|
71
|
+
for (const value of values) {
|
|
72
|
+
const entry = [name, value];
|
|
73
|
+
this.#entries.push(entry);
|
|
74
|
+
newTableEntries.push(entry);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
has(name) {
|
|
79
|
+
return this.#table.has(name.toLowerCase());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
delete(name) {
|
|
83
|
+
if (this.#immutable) {
|
|
84
|
+
throw { tag: 'immutable' };
|
|
85
|
+
}
|
|
86
|
+
const lowercased = name.toLowerCase();
|
|
87
|
+
const tableEntries = this.#table.get(lowercased);
|
|
88
|
+
if (tableEntries) {
|
|
89
|
+
this.#entries = this.#entries.filter(
|
|
90
|
+
(entry) => !tableEntries.includes(entry)
|
|
91
|
+
);
|
|
92
|
+
this.#table.delete(lowercased);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
append(name, value) {
|
|
97
|
+
if (this.#immutable) {
|
|
98
|
+
throw { tag: 'immutable' };
|
|
99
|
+
}
|
|
100
|
+
validateHeaderName(name);
|
|
101
|
+
validateHeaderValue(value);
|
|
102
|
+
const lowercased = name.toLowerCase();
|
|
103
|
+
if (forbiddenHeaders.has(lowercased)) {
|
|
104
|
+
throw { tag: 'forbidden' };
|
|
105
|
+
}
|
|
106
|
+
const entry = [name, value];
|
|
107
|
+
this.#entries.push(entry);
|
|
108
|
+
const tableEntries = this.#table.get(lowercased);
|
|
109
|
+
if (tableEntries) {
|
|
110
|
+
tableEntries.push(entry);
|
|
111
|
+
} else {
|
|
112
|
+
this.#table.set(lowercased, [entry]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
entries() {
|
|
117
|
+
return this.#entries;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
clone() {
|
|
121
|
+
return fieldsFromEntriesChecked(this.#entries);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static _lock(fields) {
|
|
125
|
+
fields.#immutable = true;
|
|
126
|
+
return fields;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static _fromEntriesChecked(entries) {
|
|
130
|
+
const fields = new Fields();
|
|
131
|
+
fields.#entries = entries;
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const lowercase = entry[0].toLowerCase();
|
|
134
|
+
const existing = fields.#table.get(lowercase);
|
|
135
|
+
if (existing) {
|
|
136
|
+
existing.push(entry);
|
|
137
|
+
} else {
|
|
138
|
+
fields.#table.set(lowercase, [entry]);
|
|
14
139
|
}
|
|
15
140
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
141
|
+
return fields;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const fieldsLock = Fields._lock;
|
|
145
|
+
delete Fields._lock;
|
|
146
|
+
const fieldsFromEntriesChecked = Fields._fromEntriesChecked;
|
|
147
|
+
delete Fields._fromEntriesChecked;
|
|
148
|
+
|
|
149
|
+
class RequestOptions {
|
|
150
|
+
#connectTimeout = DEFAULT_HTTP_TIMEOUT_NS;
|
|
151
|
+
#firstByteTimeout = DEFAULT_HTTP_TIMEOUT_NS;
|
|
152
|
+
#betweenBytesTimeout = DEFAULT_HTTP_TIMEOUT_NS;
|
|
153
|
+
connectTimeout() {
|
|
154
|
+
return this.#connectTimeout;
|
|
155
|
+
}
|
|
156
|
+
setConnectTimeout(duration) {
|
|
157
|
+
if (duration < 0n) {
|
|
158
|
+
throw new Error('duration must not be negative');
|
|
159
|
+
}
|
|
160
|
+
this.#connectTimeout = duration;
|
|
161
|
+
}
|
|
162
|
+
firstByteTimeout() {
|
|
163
|
+
return this.#firstByteTimeout;
|
|
164
|
+
}
|
|
165
|
+
setFirstByteTimeout(duration) {
|
|
166
|
+
if (duration < 0n) {
|
|
167
|
+
throw new Error('duration must not be negative');
|
|
168
|
+
}
|
|
169
|
+
this.#firstByteTimeout = duration;
|
|
170
|
+
}
|
|
171
|
+
betweenBytesTimeout() {
|
|
172
|
+
return this.#betweenBytesTimeout;
|
|
173
|
+
}
|
|
174
|
+
setBetweenBytesTimeout(duration) {
|
|
175
|
+
if (duration < 0n) {
|
|
176
|
+
throw new Error('duration must not be negative');
|
|
177
|
+
}
|
|
178
|
+
this.#betweenBytesTimeout = duration;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class OutgoingBody {
|
|
183
|
+
#outputStream = null;
|
|
184
|
+
#chunks = [];
|
|
185
|
+
#finished = false;
|
|
186
|
+
|
|
187
|
+
write() {
|
|
188
|
+
const outputStream = this.#outputStream;
|
|
189
|
+
if (outputStream === null) {
|
|
190
|
+
throw undefined;
|
|
191
|
+
}
|
|
192
|
+
this.#outputStream = null;
|
|
193
|
+
return outputStream;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static finish(body, trailers) {
|
|
197
|
+
if (trailers) {
|
|
198
|
+
throw { tag: 'internal-error', val: 'trailers unsupported' };
|
|
199
|
+
}
|
|
200
|
+
if (body.#finished) {
|
|
201
|
+
throw { tag: 'internal-error', val: 'body already finished' };
|
|
202
|
+
}
|
|
203
|
+
body.#finished = true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static _bodyData(outgoingBody) {
|
|
207
|
+
if (outgoingBody.#chunks.length === 0) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
let totalLen = 0;
|
|
211
|
+
for (const chunk of outgoingBody.#chunks) {
|
|
212
|
+
totalLen += chunk.byteLength;
|
|
213
|
+
}
|
|
214
|
+
const result = new Uint8Array(totalLen);
|
|
215
|
+
let offset = 0;
|
|
216
|
+
for (const chunk of outgoingBody.#chunks) {
|
|
217
|
+
result.set(chunk, offset);
|
|
218
|
+
offset += chunk.byteLength;
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static _create() {
|
|
224
|
+
const outgoingBody = new OutgoingBody();
|
|
225
|
+
const chunks = outgoingBody.#chunks;
|
|
226
|
+
outgoingBody.#outputStream = new OutputStream({
|
|
227
|
+
write(buf) {
|
|
228
|
+
chunks.push(new Uint8Array(buf));
|
|
229
|
+
},
|
|
230
|
+
flush() {},
|
|
231
|
+
blockingFlush() {},
|
|
232
|
+
subscribe() {
|
|
233
|
+
return new Pollable();
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
return outgoingBody;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
[symbolDispose]() {}
|
|
240
|
+
}
|
|
241
|
+
const outgoingBodyCreate = OutgoingBody._create;
|
|
242
|
+
delete OutgoingBody._create;
|
|
243
|
+
const outgoingBodyData = OutgoingBody._bodyData;
|
|
244
|
+
delete OutgoingBody._bodyData;
|
|
245
|
+
|
|
246
|
+
class OutgoingRequest {
|
|
247
|
+
/** @type {{ tag: string, val?: string }} */ #method = { tag: 'get' };
|
|
248
|
+
/** @type {{ tag: string, val?: string } | undefined} */ #scheme = undefined;
|
|
249
|
+
/** @type {string | undefined} */ #pathWithQuery = undefined;
|
|
250
|
+
/** @type {string | undefined} */ #authority = undefined;
|
|
251
|
+
/** @type {Fields} */ #headers;
|
|
252
|
+
/** @type {OutgoingBody} */ #body;
|
|
253
|
+
#bodyRequested = false;
|
|
254
|
+
|
|
255
|
+
constructor(headers) {
|
|
256
|
+
fieldsLock(headers);
|
|
257
|
+
this.#headers = headers;
|
|
258
|
+
this.#body = outgoingBodyCreate();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
body() {
|
|
262
|
+
if (this.#bodyRequested) {
|
|
263
|
+
throw new Error('Body already requested');
|
|
264
|
+
}
|
|
265
|
+
this.#bodyRequested = true;
|
|
266
|
+
return this.#body;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
method() {
|
|
270
|
+
return this.#method;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
setMethod(method) {
|
|
274
|
+
if (method.tag === 'other' && !method.val.match(/^[a-zA-Z-]+$/)) {
|
|
275
|
+
throw undefined;
|
|
276
|
+
}
|
|
277
|
+
this.#method = method;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
pathWithQuery() {
|
|
281
|
+
return this.#pathWithQuery;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
setPathWithQuery(pathWithQuery) {
|
|
285
|
+
if (
|
|
286
|
+
pathWithQuery &&
|
|
287
|
+
!pathWithQuery.match(/^[a-zA-Z0-9.\-_~!$&'()*+,;=:@%?/]+$/)
|
|
288
|
+
) {
|
|
289
|
+
throw undefined;
|
|
290
|
+
}
|
|
291
|
+
this.#pathWithQuery = pathWithQuery;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
scheme() {
|
|
295
|
+
return this.#scheme;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
setScheme(scheme) {
|
|
299
|
+
if (scheme?.tag === 'other' && !scheme.val.match(/^[a-zA-Z]+$/)) {
|
|
300
|
+
throw undefined;
|
|
301
|
+
}
|
|
302
|
+
this.#scheme = scheme;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
authority() {
|
|
306
|
+
return this.#authority;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
setAuthority(authority) {
|
|
310
|
+
if (authority) {
|
|
311
|
+
const [host, port, ...extra] = authority.split(':');
|
|
312
|
+
const portNum = Number(port);
|
|
313
|
+
if (
|
|
314
|
+
extra.length ||
|
|
315
|
+
(port !== undefined &&
|
|
316
|
+
(portNum.toString() !== port || portNum > 65535)) ||
|
|
317
|
+
!host.match(/^[a-zA-Z0-9-.]+$/)
|
|
318
|
+
) {
|
|
319
|
+
throw undefined;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
this.#authority = authority;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
headers() {
|
|
326
|
+
return this.#headers;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
[symbolDispose]() {}
|
|
330
|
+
|
|
331
|
+
static _handle(request, options) {
|
|
332
|
+
const scheme = schemeString(request.#scheme);
|
|
333
|
+
const method = request.#method.val || request.#method.tag;
|
|
334
|
+
|
|
335
|
+
if (!request.#pathWithQuery) {
|
|
336
|
+
throw { tag: 'HTTP-request-URI-invalid' };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const url = `${scheme}//${request.#authority || ''}${request.#pathWithQuery}`;
|
|
340
|
+
|
|
341
|
+
const headers = new Headers();
|
|
342
|
+
for (const [key, value] of request.#headers.entries()) {
|
|
343
|
+
const lowerKey = key.toLowerCase();
|
|
344
|
+
if (!forbiddenHeaders.has(lowerKey)) {
|
|
345
|
+
headers.set(key, utf8Decoder.decode(value));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const bodyData = outgoingBodyData(request.#body);
|
|
350
|
+
|
|
351
|
+
let timeoutMs = Number(DEFAULT_HTTP_TIMEOUT_NS / 1_000_000n);
|
|
352
|
+
if (options) {
|
|
353
|
+
const ct = options.connectTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
|
|
354
|
+
const fbt = options.firstByteTimeout?.() ?? DEFAULT_HTTP_TIMEOUT_NS;
|
|
355
|
+
const minTimeout = ct < fbt ? ct : fbt;
|
|
356
|
+
timeoutMs = Number(minTimeout / 1_000_000n);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return futureIncomingResponseCreate(
|
|
360
|
+
url, method.toUpperCase(), headers, bodyData, timeoutMs
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const outgoingRequestHandle = OutgoingRequest._handle;
|
|
365
|
+
delete OutgoingRequest._handle;
|
|
366
|
+
|
|
367
|
+
class IncomingBody {
|
|
368
|
+
#finished = false;
|
|
369
|
+
#stream = undefined;
|
|
370
|
+
|
|
371
|
+
stream() {
|
|
372
|
+
if (!this.#stream) {
|
|
373
|
+
throw undefined;
|
|
374
|
+
}
|
|
375
|
+
const stream = this.#stream;
|
|
376
|
+
this.#stream = null;
|
|
377
|
+
return stream;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
static finish(incomingBody) {
|
|
381
|
+
if (incomingBody.#finished) {
|
|
382
|
+
throw new Error('incoming body already finished');
|
|
383
|
+
}
|
|
384
|
+
incomingBody.#finished = true;
|
|
385
|
+
return futureTrailersCreate();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
[symbolDispose]() {}
|
|
389
|
+
|
|
390
|
+
static _create(fetchResponse) {
|
|
391
|
+
const incomingBody = new IncomingBody();
|
|
392
|
+
let buffer = null;
|
|
393
|
+
let bufferOffset = 0;
|
|
394
|
+
let done = false;
|
|
395
|
+
let reader = null;
|
|
396
|
+
let readPromise = null;
|
|
397
|
+
|
|
398
|
+
function ensureReader() {
|
|
399
|
+
if (!reader && fetchResponse.body) {
|
|
400
|
+
reader = fetchResponse.body.getReader();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function startRead() {
|
|
405
|
+
if (readPromise || done) { return; }
|
|
406
|
+
ensureReader();
|
|
407
|
+
if (!reader) {
|
|
408
|
+
done = true;
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
readPromise = reader.read().then(
|
|
412
|
+
(result) => {
|
|
413
|
+
readPromise = null;
|
|
414
|
+
if (result.done) {
|
|
415
|
+
done = true;
|
|
416
|
+
} else {
|
|
417
|
+
buffer = result.value;
|
|
418
|
+
bufferOffset = 0;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
() => {
|
|
422
|
+
readPromise = null;
|
|
423
|
+
done = true;
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
incomingBody.#stream = new InputStream({
|
|
429
|
+
read(len) {
|
|
430
|
+
if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
|
|
431
|
+
throw { tag: 'closed' };
|
|
432
|
+
}
|
|
433
|
+
if (buffer !== null && bufferOffset < buffer.byteLength) {
|
|
434
|
+
const available = buffer.byteLength - bufferOffset;
|
|
435
|
+
const toRead = Math.min(Number(len), available);
|
|
436
|
+
const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
|
|
437
|
+
bufferOffset += toRead;
|
|
438
|
+
if (bufferOffset >= buffer.byteLength) {
|
|
439
|
+
buffer = null;
|
|
440
|
+
bufferOffset = 0;
|
|
441
|
+
if (!done) { startRead(); }
|
|
442
|
+
}
|
|
443
|
+
return slice;
|
|
444
|
+
}
|
|
445
|
+
throw { tag: 'would-block' };
|
|
446
|
+
},
|
|
447
|
+
blockingRead(len) {
|
|
448
|
+
if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
|
|
449
|
+
throw { tag: 'closed' };
|
|
450
|
+
}
|
|
451
|
+
if (buffer !== null && bufferOffset < buffer.byteLength) {
|
|
452
|
+
const available = buffer.byteLength - bufferOffset;
|
|
453
|
+
const toRead = Math.min(Number(len), available);
|
|
454
|
+
const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
|
|
455
|
+
bufferOffset += toRead;
|
|
456
|
+
if (bufferOffset >= buffer.byteLength) {
|
|
457
|
+
buffer = null;
|
|
458
|
+
bufferOffset = 0;
|
|
459
|
+
if (!done) { startRead(); }
|
|
460
|
+
}
|
|
461
|
+
return slice;
|
|
462
|
+
}
|
|
463
|
+
startRead();
|
|
464
|
+
const waitFor = readPromise || Promise.resolve();
|
|
465
|
+
return waitFor.then(() => {
|
|
466
|
+
if (done && (buffer === null || bufferOffset >= buffer.byteLength)) {
|
|
467
|
+
throw { tag: 'closed' };
|
|
468
|
+
}
|
|
469
|
+
if (buffer !== null && bufferOffset < buffer.byteLength) {
|
|
470
|
+
const available = buffer.byteLength - bufferOffset;
|
|
471
|
+
const toRead = Math.min(Number(len), available);
|
|
472
|
+
const slice = buffer.slice(bufferOffset, bufferOffset + toRead);
|
|
473
|
+
bufferOffset += toRead;
|
|
474
|
+
if (bufferOffset >= buffer.byteLength) {
|
|
475
|
+
buffer = null;
|
|
476
|
+
bufferOffset = 0;
|
|
477
|
+
if (!done) { startRead(); }
|
|
478
|
+
}
|
|
479
|
+
return slice;
|
|
480
|
+
}
|
|
481
|
+
throw { tag: 'closed' };
|
|
482
|
+
});
|
|
483
|
+
},
|
|
484
|
+
subscribe() {
|
|
485
|
+
if (done || (buffer !== null && bufferOffset < buffer.byteLength)) {
|
|
486
|
+
return new Pollable();
|
|
487
|
+
}
|
|
488
|
+
startRead();
|
|
489
|
+
if (readPromise) {
|
|
490
|
+
return new Pollable(readPromise);
|
|
491
|
+
}
|
|
492
|
+
return new Pollable();
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
startRead();
|
|
497
|
+
return incomingBody;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const incomingBodyCreate = IncomingBody._create;
|
|
501
|
+
delete IncomingBody._create;
|
|
502
|
+
|
|
503
|
+
class IncomingResponse {
|
|
504
|
+
/** @type {Fields} */ #headers = undefined;
|
|
505
|
+
#status = 0;
|
|
506
|
+
/** @type {IncomingBody} */ #body;
|
|
507
|
+
|
|
508
|
+
status() {
|
|
509
|
+
return this.#status;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
headers() {
|
|
513
|
+
return this.#headers;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
consume() {
|
|
517
|
+
if (this.#body === undefined) {
|
|
518
|
+
throw undefined;
|
|
519
|
+
}
|
|
520
|
+
const body = this.#body;
|
|
521
|
+
this.#body = undefined;
|
|
522
|
+
return body;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
[symbolDispose]() {}
|
|
526
|
+
|
|
527
|
+
static _create(fetchResponse) {
|
|
528
|
+
const res = new IncomingResponse();
|
|
529
|
+
res.#status = fetchResponse.status;
|
|
530
|
+
|
|
531
|
+
const headerEntries = [];
|
|
532
|
+
const encoder = new TextEncoder();
|
|
533
|
+
fetchResponse.headers.forEach((value, key) => {
|
|
534
|
+
headerEntries.push([key, encoder.encode(value)]);
|
|
535
|
+
});
|
|
536
|
+
res.#headers = fieldsLock(fieldsFromEntriesChecked(headerEntries));
|
|
537
|
+
res.#body = incomingBodyCreate(fetchResponse);
|
|
538
|
+
return res;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const incomingResponseCreate = IncomingResponse._create;
|
|
542
|
+
delete IncomingResponse._create;
|
|
543
|
+
|
|
544
|
+
class FutureTrailers {
|
|
545
|
+
#requested = false;
|
|
546
|
+
subscribe() {
|
|
547
|
+
return new Pollable();
|
|
548
|
+
}
|
|
549
|
+
get() {
|
|
550
|
+
if (this.#requested) {
|
|
551
|
+
return { tag: 'err' };
|
|
552
|
+
}
|
|
553
|
+
this.#requested = true;
|
|
30
554
|
return {
|
|
31
|
-
|
|
555
|
+
tag: 'ok',
|
|
556
|
+
val: {
|
|
557
|
+
tag: 'ok',
|
|
558
|
+
val: undefined,
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
static _create() {
|
|
563
|
+
return new FutureTrailers();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const futureTrailersCreate = FutureTrailers._create;
|
|
567
|
+
delete FutureTrailers._create;
|
|
568
|
+
|
|
569
|
+
function mapFetchError(err) {
|
|
570
|
+
if (err.name === 'AbortError') {
|
|
571
|
+
return { tag: 'connection-timeout' };
|
|
572
|
+
}
|
|
573
|
+
if (err.name === 'TypeError') {
|
|
574
|
+
return { tag: 'connection-refused' };
|
|
575
|
+
}
|
|
576
|
+
return { tag: 'internal-error', val: err.message };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
class FutureIncomingResponse {
|
|
580
|
+
#result = undefined;
|
|
581
|
+
#promise = null;
|
|
582
|
+
|
|
583
|
+
subscribe() {
|
|
584
|
+
return new Pollable(this.#promise);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
get() {
|
|
588
|
+
if (this.#result === undefined) {
|
|
589
|
+
return undefined;
|
|
590
|
+
}
|
|
591
|
+
const result = this.#result;
|
|
592
|
+
this.#result = { tag: 'err' };
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
[symbolDispose]() {
|
|
597
|
+
this.#promise = null;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
static _create(url, method, headers, bodyData, timeoutMs) {
|
|
601
|
+
const future = new FutureIncomingResponse();
|
|
602
|
+
|
|
603
|
+
const controller = new AbortController();
|
|
604
|
+
let timer;
|
|
605
|
+
if (timeoutMs < Infinity) {
|
|
606
|
+
timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const init = {
|
|
610
|
+
method,
|
|
32
611
|
headers,
|
|
33
|
-
|
|
612
|
+
signal: controller.signal,
|
|
34
613
|
};
|
|
35
|
-
|
|
36
|
-
|
|
614
|
+
if (bodyData && method !== 'GET' && method !== 'HEAD') {
|
|
615
|
+
init.body = bodyData;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
future.#promise = fetch(url, init).then(
|
|
619
|
+
(response) => {
|
|
620
|
+
if (timer) { clearTimeout(timer); }
|
|
621
|
+
future.#result = {
|
|
622
|
+
tag: 'ok',
|
|
623
|
+
val: {
|
|
624
|
+
tag: 'ok',
|
|
625
|
+
val: incomingResponseCreate(response),
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
},
|
|
629
|
+
(err) => {
|
|
630
|
+
if (timer) { clearTimeout(timer); }
|
|
631
|
+
future.#result = {
|
|
632
|
+
tag: 'ok',
|
|
633
|
+
val: {
|
|
634
|
+
tag: 'err',
|
|
635
|
+
val: mapFetchError(err),
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
return future;
|
|
37
642
|
}
|
|
38
643
|
}
|
|
644
|
+
const futureIncomingResponseCreate = FutureIncomingResponse._create;
|
|
645
|
+
delete FutureIncomingResponse._create;
|
|
39
646
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
647
|
+
function schemeString(scheme) {
|
|
648
|
+
if (!scheme) {
|
|
649
|
+
return 'https:';
|
|
650
|
+
}
|
|
651
|
+
switch (scheme.tag) {
|
|
652
|
+
case 'HTTP':
|
|
653
|
+
return 'http:';
|
|
654
|
+
case 'HTTPS':
|
|
655
|
+
return 'https:';
|
|
656
|
+
case 'other':
|
|
657
|
+
return scheme.val.toLowerCase() + ':';
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function httpErrorCode(err) {
|
|
662
|
+
if (err.payload) {
|
|
663
|
+
return err.payload;
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
tag: 'internal-error',
|
|
667
|
+
val: err.message,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
43
670
|
|
|
44
671
|
export const outgoingHandler = {
|
|
672
|
+
handle: outgoingRequestHandle,
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
export const incomingHandler = {
|
|
45
676
|
handle() {},
|
|
46
677
|
};
|
|
47
678
|
|
|
48
679
|
export const types = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
fieldsDelete(_fields, _name) {
|
|
62
|
-
console.log('[types] Fields delete');
|
|
63
|
-
},
|
|
64
|
-
fieldsAppend(_fields, _name, _value) {
|
|
65
|
-
console.log('[types] Fields append');
|
|
66
|
-
},
|
|
67
|
-
fieldsEntries(_fields) {
|
|
68
|
-
console.log('[types] Fields entries');
|
|
69
|
-
},
|
|
70
|
-
fieldsClone(_fields) {
|
|
71
|
-
console.log('[types] Fields clone');
|
|
72
|
-
},
|
|
73
|
-
finishIncomingStream(s) {
|
|
74
|
-
console.log(`[types] Finish incoming stream ${s}`);
|
|
75
|
-
},
|
|
76
|
-
finishOutgoingStream(s, _trailers) {
|
|
77
|
-
console.log(`[types] Finish outgoing stream ${s}`);
|
|
78
|
-
},
|
|
79
|
-
dropIncomingRequest(_req) {
|
|
80
|
-
console.log('[types] Drop incoming request');
|
|
81
|
-
},
|
|
82
|
-
dropOutgoingRequest(_req) {
|
|
83
|
-
console.log('[types] Drop outgoing request');
|
|
84
|
-
},
|
|
85
|
-
incomingRequestMethod(_req) {
|
|
86
|
-
console.log('[types] Incoming request method');
|
|
87
|
-
},
|
|
88
|
-
incomingRequestPathWithQuery(_req) {
|
|
89
|
-
console.log('[types] Incoming request path with query');
|
|
90
|
-
},
|
|
91
|
-
incomingRequestScheme(_req) {
|
|
92
|
-
console.log('[types] Incoming request scheme');
|
|
93
|
-
},
|
|
94
|
-
incomingRequestAuthority(_req) {
|
|
95
|
-
console.log('[types] Incoming request authority');
|
|
96
|
-
},
|
|
97
|
-
incomingRequestHeaders(_req) {
|
|
98
|
-
console.log('[types] Incoming request headers');
|
|
99
|
-
},
|
|
100
|
-
incomingRequestConsume(_req) {
|
|
101
|
-
console.log('[types] Incoming request consume');
|
|
102
|
-
},
|
|
103
|
-
newOutgoingRequest(_method, _pathWithQuery, _scheme, _authority, _headers) {
|
|
104
|
-
console.log('[types] New outgoing request');
|
|
105
|
-
},
|
|
106
|
-
outgoingRequestWrite(_req) {
|
|
107
|
-
console.log('[types] Outgoing request write');
|
|
108
|
-
},
|
|
109
|
-
dropResponseOutparam(_res) {
|
|
110
|
-
console.log('[types] Drop response outparam');
|
|
111
|
-
},
|
|
112
|
-
setResponseOutparam(_response) {
|
|
113
|
-
console.log('[types] Drop fields');
|
|
114
|
-
},
|
|
115
|
-
dropIncomingResponse(_res) {
|
|
116
|
-
console.log('[types] Drop incoming response');
|
|
117
|
-
},
|
|
118
|
-
dropOutgoingResponse(_res) {
|
|
119
|
-
console.log('[types] Drop outgoing response');
|
|
120
|
-
},
|
|
121
|
-
incomingResponseStatus(_res) {
|
|
122
|
-
console.log('[types] Incoming response status');
|
|
123
|
-
},
|
|
124
|
-
incomingResponseHeaders(_res) {
|
|
125
|
-
console.log('[types] Incoming response headers');
|
|
126
|
-
},
|
|
127
|
-
incomingResponseConsume(_res) {
|
|
128
|
-
console.log('[types] Incoming response consume');
|
|
129
|
-
},
|
|
130
|
-
newOutgoingResponse(_statusCode, _headers) {
|
|
131
|
-
console.log('[types] New outgoing response');
|
|
132
|
-
},
|
|
133
|
-
outgoingResponseWrite(_res) {
|
|
134
|
-
console.log('[types] Outgoing response write');
|
|
135
|
-
},
|
|
136
|
-
dropFutureIncomingResponse(_f) {
|
|
137
|
-
console.log('[types] Drop future incoming response');
|
|
138
|
-
},
|
|
139
|
-
futureIncomingResponseGet(_f) {
|
|
140
|
-
console.log('[types] Future incoming response get');
|
|
141
|
-
},
|
|
142
|
-
listenToFutureIncomingResponse(_f) {
|
|
143
|
-
console.log('[types] Listen to future incoming response');
|
|
144
|
-
},
|
|
145
|
-
Fields: class Fields {},
|
|
146
|
-
FutureIncomingResponse: new class FutureIncomingResponse {},
|
|
147
|
-
IncomingBody: new class IncomingBody {},
|
|
148
|
-
IncomingRequest: new class IncomingRequest {},
|
|
149
|
-
IncomingResponse: new class IncomingResponse {},
|
|
150
|
-
OutgoingBody: new class OutgoingBody {},
|
|
151
|
-
OutgoingRequest: new class OutgoingRequest {},
|
|
152
|
-
OutgoingResponse: new class OutgoingResponse {},
|
|
153
|
-
RequestOptions: new class RequestOptions {},
|
|
154
|
-
ResponseOutparam: new class ResponseOutparam {},
|
|
680
|
+
Fields,
|
|
681
|
+
FutureIncomingResponse,
|
|
682
|
+
FutureTrailers,
|
|
683
|
+
IncomingBody,
|
|
684
|
+
IncomingRequest: class IncomingRequest {},
|
|
685
|
+
IncomingResponse,
|
|
686
|
+
OutgoingBody,
|
|
687
|
+
OutgoingRequest,
|
|
688
|
+
OutgoingResponse: class OutgoingResponse {},
|
|
689
|
+
ResponseOutparam: class ResponseOutparam {},
|
|
690
|
+
RequestOptions,
|
|
691
|
+
httpErrorCode,
|
|
155
692
|
};
|