@bytecodealliance/preview3-shim 0.6.1 → 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/package.json +3 -3
- package/dist/common/errors.d.ts +0 -3
- package/dist/common/errors.js +0 -5
|
@@ -1,6 +1,141 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import * as preview2Http from "@bytecodealliance/preview2-shim/http";
|
|
2
|
+
import { readableFromPreview2Input, writeToPreview2Output } from "../streams.js";
|
|
3
|
+
import { Fields, Request, Response } from "./types.js";
|
|
4
|
+
class HttpError extends Error {
|
|
5
|
+
payload;
|
|
6
|
+
constructor(payload, message) {
|
|
7
|
+
super(message ?? payload.tag);
|
|
8
|
+
this.name = "HttpError";
|
|
9
|
+
this.payload = payload;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function internalError(error) {
|
|
13
|
+
return {
|
|
14
|
+
tag: "internal-error",
|
|
15
|
+
val: error instanceof Error ? error.message : String(error),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function preview2Options(options) {
|
|
19
|
+
if (!options) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const converted = new preview2Http.types.RequestOptions();
|
|
23
|
+
converted.setConnectTimeout(options.getConnectTimeout());
|
|
24
|
+
converted.setFirstByteTimeout(options.getFirstByteTimeout());
|
|
25
|
+
converted.setBetweenBytesTimeout(options.getBetweenBytesTimeout());
|
|
26
|
+
return converted;
|
|
27
|
+
}
|
|
28
|
+
async function responseTrailers(incomingBody, bodyResult) {
|
|
29
|
+
const completed = await bodyResult;
|
|
30
|
+
if (completed.tag === "err") {
|
|
31
|
+
return completed;
|
|
32
|
+
}
|
|
33
|
+
const future = preview2Http.types.IncomingBody.finish(incomingBody);
|
|
34
|
+
await future.subscribe().block();
|
|
35
|
+
const result = future.get();
|
|
36
|
+
if (!result || result.tag === "err") {
|
|
37
|
+
return { tag: "err", val: internalError("response trailers were already consumed") };
|
|
38
|
+
}
|
|
39
|
+
if (result.val.tag === "err") {
|
|
40
|
+
return { tag: "err", val: result.val.val };
|
|
41
|
+
}
|
|
42
|
+
const fields = result.val.val;
|
|
43
|
+
return {
|
|
44
|
+
tag: "ok",
|
|
45
|
+
val: fields ? new Fields(fields) : undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function trailerEntries(trailers) {
|
|
49
|
+
if (trailers?.tag === "none") {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
if (trailers?.tag === "some") {
|
|
53
|
+
return trailerEntries(trailers.val);
|
|
54
|
+
}
|
|
55
|
+
if (trailers instanceof Fields) {
|
|
56
|
+
return trailers.copyAll();
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(trailers)) {
|
|
59
|
+
return trailers;
|
|
60
|
+
}
|
|
61
|
+
throw new TypeError("trailers must be Fields or a list of entries");
|
|
62
|
+
}
|
|
63
|
+
async function send(request) {
|
|
64
|
+
if (!(request instanceof Request)) {
|
|
65
|
+
throw new TypeError("request must be a Request resource");
|
|
66
|
+
}
|
|
67
|
+
const outgoing = new preview2Http.types.OutgoingRequest(request.getHeaders()._preview2.clone());
|
|
68
|
+
outgoing.setMethod(request.getMethod());
|
|
69
|
+
outgoing.setScheme(request.getScheme() ?? { tag: "HTTP" });
|
|
70
|
+
outgoing.setAuthority(request.getAuthority());
|
|
71
|
+
outgoing.setPathWithQuery(request.getPathWithQuery());
|
|
72
|
+
let transmit = Promise.resolve({ tag: "ok", val: undefined });
|
|
73
|
+
const contents = request._body();
|
|
74
|
+
if (contents) {
|
|
75
|
+
const outgoingBody = outgoing.body();
|
|
76
|
+
transmit = (async () => {
|
|
77
|
+
let result = { tag: "ok", val: undefined };
|
|
78
|
+
try {
|
|
79
|
+
await writeToPreview2Output(contents, outgoingBody.write());
|
|
80
|
+
const trailers = await request._trailers();
|
|
81
|
+
if (trailers.tag === "err") {
|
|
82
|
+
result = trailers;
|
|
83
|
+
}
|
|
84
|
+
else if (trailers.val && trailerEntries(trailers.val).length > 0) {
|
|
85
|
+
result = {
|
|
86
|
+
tag: "err",
|
|
87
|
+
val: {
|
|
88
|
+
tag: "internal-error",
|
|
89
|
+
val: "browser Fetch does not support request trailers",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
result = { tag: "err", val: internalError(error) };
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
try {
|
|
99
|
+
preview2Http.types.OutgoingBody.finish(outgoingBody, undefined);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (result.tag === "ok") {
|
|
103
|
+
result = { tag: "err", val: internalError(error) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
})();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
transmit = request
|
|
112
|
+
._trailers()
|
|
113
|
+
.then((trailers) => (trailers.tag === "err" ? trailers : { tag: "ok", val: undefined }));
|
|
114
|
+
}
|
|
115
|
+
const future = preview2Http.outgoingHandler.handle(outgoing, preview2Options(request.getOptions()));
|
|
116
|
+
void transmit.then((result) => request._resolve(result));
|
|
117
|
+
try {
|
|
118
|
+
await future.subscribe().block();
|
|
119
|
+
const result = future.get();
|
|
120
|
+
if (!result || result.tag === "err") {
|
|
121
|
+
throw new HttpError(internalError("HTTP response future was already consumed"));
|
|
122
|
+
}
|
|
123
|
+
if (result.val.tag === "err") {
|
|
124
|
+
throw new HttpError(result.val.val);
|
|
125
|
+
}
|
|
126
|
+
const incoming = result.val.val;
|
|
127
|
+
const headers = new Fields(incoming.headers().clone());
|
|
128
|
+
const incomingBody = incoming.consume();
|
|
129
|
+
const [contents, bodyResult] = readableFromPreview2Input(incomingBody.stream(), internalError);
|
|
130
|
+
const [response] = Response.new(headers, contents, responseTrailers(incomingBody, bodyResult));
|
|
131
|
+
response.setStatusCode(incoming.status());
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const httpError = error instanceof HttpError ? error : new HttpError(internalError(error));
|
|
136
|
+
request._resolve({ tag: "err", val: httpError.payload });
|
|
137
|
+
throw httpError;
|
|
138
|
+
}
|
|
4
139
|
}
|
|
5
140
|
export default {
|
|
6
141
|
send,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Request, Response } from "../../../types/interfaces/wasi-http-handler.d.ts";
|
|
2
|
+
type Handler = {
|
|
3
|
+
handle(request: Request): Promise<Response> | Response;
|
|
4
|
+
};
|
|
2
5
|
declare function handle(request: Request): Promise<Response>;
|
|
6
|
+
export declare function _setHandler(handler: Handler | undefined): Handler | undefined;
|
|
3
7
|
declare const _default: {
|
|
4
8
|
handle: typeof handle;
|
|
5
9
|
};
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
function handle(request) {
|
|
3
|
-
|
|
1
|
+
let currentHandler;
|
|
2
|
+
async function handle(request) {
|
|
3
|
+
if (!currentHandler) {
|
|
4
|
+
throw new Error("wasi:http/handler import is not configured");
|
|
5
|
+
}
|
|
6
|
+
return currentHandler.handle(request);
|
|
7
|
+
}
|
|
8
|
+
export function _setHandler(handler) {
|
|
9
|
+
const previous = currentHandler;
|
|
10
|
+
currentHandler = handler;
|
|
11
|
+
return previous;
|
|
4
12
|
}
|
|
5
13
|
export default {
|
|
6
14
|
handle,
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { types as preview2Types } from "@bytecodealliance/preview2-shim/http";
|
|
2
|
+
import type { Fields as FieldsT, Request as RequestT, RequestOptions as RequestOptionsT, Response as ResponseT, FieldName, FieldValue, Headers, Method, Scheme, Duration, StatusCode, Trailers, HeaderError, ErrorCode, Result } from "../../../types/interfaces/wasi-http-types.d.ts";
|
|
3
|
+
type Preview2Fields = InstanceType<typeof preview2Types.Fields>;
|
|
4
|
+
export declare class HttpError extends Error {
|
|
5
|
+
readonly payload: HeaderError;
|
|
6
|
+
constructor(payload: HeaderError, message?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare const _forbiddenHeaders: {
|
|
9
|
+
value: Set<string>;
|
|
10
|
+
};
|
|
11
|
+
export declare class Fields implements FieldsT {
|
|
12
|
+
#private;
|
|
13
|
+
readonly _preview2: Preview2Fields;
|
|
14
|
+
constructor(fields?: import("@bytecodealliance/preview2-shim/interfaces/wasi-http-types").Fields);
|
|
15
|
+
static fromList(entries: Array<[FieldName, FieldValue]>): Fields;
|
|
4
16
|
get(name: FieldName): Array<FieldValue>;
|
|
5
17
|
has(name: FieldName): boolean;
|
|
6
18
|
set(name: FieldName, value: Array<FieldValue>): void;
|
|
@@ -8,10 +20,25 @@ declare class Fields implements FieldsT {
|
|
|
8
20
|
getAndDelete(name: FieldName): Array<FieldValue>;
|
|
9
21
|
append(name: FieldName, value: FieldValue): void;
|
|
10
22
|
copyAll(): Array<[FieldName, FieldValue]>;
|
|
11
|
-
clone():
|
|
23
|
+
clone(): Fields;
|
|
24
|
+
static _lock(fields: Fields): Fields;
|
|
25
|
+
}
|
|
26
|
+
export declare class RequestOptions implements RequestOptionsT {
|
|
27
|
+
#private;
|
|
28
|
+
getConnectTimeout(): Duration | undefined;
|
|
29
|
+
setConnectTimeout(duration: Duration | undefined): void;
|
|
30
|
+
getFirstByteTimeout(): Duration | undefined;
|
|
31
|
+
setFirstByteTimeout(duration: Duration | undefined): void;
|
|
32
|
+
getBetweenBytesTimeout(): Duration | undefined;
|
|
33
|
+
setBetweenBytesTimeout(duration: Duration | undefined): void;
|
|
34
|
+
clone(): RequestOptions;
|
|
35
|
+
static _lock(options: RequestOptions | undefined): RequestOptions | undefined;
|
|
12
36
|
}
|
|
13
|
-
|
|
14
|
-
|
|
37
|
+
type TransmissionResult = Result<void, ErrorCode>;
|
|
38
|
+
type TrailersResult = Result<Trailers | undefined, ErrorCode>;
|
|
39
|
+
export declare class Request implements RequestT {
|
|
40
|
+
#private;
|
|
41
|
+
static new(headers: Headers, contents: ReadableStream<number> | undefined, trailers: Promise<TrailersResult>, options: RequestOptions | undefined): [Request, Promise<TransmissionResult>];
|
|
15
42
|
getMethod(): Method;
|
|
16
43
|
setMethod(method: Method): void;
|
|
17
44
|
getPathWithQuery(): string | undefined;
|
|
@@ -20,25 +47,20 @@ declare class Request implements RequestT {
|
|
|
20
47
|
setScheme(scheme: Scheme | undefined): void;
|
|
21
48
|
getAuthority(): string | undefined;
|
|
22
49
|
setAuthority(authority: string | undefined): void;
|
|
23
|
-
getOptions():
|
|
24
|
-
getHeaders():
|
|
25
|
-
static consumeBody(this_: Request, res: Promise<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setConnectTimeout(duration: Duration | undefined): void;
|
|
30
|
-
getFirstByteTimeout(): Duration | undefined;
|
|
31
|
-
setFirstByteTimeout(duration: Duration | undefined): void;
|
|
32
|
-
getBetweenBytesTimeout(): Duration | undefined;
|
|
33
|
-
setBetweenBytesTimeout(duration: Duration | undefined): void;
|
|
34
|
-
clone(): RequestOptionsT;
|
|
50
|
+
getOptions(): RequestOptions | undefined;
|
|
51
|
+
getHeaders(): Fields;
|
|
52
|
+
static consumeBody(this_: Request, res: Promise<TransmissionResult>): [ReadableStream<number>, Promise<TrailersResult>];
|
|
53
|
+
_body(): ReadableStream<number> | undefined;
|
|
54
|
+
_trailers(): Promise<TrailersResult>;
|
|
55
|
+
_resolve(result: TransmissionResult): void;
|
|
35
56
|
}
|
|
36
|
-
declare class Response implements ResponseT {
|
|
37
|
-
|
|
57
|
+
export declare class Response implements ResponseT {
|
|
58
|
+
#private;
|
|
59
|
+
static new(headers: Headers, contents: ReadableStream<number> | undefined, trailers: Promise<TrailersResult>): [Response, Promise<TransmissionResult>];
|
|
38
60
|
getStatusCode(): StatusCode;
|
|
39
61
|
setStatusCode(statusCode: StatusCode): void;
|
|
40
|
-
getHeaders():
|
|
41
|
-
static consumeBody(this_: Response, res: Promise<
|
|
62
|
+
getHeaders(): Fields;
|
|
63
|
+
static consumeBody(this_: Response, res: Promise<TransmissionResult>): [ReadableStream<number>, Promise<TrailersResult>];
|
|
42
64
|
}
|
|
43
65
|
declare const _default: {
|
|
44
66
|
Fields: typeof Fields;
|
|
@@ -1,109 +1,251 @@
|
|
|
1
|
-
import {
|
|
2
|
-
class
|
|
1
|
+
import { types as preview2Types } from "@bytecodealliance/preview2-shim/http";
|
|
2
|
+
export class HttpError extends Error {
|
|
3
|
+
payload;
|
|
4
|
+
constructor(payload, message) {
|
|
5
|
+
super(message ?? payload.tag);
|
|
6
|
+
this.name = "HttpError";
|
|
7
|
+
this.payload = payload;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const _forbiddenHeaders = {
|
|
11
|
+
value: new Set([
|
|
12
|
+
"connection",
|
|
13
|
+
"http2-settings",
|
|
14
|
+
"host",
|
|
15
|
+
"keep-alive",
|
|
16
|
+
"proxy-authenticate",
|
|
17
|
+
"proxy-authorization",
|
|
18
|
+
"proxy-connection",
|
|
19
|
+
"transfer-encoding",
|
|
20
|
+
"upgrade",
|
|
21
|
+
]),
|
|
22
|
+
};
|
|
23
|
+
function rejectForbidden(name) {
|
|
24
|
+
if (_forbiddenHeaders.value.has(name.toLowerCase())) {
|
|
25
|
+
throw new HttpError({ tag: "forbidden" }, `Header ${name} is forbidden`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class Fields {
|
|
29
|
+
_preview2;
|
|
30
|
+
#immutable = false;
|
|
31
|
+
constructor(fields = new preview2Types.Fields()) {
|
|
32
|
+
this._preview2 = fields;
|
|
33
|
+
}
|
|
3
34
|
static fromList(entries) {
|
|
4
|
-
|
|
35
|
+
for (const [name] of entries) {
|
|
36
|
+
rejectForbidden(name);
|
|
37
|
+
}
|
|
38
|
+
return new Fields(preview2Types.Fields.fromList(entries));
|
|
5
39
|
}
|
|
6
40
|
get(name) {
|
|
7
|
-
|
|
41
|
+
return this._preview2.get(name);
|
|
8
42
|
}
|
|
9
43
|
has(name) {
|
|
10
|
-
|
|
44
|
+
return this._preview2.has(name);
|
|
11
45
|
}
|
|
12
46
|
set(name, value) {
|
|
13
|
-
|
|
47
|
+
this.#ensureMutable();
|
|
48
|
+
rejectForbidden(name);
|
|
49
|
+
this._preview2.set(name, value);
|
|
14
50
|
}
|
|
15
51
|
delete(name) {
|
|
16
|
-
|
|
52
|
+
this.#ensureMutable();
|
|
53
|
+
rejectForbidden(name);
|
|
54
|
+
this._preview2.delete(name);
|
|
17
55
|
}
|
|
18
56
|
getAndDelete(name) {
|
|
19
|
-
|
|
57
|
+
const values = this.get(name);
|
|
58
|
+
this.delete(name);
|
|
59
|
+
return values;
|
|
20
60
|
}
|
|
21
61
|
append(name, value) {
|
|
22
|
-
|
|
62
|
+
this.#ensureMutable();
|
|
63
|
+
rejectForbidden(name);
|
|
64
|
+
this._preview2.append(name, value);
|
|
23
65
|
}
|
|
24
66
|
copyAll() {
|
|
25
|
-
|
|
67
|
+
return this._preview2.entries().map(([name, value]) => [name, value.slice()]);
|
|
68
|
+
}
|
|
69
|
+
clone() {
|
|
70
|
+
return new Fields(this._preview2.clone());
|
|
71
|
+
}
|
|
72
|
+
static _lock(fields) {
|
|
73
|
+
fields.#immutable = true;
|
|
74
|
+
return fields;
|
|
75
|
+
}
|
|
76
|
+
#ensureMutable() {
|
|
77
|
+
if (this.#immutable) {
|
|
78
|
+
throw new HttpError({ tag: "immutable" }, "Cannot modify immutable fields");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class RequestOptions {
|
|
83
|
+
#connectTimeout;
|
|
84
|
+
#firstByteTimeout;
|
|
85
|
+
#betweenBytesTimeout;
|
|
86
|
+
#immutable = false;
|
|
87
|
+
getConnectTimeout() {
|
|
88
|
+
return this.#connectTimeout;
|
|
89
|
+
}
|
|
90
|
+
setConnectTimeout(duration) {
|
|
91
|
+
this.#ensureMutable();
|
|
92
|
+
this.#connectTimeout = duration;
|
|
93
|
+
}
|
|
94
|
+
getFirstByteTimeout() {
|
|
95
|
+
return this.#firstByteTimeout;
|
|
96
|
+
}
|
|
97
|
+
setFirstByteTimeout(duration) {
|
|
98
|
+
this.#ensureMutable();
|
|
99
|
+
this.#firstByteTimeout = duration;
|
|
100
|
+
}
|
|
101
|
+
getBetweenBytesTimeout() {
|
|
102
|
+
return this.#betweenBytesTimeout;
|
|
103
|
+
}
|
|
104
|
+
setBetweenBytesTimeout(duration) {
|
|
105
|
+
this.#ensureMutable();
|
|
106
|
+
this.#betweenBytesTimeout = duration;
|
|
26
107
|
}
|
|
27
108
|
clone() {
|
|
28
|
-
|
|
109
|
+
const options = new RequestOptions();
|
|
110
|
+
options.#connectTimeout = this.#connectTimeout;
|
|
111
|
+
options.#firstByteTimeout = this.#firstByteTimeout;
|
|
112
|
+
options.#betweenBytesTimeout = this.#betweenBytesTimeout;
|
|
113
|
+
return options;
|
|
114
|
+
}
|
|
115
|
+
static _lock(options) {
|
|
116
|
+
if (options) {
|
|
117
|
+
options.#immutable = true;
|
|
118
|
+
}
|
|
119
|
+
return options;
|
|
120
|
+
}
|
|
121
|
+
#ensureMutable() {
|
|
122
|
+
if (this.#immutable) {
|
|
123
|
+
throw new HttpError({ tag: "immutable" }, "Cannot modify immutable request options");
|
|
124
|
+
}
|
|
29
125
|
}
|
|
30
126
|
}
|
|
31
|
-
|
|
127
|
+
function emptyByteStream() {
|
|
128
|
+
return new ReadableStream({
|
|
129
|
+
start(controller) {
|
|
130
|
+
controller.close();
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
export class Request {
|
|
135
|
+
#method = { tag: "get" };
|
|
136
|
+
#pathWithQuery;
|
|
137
|
+
#scheme;
|
|
138
|
+
#authority;
|
|
139
|
+
#headers;
|
|
140
|
+
#contents;
|
|
141
|
+
#trailers;
|
|
142
|
+
#options;
|
|
143
|
+
#resolveTransmission;
|
|
144
|
+
#consumed = false;
|
|
145
|
+
#transmissionResolved = false;
|
|
32
146
|
static new(headers, contents, trailers, options) {
|
|
33
|
-
|
|
147
|
+
if (!(headers instanceof Fields)) {
|
|
148
|
+
throw new TypeError("headers must be a Fields resource");
|
|
149
|
+
}
|
|
150
|
+
if (options !== undefined && !(options instanceof RequestOptions)) {
|
|
151
|
+
throw new TypeError("options must be a RequestOptions resource");
|
|
152
|
+
}
|
|
153
|
+
const request = new Request();
|
|
154
|
+
request.#headers = headers;
|
|
155
|
+
request.#contents = contents;
|
|
156
|
+
request.#trailers = Promise.resolve(trailers);
|
|
157
|
+
request.#options = RequestOptions._lock(options);
|
|
158
|
+
Fields._lock(headers);
|
|
159
|
+
const transmission = new Promise((resolve) => (request.#resolveTransmission = resolve));
|
|
160
|
+
return [request, transmission];
|
|
34
161
|
}
|
|
35
162
|
getMethod() {
|
|
36
|
-
|
|
163
|
+
return this.#method;
|
|
37
164
|
}
|
|
38
165
|
setMethod(method) {
|
|
39
|
-
|
|
166
|
+
this.#method = method;
|
|
40
167
|
}
|
|
41
168
|
getPathWithQuery() {
|
|
42
|
-
|
|
169
|
+
return this.#pathWithQuery;
|
|
43
170
|
}
|
|
44
171
|
setPathWithQuery(pathWithQuery) {
|
|
45
|
-
|
|
172
|
+
this.#pathWithQuery = pathWithQuery;
|
|
46
173
|
}
|
|
47
174
|
getScheme() {
|
|
48
|
-
|
|
175
|
+
return this.#scheme;
|
|
49
176
|
}
|
|
50
177
|
setScheme(scheme) {
|
|
51
|
-
|
|
178
|
+
this.#scheme = scheme;
|
|
52
179
|
}
|
|
53
180
|
getAuthority() {
|
|
54
|
-
|
|
181
|
+
return this.#authority;
|
|
55
182
|
}
|
|
56
183
|
setAuthority(authority) {
|
|
57
|
-
|
|
184
|
+
this.#authority = authority;
|
|
58
185
|
}
|
|
59
186
|
getOptions() {
|
|
60
|
-
|
|
187
|
+
return this.#options;
|
|
61
188
|
}
|
|
62
189
|
getHeaders() {
|
|
63
|
-
|
|
190
|
+
return this.#headers;
|
|
64
191
|
}
|
|
65
192
|
static consumeBody(this_, res) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
193
|
+
if (this_.#consumed) {
|
|
194
|
+
throw new Error("request body has already been consumed");
|
|
195
|
+
}
|
|
196
|
+
this_.#consumed = true;
|
|
197
|
+
void Promise.resolve(res).catch(() => { });
|
|
198
|
+
return [this_.#contents ?? emptyByteStream(), this_.#trailers];
|
|
72
199
|
}
|
|
73
|
-
|
|
74
|
-
|
|
200
|
+
_body() {
|
|
201
|
+
return this.#contents;
|
|
75
202
|
}
|
|
76
|
-
|
|
77
|
-
|
|
203
|
+
_trailers() {
|
|
204
|
+
return this.#trailers;
|
|
78
205
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
setBetweenBytesTimeout(duration) {
|
|
86
|
-
throw new Todo();
|
|
87
|
-
}
|
|
88
|
-
clone() {
|
|
89
|
-
throw new Todo();
|
|
206
|
+
_resolve(result) {
|
|
207
|
+
if (!this.#transmissionResolved) {
|
|
208
|
+
this.#transmissionResolved = true;
|
|
209
|
+
this.#resolveTransmission(result);
|
|
210
|
+
}
|
|
90
211
|
}
|
|
91
212
|
}
|
|
92
|
-
class Response {
|
|
213
|
+
export class Response {
|
|
214
|
+
#statusCode = 200;
|
|
215
|
+
#headers;
|
|
216
|
+
#contents;
|
|
217
|
+
#trailers;
|
|
218
|
+
#consumed = false;
|
|
93
219
|
static new(headers, contents, trailers) {
|
|
94
|
-
|
|
220
|
+
if (!(headers instanceof Fields)) {
|
|
221
|
+
throw new TypeError("headers must be a Fields resource");
|
|
222
|
+
}
|
|
223
|
+
const response = new Response();
|
|
224
|
+
response.#headers = headers;
|
|
225
|
+
Fields._lock(headers);
|
|
226
|
+
response.#contents = contents;
|
|
227
|
+
response.#trailers = Promise.resolve(trailers);
|
|
228
|
+
return [response, Promise.resolve({ tag: "ok", val: undefined })];
|
|
95
229
|
}
|
|
96
230
|
getStatusCode() {
|
|
97
|
-
|
|
231
|
+
return this.#statusCode;
|
|
98
232
|
}
|
|
99
233
|
setStatusCode(statusCode) {
|
|
100
|
-
|
|
234
|
+
if (!Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599) {
|
|
235
|
+
throw new RangeError(`invalid HTTP status code: ${statusCode}`);
|
|
236
|
+
}
|
|
237
|
+
this.#statusCode = statusCode;
|
|
101
238
|
}
|
|
102
239
|
getHeaders() {
|
|
103
|
-
|
|
240
|
+
return this.#headers;
|
|
104
241
|
}
|
|
105
242
|
static consumeBody(this_, res) {
|
|
106
|
-
|
|
243
|
+
if (this_.#consumed) {
|
|
244
|
+
throw new Error("response body has already been consumed");
|
|
245
|
+
}
|
|
246
|
+
this_.#consumed = true;
|
|
247
|
+
void Promise.resolve(res).catch(() => { });
|
|
248
|
+
return [this_.#contents ?? emptyByteStream(), this_.#trailers];
|
|
107
249
|
}
|
|
108
250
|
}
|
|
109
251
|
export default {
|
package/dist/browser/http.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { default as client } from "./http/client.js";
|
|
2
2
|
export { default as handler } from "./http/handler.js";
|
|
3
3
|
export { default as types } from "./http/types.js";
|
|
4
|
+
export { _forbiddenHeaders, HttpError } from "./http/types.js";
|
|
5
|
+
export { _setRequestStreaming } from "@bytecodealliance/preview2-shim/http";
|
|
6
|
+
export { _setHandler } from "./http/handler.js";
|
package/dist/browser/http.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { default as client } from "./http/client.js";
|
|
2
2
|
export { default as handler } from "./http/handler.js";
|
|
3
3
|
export { default as types } from "./http/types.js";
|
|
4
|
+
export { _forbiddenHeaders, HttpError } from "./http/types.js";
|
|
5
|
+
export { _setRequestStreaming } from "@bytecodealliance/preview2-shim/http";
|
|
6
|
+
export { _setHandler } from "./http/handler.js";
|
|
@@ -1,6 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { ipNameLookup } from "@bytecodealliance/preview2-shim/sockets";
|
|
2
|
+
class NameLookupError extends Error {
|
|
3
|
+
payload;
|
|
4
|
+
constructor(payload) {
|
|
5
|
+
super(payload.tag);
|
|
6
|
+
this.payload = payload;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
async function resolveAddresses(name) {
|
|
10
|
+
try {
|
|
11
|
+
const stream = ipNameLookup.resolveAddresses({}, name);
|
|
12
|
+
const addresses = [];
|
|
13
|
+
while (true) {
|
|
14
|
+
const address = stream.resolveNextAddress();
|
|
15
|
+
if (!address) {
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
addresses.push(address);
|
|
19
|
+
}
|
|
20
|
+
if (addresses.length === 0) {
|
|
21
|
+
throw new NameLookupError({ tag: "name-unresolvable" });
|
|
22
|
+
}
|
|
23
|
+
return addresses;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error instanceof NameLookupError) {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
const tag = typeof error === "string" ? error : error?.tag;
|
|
30
|
+
if (tag === "not-supported") {
|
|
31
|
+
throw new NameLookupError({ tag: "other", val: "not-supported" });
|
|
32
|
+
}
|
|
33
|
+
throw new NameLookupError({ tag: "other", val: String(error) });
|
|
34
|
+
}
|
|
4
35
|
}
|
|
5
36
|
export default {
|
|
6
37
|
resolveAddresses,
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
import * as preview2Sockets from "@bytecodealliance/preview2-shim/sockets";
|
|
1
2
|
import type { Duration, ErrorCode, IpAddressFamily, IpSocketAddress, Result, TcpSocket as TcpSocketT, UdpSocket as UdpSocketT } from "../../../types/interfaces/wasi-sockets-types.d.ts";
|
|
3
|
+
type TcpProvider = {
|
|
4
|
+
tcp: typeof preview2Sockets.tcp;
|
|
5
|
+
tcpCreateSocket: typeof preview2Sockets.tcpCreateSocket;
|
|
6
|
+
};
|
|
7
|
+
type UdpProvider = {
|
|
8
|
+
udp: typeof preview2Sockets.udp;
|
|
9
|
+
udpCreateSocket: typeof preview2Sockets.udpCreateSocket;
|
|
10
|
+
};
|
|
11
|
+
export declare function socketErrorCode(error: unknown): ErrorCode;
|
|
12
|
+
export declare function _setTcpProvider(provider: TcpProvider): TcpProvider;
|
|
13
|
+
export declare function _setUdpProvider(provider: UdpProvider): UdpProvider;
|
|
2
14
|
export declare class TcpSocket implements TcpSocketT {
|
|
15
|
+
#private;
|
|
3
16
|
static create(addressFamily: IpAddressFamily): TcpSocket;
|
|
17
|
+
static _wrap(socket: unknown, input?: unknown, output?: unknown): TcpSocket;
|
|
4
18
|
bind(localAddress: IpSocketAddress): void;
|
|
5
19
|
connect(remoteAddress: IpSocketAddress): Promise<void>;
|
|
6
20
|
listen(): ReadableStream<TcpSocketT>;
|
|
@@ -27,6 +41,7 @@ export declare class TcpSocket implements TcpSocketT {
|
|
|
27
41
|
setSendBufferSize(value: bigint): void;
|
|
28
42
|
}
|
|
29
43
|
export declare class UdpSocket implements UdpSocketT {
|
|
44
|
+
#private;
|
|
30
45
|
static create(addressFamily: IpAddressFamily): UdpSocket;
|
|
31
46
|
bind(localAddress: IpSocketAddress): void;
|
|
32
47
|
connect(remoteAddress: IpSocketAddress): void;
|