@npy/fetch 0.1.1 → 0.1.2
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/_internal/decode-stream-error.cjs +18 -0
- package/_internal/decode-stream-error.d.cts +11 -0
- package/_internal/decode-stream-error.d.ts +11 -0
- package/_internal/decode-stream-error.js +18 -0
- package/_internal/error-mapping.cjs +44 -0
- package/_internal/error-mapping.d.cts +15 -0
- package/_internal/error-mapping.d.ts +15 -0
- package/_internal/error-mapping.js +41 -0
- package/_internal/guards.cjs +5 -6
- package/{src/_internal → _internal}/guards.d.cts +2 -0
- package/{src/_internal → _internal}/guards.d.ts +2 -0
- package/_internal/guards.js +5 -7
- package/{src/_internal → _internal}/net.d.cts +1 -2
- package/{src/_internal → _internal}/net.d.ts +1 -2
- package/_internal/symbols.cjs +4 -0
- package/_internal/symbols.d.cts +1 -0
- package/_internal/symbols.d.ts +1 -0
- package/_internal/symbols.js +4 -0
- package/agent-pool.cjs +23 -5
- package/agent-pool.d.cts +2 -0
- package/agent-pool.d.ts +2 -0
- package/agent-pool.js +23 -5
- package/agent.cjs +17 -14
- package/agent.js +17 -14
- package/body.cjs +10 -59
- package/body.d.cts +12 -0
- package/body.d.ts +12 -0
- package/body.js +11 -60
- package/dialers/proxy.cjs +7 -0
- package/{src/dialers → dialers}/proxy.d.cts +11 -3
- package/{src/dialers → dialers}/proxy.d.ts +11 -3
- package/dialers/proxy.js +7 -0
- package/dialers/tcp.cjs +22 -0
- package/{src/dialers → dialers}/tcp.d.cts +23 -2
- package/{src/dialers → dialers}/tcp.d.ts +23 -2
- package/dialers/tcp.js +22 -0
- package/encoding.cjs +32 -13
- package/encoding.d.cts +35 -0
- package/encoding.d.ts +35 -0
- package/encoding.js +32 -13
- package/fetch.cjs +279 -43
- package/fetch.d.cts +58 -0
- package/fetch.d.ts +58 -0
- package/fetch.js +278 -43
- package/http-client.cjs +47 -5
- package/http-client.d.cts +39 -0
- package/http-client.d.ts +39 -0
- package/http-client.js +47 -5
- package/index.cjs +7 -3
- package/index.d.cts +14 -1
- package/index.d.ts +14 -1
- package/index.js +6 -4
- package/io/io.cjs +68 -4
- package/{src/io → io}/io.d.cts +1 -1
- package/{src/io → io}/io.d.ts +1 -1
- package/io/io.js +68 -4
- package/io/readers.cjs +14 -54
- package/io/readers.d.cts +69 -0
- package/io/readers.d.ts +69 -0
- package/io/readers.js +14 -54
- package/io/writers.cjs +10 -5
- package/{src/io → io}/writers.d.cts +1 -1
- package/{src/io → io}/writers.d.ts +1 -1
- package/io/writers.js +11 -6
- package/package.json +18 -2
- package/types/agent.d.cts +72 -0
- package/types/agent.d.ts +72 -0
- package/{src/types → types}/dialer.d.cts +3 -0
- package/{src/types → types}/dialer.d.ts +3 -0
- package/_internal/error-adapters.cjs +0 -146
- package/_internal/error-adapters.js +0 -142
- package/src/_internal/error-adapters.d.cts +0 -22
- package/src/_internal/error-adapters.d.ts +0 -22
- package/src/agent-pool.d.cts +0 -2
- package/src/agent-pool.d.ts +0 -2
- package/src/body.d.cts +0 -23
- package/src/body.d.ts +0 -23
- package/src/encoding.d.cts +0 -24
- package/src/encoding.d.ts +0 -24
- package/src/fetch.d.cts +0 -36
- package/src/fetch.d.ts +0 -36
- package/src/http-client.d.cts +0 -23
- package/src/http-client.d.ts +0 -23
- package/src/index.d.cts +0 -7
- package/src/index.d.ts +0 -7
- package/src/io/readers.d.cts +0 -199
- package/src/io/readers.d.ts +0 -199
- package/src/types/agent.d.cts +0 -128
- package/src/types/agent.d.ts +0 -128
- package/tests/test-utils.d.cts +0 -8
- package/tests/test-utils.d.ts +0 -8
- /package/{src/_internal → _internal}/consts.d.cts +0 -0
- /package/{src/_internal → _internal}/consts.d.ts +0 -0
- /package/{src/_internal → _internal}/promises.d.cts +0 -0
- /package/{src/_internal → _internal}/promises.d.ts +0 -0
- /package/{src/_internal → _internal}/streams.d.cts +0 -0
- /package/{src/_internal → _internal}/streams.d.ts +0 -0
- /package/{src/agent.d.cts → agent.d.cts} +0 -0
- /package/{src/agent.d.ts → agent.d.ts} +0 -0
- /package/{src/dialers → dialers}/index.d.cts +0 -0
- /package/{src/dialers → dialers}/index.d.ts +0 -0
- /package/{src/errors.d.cts → errors.d.cts} +0 -0
- /package/{src/errors.d.ts → errors.d.ts} +0 -0
- /package/{src/io → io}/_utils.d.cts +0 -0
- /package/{src/io → io}/_utils.d.ts +0 -0
- /package/{src/io → io}/buf-writer.d.cts +0 -0
- /package/{src/io → io}/buf-writer.d.ts +0 -0
- /package/{src/types → types}/index.d.cts +0 -0
- /package/{src/types → types}/index.d.ts +0 -0
package/io/io.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_encoding = require("../encoding.cjs");
|
|
2
1
|
const require_streams = require("../_internal/streams.cjs");
|
|
2
|
+
const require_encoding = require("../encoding.cjs");
|
|
3
3
|
const require__utils = require("./_utils.cjs");
|
|
4
4
|
const require_readers = require("./readers.cjs");
|
|
5
5
|
const require_writers = require("./writers.cjs");
|
|
@@ -18,7 +18,70 @@ function parseStatusLine(line) {
|
|
|
18
18
|
statusText: m[4] ?? ""
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
function toArrayBufferBytes(bytes) {
|
|
22
|
+
if (bytes.buffer instanceof ArrayBuffer) return bytes;
|
|
23
|
+
return new Uint8Array(bytes);
|
|
24
|
+
}
|
|
25
|
+
function wrapStreamErrors(source, mapError) {
|
|
26
|
+
const reader = source.getReader();
|
|
27
|
+
let pending = null;
|
|
28
|
+
return new ReadableStream({
|
|
29
|
+
type: "bytes",
|
|
30
|
+
async pull(controller) {
|
|
31
|
+
try {
|
|
32
|
+
const byob = controller.byobRequest;
|
|
33
|
+
if (byob?.view) {
|
|
34
|
+
const target = new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength);
|
|
35
|
+
if (target.byteLength === 0) {
|
|
36
|
+
byob.respond(0);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let written = 0;
|
|
40
|
+
if (pending && pending.byteLength > 0) {
|
|
41
|
+
const n = Math.min(target.byteLength, pending.byteLength);
|
|
42
|
+
target.set(pending.subarray(0, n), written);
|
|
43
|
+
written += n;
|
|
44
|
+
pending = n === pending.byteLength ? null : pending.subarray(n);
|
|
45
|
+
}
|
|
46
|
+
while (written === 0) {
|
|
47
|
+
const { done, value } = await reader.read();
|
|
48
|
+
if (done) {
|
|
49
|
+
byob.respond(0);
|
|
50
|
+
controller.close();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!value || value.byteLength === 0) continue;
|
|
54
|
+
const n = Math.min(target.byteLength - written, value.byteLength);
|
|
55
|
+
target.set(value.subarray(0, n), written);
|
|
56
|
+
written += n;
|
|
57
|
+
if (n < value.byteLength) pending = value.subarray(n);
|
|
58
|
+
}
|
|
59
|
+
byob.respond(written);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (pending && pending.byteLength > 0) {
|
|
63
|
+
const chunk = pending;
|
|
64
|
+
pending = null;
|
|
65
|
+
controller.enqueue(toArrayBufferBytes(chunk));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { done, value } = await reader.read();
|
|
69
|
+
if (done) {
|
|
70
|
+
controller.close();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (value && value.byteLength > 0) controller.enqueue(toArrayBufferBytes(value));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
controller.error(mapError(error));
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async cancel(reason) {
|
|
79
|
+
pending = null;
|
|
80
|
+
await reader.cancel(reason);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function readResponse(conn, options = {}, shouldIgnoreBody, onDone, mapBodyError) {
|
|
22
85
|
const lr = new require_readers.LineReader(conn, options);
|
|
23
86
|
const finalize = (() => {
|
|
24
87
|
let called = false;
|
|
@@ -70,12 +133,12 @@ async function readResponse(conn, options = {}, shouldIgnoreBody, onDone) {
|
|
|
70
133
|
});
|
|
71
134
|
}
|
|
72
135
|
if (chunked) headers.delete("content-length");
|
|
73
|
-
const reader = chunked ? new require_readers.ChunkedBodyReader(lr, options) : new require_readers.BodyReader(lr, contentLength
|
|
136
|
+
const reader = chunked ? new require_readers.ChunkedBodyReader(lr, options) : new require_readers.BodyReader(lr, contentLength, options);
|
|
74
137
|
let body = new ReadableStream({
|
|
75
138
|
type: "bytes",
|
|
76
139
|
async pull(controller) {
|
|
77
140
|
const byob = controller.byobRequest;
|
|
78
|
-
const view = byob?.view ? new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength) : new Uint8Array(new ArrayBuffer(options.
|
|
141
|
+
const view = byob?.view ? new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength) : new Uint8Array(new ArrayBuffer(options.readChunkSize ?? 16 * 1024));
|
|
79
142
|
try {
|
|
80
143
|
const n = await reader.read(view);
|
|
81
144
|
if (n === 0) {
|
|
@@ -117,6 +180,7 @@ async function readResponse(conn, options = {}, shouldIgnoreBody, onDone) {
|
|
|
117
180
|
}
|
|
118
181
|
const maxDecoded = require__utils.parseMaxBytes(options.maxDecodedBodySize);
|
|
119
182
|
if (maxDecoded != null) body = body.pipeThrough(new require_streams.MaxBytesTransformStream(maxDecoded));
|
|
183
|
+
if (mapBodyError != null) body = wrapStreamErrors(body, mapBodyError);
|
|
120
184
|
} catch (err) {
|
|
121
185
|
finalize(false);
|
|
122
186
|
throw err;
|
package/{src/io → io}/io.d.cts
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IConnection } from '@fuman/net';
|
|
2
2
|
import { LineReader, Readers } from './readers';
|
|
3
3
|
import { Writers } from './writers';
|
|
4
|
-
export declare function readResponse(conn: IConnection<unknown>, options: (Readers.Options & LineReader.ReadHeadersOptions) | undefined, shouldIgnoreBody: (status: number) => boolean, onDone?: (reusable: boolean) => void): Promise<Response>;
|
|
4
|
+
export declare function readResponse(conn: IConnection<unknown>, options: (Readers.Options & LineReader.ReadHeadersOptions) | undefined, shouldIgnoreBody: (status: number) => boolean, onDone?: (reusable: boolean) => void, mapBodyError?: (err: unknown) => unknown): Promise<Response>;
|
|
5
5
|
export declare function writeRequest(conn: IConnection<unknown>, req: Writers.Request, options?: Writers.Options): Promise<void>;
|
package/{src/io → io}/io.d.ts
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IConnection } from '@fuman/net';
|
|
2
2
|
import { LineReader, Readers } from './readers';
|
|
3
3
|
import { Writers } from './writers';
|
|
4
|
-
export declare function readResponse(conn: IConnection<unknown>, options: (Readers.Options & LineReader.ReadHeadersOptions) | undefined, shouldIgnoreBody: (status: number) => boolean, onDone?: (reusable: boolean) => void): Promise<Response>;
|
|
4
|
+
export declare function readResponse(conn: IConnection<unknown>, options: (Readers.Options & LineReader.ReadHeadersOptions) | undefined, shouldIgnoreBody: (status: number) => boolean, onDone?: (reusable: boolean) => void, mapBodyError?: (err: unknown) => unknown): Promise<Response>;
|
|
5
5
|
export declare function writeRequest(conn: IConnection<unknown>, req: Writers.Request, options?: Writers.Options): Promise<void>;
|
package/io/io.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { decodeStream } from "../encoding.js";
|
|
2
1
|
import { MaxBytesTransformStream } from "../_internal/streams.js";
|
|
2
|
+
import { decodeStream } from "../encoding.js";
|
|
3
3
|
import { parseContentLength, parseMaxBytes, parseTransferEncoding, splitTokens } from "./_utils.js";
|
|
4
4
|
import { BodyReader, ChunkedBodyReader, LineReader } from "./readers.js";
|
|
5
5
|
import { createRequestWriter } from "./writers.js";
|
|
@@ -18,7 +18,70 @@ function parseStatusLine(line) {
|
|
|
18
18
|
statusText: m[4] ?? ""
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
function toArrayBufferBytes(bytes) {
|
|
22
|
+
if (bytes.buffer instanceof ArrayBuffer) return bytes;
|
|
23
|
+
return new Uint8Array(bytes);
|
|
24
|
+
}
|
|
25
|
+
function wrapStreamErrors(source, mapError) {
|
|
26
|
+
const reader = source.getReader();
|
|
27
|
+
let pending = null;
|
|
28
|
+
return new ReadableStream({
|
|
29
|
+
type: "bytes",
|
|
30
|
+
async pull(controller) {
|
|
31
|
+
try {
|
|
32
|
+
const byob = controller.byobRequest;
|
|
33
|
+
if (byob?.view) {
|
|
34
|
+
const target = new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength);
|
|
35
|
+
if (target.byteLength === 0) {
|
|
36
|
+
byob.respond(0);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let written = 0;
|
|
40
|
+
if (pending && pending.byteLength > 0) {
|
|
41
|
+
const n = Math.min(target.byteLength, pending.byteLength);
|
|
42
|
+
target.set(pending.subarray(0, n), written);
|
|
43
|
+
written += n;
|
|
44
|
+
pending = n === pending.byteLength ? null : pending.subarray(n);
|
|
45
|
+
}
|
|
46
|
+
while (written === 0) {
|
|
47
|
+
const { done, value } = await reader.read();
|
|
48
|
+
if (done) {
|
|
49
|
+
byob.respond(0);
|
|
50
|
+
controller.close();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!value || value.byteLength === 0) continue;
|
|
54
|
+
const n = Math.min(target.byteLength - written, value.byteLength);
|
|
55
|
+
target.set(value.subarray(0, n), written);
|
|
56
|
+
written += n;
|
|
57
|
+
if (n < value.byteLength) pending = value.subarray(n);
|
|
58
|
+
}
|
|
59
|
+
byob.respond(written);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (pending && pending.byteLength > 0) {
|
|
63
|
+
const chunk = pending;
|
|
64
|
+
pending = null;
|
|
65
|
+
controller.enqueue(toArrayBufferBytes(chunk));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { done, value } = await reader.read();
|
|
69
|
+
if (done) {
|
|
70
|
+
controller.close();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (value && value.byteLength > 0) controller.enqueue(toArrayBufferBytes(value));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
controller.error(mapError(error));
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async cancel(reason) {
|
|
79
|
+
pending = null;
|
|
80
|
+
await reader.cancel(reason);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function readResponse(conn, options = {}, shouldIgnoreBody, onDone, mapBodyError) {
|
|
22
85
|
const lr = new LineReader(conn, options);
|
|
23
86
|
const finalize = (() => {
|
|
24
87
|
let called = false;
|
|
@@ -70,12 +133,12 @@ async function readResponse(conn, options = {}, shouldIgnoreBody, onDone) {
|
|
|
70
133
|
});
|
|
71
134
|
}
|
|
72
135
|
if (chunked) headers.delete("content-length");
|
|
73
|
-
const reader = chunked ? new ChunkedBodyReader(lr, options) : new BodyReader(lr, contentLength
|
|
136
|
+
const reader = chunked ? new ChunkedBodyReader(lr, options) : new BodyReader(lr, contentLength, options);
|
|
74
137
|
let body = new ReadableStream({
|
|
75
138
|
type: "bytes",
|
|
76
139
|
async pull(controller) {
|
|
77
140
|
const byob = controller.byobRequest;
|
|
78
|
-
const view = byob?.view ? new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength) : new Uint8Array(new ArrayBuffer(options.
|
|
141
|
+
const view = byob?.view ? new Uint8Array(byob.view.buffer, byob.view.byteOffset, byob.view.byteLength) : new Uint8Array(new ArrayBuffer(options.readChunkSize ?? 16 * 1024));
|
|
79
142
|
try {
|
|
80
143
|
const n = await reader.read(view);
|
|
81
144
|
if (n === 0) {
|
|
@@ -117,6 +180,7 @@ async function readResponse(conn, options = {}, shouldIgnoreBody, onDone) {
|
|
|
117
180
|
}
|
|
118
181
|
const maxDecoded = parseMaxBytes(options.maxDecodedBodySize);
|
|
119
182
|
if (maxDecoded != null) body = body.pipeThrough(new MaxBytesTransformStream(maxDecoded));
|
|
183
|
+
if (mapBodyError != null) body = wrapStreamErrors(body, mapBodyError);
|
|
120
184
|
} catch (err) {
|
|
121
185
|
finalize(false);
|
|
122
186
|
throw err;
|
package/io/readers.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
const require_consts = require("../_internal/consts.cjs");
|
|
3
2
|
const require__utils = require("./_utils.cjs");
|
|
3
|
+
const require_consts = require("../_internal/consts.cjs");
|
|
4
4
|
let _fuman_io = require("@fuman/io");
|
|
5
5
|
let _fuman_net = require("@fuman/net");
|
|
6
6
|
//#region src/io/readers.ts
|
|
@@ -8,32 +8,21 @@ var invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g;
|
|
|
8
8
|
function sanitizeHeaderValue(v) {
|
|
9
9
|
return v.replace(invalidHeaderCharRegex, (m) => encodeURI(m));
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* CRLF-delimited reader that preserves over-reads in an internal buffer.
|
|
13
|
-
*
|
|
14
|
-
* Key property: once you finish reading headers, any bytes already read beyond
|
|
15
|
-
* the header terminator stay buffered and will be returned by read().
|
|
16
|
-
*/
|
|
17
11
|
var LineReader = class {
|
|
18
12
|
#src;
|
|
19
13
|
#buf;
|
|
20
14
|
#codec = new _fuman_io.DelimiterCodec(require_consts.CRLF_BYTES, { strategy: "discard" });
|
|
21
15
|
#eof = false;
|
|
22
|
-
#
|
|
16
|
+
#readChunkSize;
|
|
23
17
|
#maxBufferedBytes;
|
|
24
18
|
#maxLineSize;
|
|
25
19
|
#closed = false;
|
|
26
20
|
close;
|
|
27
|
-
/**
|
|
28
|
-
* LineReader configuration.
|
|
29
|
-
*
|
|
30
|
-
* @namespace LineReader
|
|
31
|
-
*/
|
|
32
21
|
static Options;
|
|
33
22
|
constructor(src, opts = {}) {
|
|
34
23
|
this.#src = src;
|
|
35
24
|
this.#buf = _fuman_io.Bytes.alloc(opts.bufferSize);
|
|
36
|
-
this.#
|
|
25
|
+
this.#readChunkSize = opts.readChunkSize ?? 16 * 1024;
|
|
37
26
|
this.#maxBufferedBytes = opts.maxBufferedBytes ?? 256 * 1024;
|
|
38
27
|
this.#maxLineSize = opts.maxLineSize ?? 64 * 1024;
|
|
39
28
|
this.close = this.#close.bind(this);
|
|
@@ -121,7 +110,7 @@ var LineReader = class {
|
|
|
121
110
|
return headers;
|
|
122
111
|
}
|
|
123
112
|
async #pull() {
|
|
124
|
-
const into = this.#buf.writeSync(this.#
|
|
113
|
+
const into = this.#buf.writeSync(this.#readChunkSize);
|
|
125
114
|
try {
|
|
126
115
|
const n = await this.#src.read(into);
|
|
127
116
|
this.#buf.disposeWriteSync(n);
|
|
@@ -140,16 +129,9 @@ var LineReader = class {
|
|
|
140
129
|
async #close() {
|
|
141
130
|
if (this.#closed) return;
|
|
142
131
|
this.#closed = true;
|
|
143
|
-
|
|
144
|
-
await this.#src.close();
|
|
145
|
-
} finally {
|
|
146
|
-
this.#closed = true;
|
|
147
|
-
}
|
|
132
|
+
await this.#src.close();
|
|
148
133
|
}
|
|
149
134
|
};
|
|
150
|
-
/**
|
|
151
|
-
* Body reader that streams bytes with either a fixed Content-Length or until EOF.
|
|
152
|
-
*/
|
|
153
135
|
var BodyReader = class {
|
|
154
136
|
#src;
|
|
155
137
|
#remaining;
|
|
@@ -157,29 +139,23 @@ var BodyReader = class {
|
|
|
157
139
|
#readSoFar = 0;
|
|
158
140
|
#closed = false;
|
|
159
141
|
close;
|
|
160
|
-
/**
|
|
161
|
-
* BodyReader configuration.
|
|
162
|
-
*
|
|
163
|
-
* @namespace BodyReader
|
|
164
|
-
*/
|
|
165
142
|
static Options;
|
|
166
143
|
constructor(src, contentLength, opts = {}) {
|
|
167
144
|
this.#src = src;
|
|
168
|
-
this.#remaining = contentLength
|
|
145
|
+
this.#remaining = contentLength;
|
|
169
146
|
this.#maxResponseSize = require__utils.parseMaxBytes(opts.maxBodySize);
|
|
170
|
-
if (this.#maxResponseSize != null && this.#remaining != null && this.#remaining > this.#maxResponseSize) throw new Error(`body too large: content-length=${this.#remaining} > maxResponseSize=${this.#maxResponseSize}`);
|
|
171
147
|
this.close = this.#close.bind(this);
|
|
172
148
|
}
|
|
173
149
|
async read(into) {
|
|
174
150
|
if (this.#closed) return 0;
|
|
175
151
|
if (this.#remaining === 0) return 0;
|
|
176
|
-
let max = into.length;
|
|
177
|
-
if (this.#remaining != null) max = Math.min(max, this.#remaining);
|
|
178
152
|
if (this.#maxResponseSize != null) {
|
|
179
153
|
const remainingLimit = this.#maxResponseSize - this.#readSoFar;
|
|
180
154
|
if (remainingLimit <= 0) throw new Error(`body too large (> ${this.#maxResponseSize} bytes)`);
|
|
181
|
-
|
|
155
|
+
if (into.length > remainingLimit) into = into.subarray(0, remainingLimit);
|
|
182
156
|
}
|
|
157
|
+
let max = into.length;
|
|
158
|
+
if (this.#remaining != null) max = Math.min(max, this.#remaining);
|
|
183
159
|
if (max === 0) return 0;
|
|
184
160
|
const view = max === into.length ? into : into.subarray(0, max);
|
|
185
161
|
let n = 0;
|
|
@@ -200,21 +176,14 @@ var BodyReader = class {
|
|
|
200
176
|
async #close() {
|
|
201
177
|
if (this.#closed) return;
|
|
202
178
|
this.#closed = true;
|
|
203
|
-
|
|
204
|
-
await this.#src.close();
|
|
205
|
-
} finally {
|
|
206
|
-
this.#closed = true;
|
|
207
|
-
}
|
|
179
|
+
await this.#src.close();
|
|
208
180
|
}
|
|
209
181
|
};
|
|
210
|
-
/**
|
|
211
|
-
* RFC 7230 chunked transfer-coding decoder.
|
|
212
|
-
*/
|
|
213
182
|
var ChunkedBodyReader = class {
|
|
214
183
|
#src;
|
|
215
184
|
#buf;
|
|
216
185
|
#codec = new _fuman_io.DelimiterCodec(require_consts.CRLF_BYTES, { strategy: "discard" });
|
|
217
|
-
#
|
|
186
|
+
#readChunkSize;
|
|
218
187
|
#maxLineSize;
|
|
219
188
|
#maxChunkSize;
|
|
220
189
|
#maxResponseSize;
|
|
@@ -223,16 +192,11 @@ var ChunkedBodyReader = class {
|
|
|
223
192
|
#closed = false;
|
|
224
193
|
#state = { kind: "size" };
|
|
225
194
|
close;
|
|
226
|
-
/**
|
|
227
|
-
* ChunkedBodyReader configuration.
|
|
228
|
-
*
|
|
229
|
-
* @namespace ChunkedBodyReader
|
|
230
|
-
*/
|
|
231
195
|
static Options;
|
|
232
196
|
constructor(src, opts = {}) {
|
|
233
197
|
this.#src = src;
|
|
234
198
|
this.#buf = _fuman_io.Bytes.alloc(opts.bufferSize);
|
|
235
|
-
this.#
|
|
199
|
+
this.#readChunkSize = opts.readChunkSize ?? 16 * 1024;
|
|
236
200
|
this.#maxLineSize = opts.maxLineSize ?? 64 * 1024;
|
|
237
201
|
this.#maxChunkSize = opts.maxChunkSize ?? 16 * 1024 * 1024;
|
|
238
202
|
this.#maxResponseSize = require__utils.parseMaxBytes(opts.maxBodySize);
|
|
@@ -324,7 +288,7 @@ var ChunkedBodyReader = class {
|
|
|
324
288
|
}
|
|
325
289
|
}
|
|
326
290
|
async #pull() {
|
|
327
|
-
const into = this.#buf.writeSync(this.#
|
|
291
|
+
const into = this.#buf.writeSync(this.#readChunkSize);
|
|
328
292
|
try {
|
|
329
293
|
const n = await this.#readFromSrc(into);
|
|
330
294
|
this.#buf.disposeWriteSync(n);
|
|
@@ -363,11 +327,7 @@ var ChunkedBodyReader = class {
|
|
|
363
327
|
async #close() {
|
|
364
328
|
if (this.#closed) return;
|
|
365
329
|
this.#closed = true;
|
|
366
|
-
|
|
367
|
-
await this.#src.close();
|
|
368
|
-
} finally {
|
|
369
|
-
this.#closed = true;
|
|
370
|
-
}
|
|
330
|
+
await this.#src.close();
|
|
371
331
|
}
|
|
372
332
|
};
|
|
373
333
|
//#endregion
|
package/io/readers.d.cts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { IClosable, IReadable } from '@fuman/io';
|
|
2
|
+
type Source = IReadable & IClosable;
|
|
3
|
+
export declare function sanitizeHeaderValue(v: string): string;
|
|
4
|
+
export declare namespace Readers {
|
|
5
|
+
interface BufferingOptions {
|
|
6
|
+
bufferSize?: number;
|
|
7
|
+
readChunkSize?: number;
|
|
8
|
+
}
|
|
9
|
+
interface SizeLimitOptions {
|
|
10
|
+
maxBodySize?: number | string;
|
|
11
|
+
maxDecodedBodySize?: number | string;
|
|
12
|
+
}
|
|
13
|
+
interface DecompressionOptions {
|
|
14
|
+
decompress?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface DelimiterLimitsOptions {
|
|
17
|
+
maxLineSize?: number;
|
|
18
|
+
maxBufferedBytes?: number;
|
|
19
|
+
}
|
|
20
|
+
interface BodyOptions extends SizeLimitOptions, DecompressionOptions {
|
|
21
|
+
}
|
|
22
|
+
interface ChunkedOptions extends BufferingOptions {
|
|
23
|
+
maxLineSize?: number;
|
|
24
|
+
maxChunkSize?: number;
|
|
25
|
+
}
|
|
26
|
+
type Options = LineReader.Options & BodyOptions & ChunkedOptions;
|
|
27
|
+
}
|
|
28
|
+
export declare class LineReader implements IReadable, IClosable {
|
|
29
|
+
#private;
|
|
30
|
+
close: () => Promise<void> | void;
|
|
31
|
+
static Options: never;
|
|
32
|
+
constructor(src: Source, opts?: LineReader.Options);
|
|
33
|
+
read(into: Uint8Array): Promise<number>;
|
|
34
|
+
readLine(): Promise<string | null>;
|
|
35
|
+
readHeaders(opts?: LineReader.ReadHeadersOptions): Promise<Headers>;
|
|
36
|
+
}
|
|
37
|
+
export declare namespace LineReader {
|
|
38
|
+
interface Options extends Readers.BufferingOptions, Readers.DelimiterLimitsOptions {
|
|
39
|
+
}
|
|
40
|
+
interface ReadHeadersOptions {
|
|
41
|
+
maxHeaderSize?: number;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export declare class BodyReader implements IReadable, IClosable {
|
|
45
|
+
#private;
|
|
46
|
+
close: () => Promise<void> | void;
|
|
47
|
+
static Options: never;
|
|
48
|
+
constructor(src: Source, contentLength: number | null, opts?: BodyReader.Options);
|
|
49
|
+
read(into: Uint8Array): Promise<number>;
|
|
50
|
+
}
|
|
51
|
+
export declare namespace BodyReader {
|
|
52
|
+
interface Options {
|
|
53
|
+
maxBodySize?: number | string;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export declare class ChunkedBodyReader implements IReadable, IClosable {
|
|
57
|
+
#private;
|
|
58
|
+
close: () => Promise<void> | void;
|
|
59
|
+
static Options: never;
|
|
60
|
+
constructor(src: Source, opts?: ChunkedBodyReader.Options);
|
|
61
|
+
read(into: Uint8Array): Promise<number>;
|
|
62
|
+
}
|
|
63
|
+
export declare namespace ChunkedBodyReader {
|
|
64
|
+
interface Options extends BodyReader.Options, Readers.BufferingOptions {
|
|
65
|
+
maxLineSize?: number;
|
|
66
|
+
maxChunkSize?: number;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {};
|
package/io/readers.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { IClosable, IReadable } from '@fuman/io';
|
|
2
|
+
type Source = IReadable & IClosable;
|
|
3
|
+
export declare function sanitizeHeaderValue(v: string): string;
|
|
4
|
+
export declare namespace Readers {
|
|
5
|
+
interface BufferingOptions {
|
|
6
|
+
bufferSize?: number;
|
|
7
|
+
readChunkSize?: number;
|
|
8
|
+
}
|
|
9
|
+
interface SizeLimitOptions {
|
|
10
|
+
maxBodySize?: number | string;
|
|
11
|
+
maxDecodedBodySize?: number | string;
|
|
12
|
+
}
|
|
13
|
+
interface DecompressionOptions {
|
|
14
|
+
decompress?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface DelimiterLimitsOptions {
|
|
17
|
+
maxLineSize?: number;
|
|
18
|
+
maxBufferedBytes?: number;
|
|
19
|
+
}
|
|
20
|
+
interface BodyOptions extends SizeLimitOptions, DecompressionOptions {
|
|
21
|
+
}
|
|
22
|
+
interface ChunkedOptions extends BufferingOptions {
|
|
23
|
+
maxLineSize?: number;
|
|
24
|
+
maxChunkSize?: number;
|
|
25
|
+
}
|
|
26
|
+
type Options = LineReader.Options & BodyOptions & ChunkedOptions;
|
|
27
|
+
}
|
|
28
|
+
export declare class LineReader implements IReadable, IClosable {
|
|
29
|
+
#private;
|
|
30
|
+
close: () => Promise<void> | void;
|
|
31
|
+
static Options: never;
|
|
32
|
+
constructor(src: Source, opts?: LineReader.Options);
|
|
33
|
+
read(into: Uint8Array): Promise<number>;
|
|
34
|
+
readLine(): Promise<string | null>;
|
|
35
|
+
readHeaders(opts?: LineReader.ReadHeadersOptions): Promise<Headers>;
|
|
36
|
+
}
|
|
37
|
+
export declare namespace LineReader {
|
|
38
|
+
interface Options extends Readers.BufferingOptions, Readers.DelimiterLimitsOptions {
|
|
39
|
+
}
|
|
40
|
+
interface ReadHeadersOptions {
|
|
41
|
+
maxHeaderSize?: number;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export declare class BodyReader implements IReadable, IClosable {
|
|
45
|
+
#private;
|
|
46
|
+
close: () => Promise<void> | void;
|
|
47
|
+
static Options: never;
|
|
48
|
+
constructor(src: Source, contentLength: number | null, opts?: BodyReader.Options);
|
|
49
|
+
read(into: Uint8Array): Promise<number>;
|
|
50
|
+
}
|
|
51
|
+
export declare namespace BodyReader {
|
|
52
|
+
interface Options {
|
|
53
|
+
maxBodySize?: number | string;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export declare class ChunkedBodyReader implements IReadable, IClosable {
|
|
57
|
+
#private;
|
|
58
|
+
close: () => Promise<void> | void;
|
|
59
|
+
static Options: never;
|
|
60
|
+
constructor(src: Source, opts?: ChunkedBodyReader.Options);
|
|
61
|
+
read(into: Uint8Array): Promise<number>;
|
|
62
|
+
}
|
|
63
|
+
export declare namespace ChunkedBodyReader {
|
|
64
|
+
interface Options extends BodyReader.Options, Readers.BufferingOptions {
|
|
65
|
+
maxLineSize?: number;
|
|
66
|
+
maxChunkSize?: number;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {};
|
package/io/readers.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CRLF_BYTES } from "../_internal/consts.js";
|
|
2
1
|
import { parseMaxBytes } from "./_utils.js";
|
|
2
|
+
import { CRLF_BYTES } from "../_internal/consts.js";
|
|
3
3
|
import { Bytes, DelimiterCodec, read } from "@fuman/io";
|
|
4
4
|
import { ConnectionClosedError } from "@fuman/net";
|
|
5
5
|
//#region src/io/readers.ts
|
|
@@ -7,32 +7,21 @@ var invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g;
|
|
|
7
7
|
function sanitizeHeaderValue(v) {
|
|
8
8
|
return v.replace(invalidHeaderCharRegex, (m) => encodeURI(m));
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
* CRLF-delimited reader that preserves over-reads in an internal buffer.
|
|
12
|
-
*
|
|
13
|
-
* Key property: once you finish reading headers, any bytes already read beyond
|
|
14
|
-
* the header terminator stay buffered and will be returned by read().
|
|
15
|
-
*/
|
|
16
10
|
var LineReader = class {
|
|
17
11
|
#src;
|
|
18
12
|
#buf;
|
|
19
13
|
#codec = new DelimiterCodec(CRLF_BYTES, { strategy: "discard" });
|
|
20
14
|
#eof = false;
|
|
21
|
-
#
|
|
15
|
+
#readChunkSize;
|
|
22
16
|
#maxBufferedBytes;
|
|
23
17
|
#maxLineSize;
|
|
24
18
|
#closed = false;
|
|
25
19
|
close;
|
|
26
|
-
/**
|
|
27
|
-
* LineReader configuration.
|
|
28
|
-
*
|
|
29
|
-
* @namespace LineReader
|
|
30
|
-
*/
|
|
31
20
|
static Options;
|
|
32
21
|
constructor(src, opts = {}) {
|
|
33
22
|
this.#src = src;
|
|
34
23
|
this.#buf = Bytes.alloc(opts.bufferSize);
|
|
35
|
-
this.#
|
|
24
|
+
this.#readChunkSize = opts.readChunkSize ?? 16 * 1024;
|
|
36
25
|
this.#maxBufferedBytes = opts.maxBufferedBytes ?? 256 * 1024;
|
|
37
26
|
this.#maxLineSize = opts.maxLineSize ?? 64 * 1024;
|
|
38
27
|
this.close = this.#close.bind(this);
|
|
@@ -120,7 +109,7 @@ var LineReader = class {
|
|
|
120
109
|
return headers;
|
|
121
110
|
}
|
|
122
111
|
async #pull() {
|
|
123
|
-
const into = this.#buf.writeSync(this.#
|
|
112
|
+
const into = this.#buf.writeSync(this.#readChunkSize);
|
|
124
113
|
try {
|
|
125
114
|
const n = await this.#src.read(into);
|
|
126
115
|
this.#buf.disposeWriteSync(n);
|
|
@@ -139,16 +128,9 @@ var LineReader = class {
|
|
|
139
128
|
async #close() {
|
|
140
129
|
if (this.#closed) return;
|
|
141
130
|
this.#closed = true;
|
|
142
|
-
|
|
143
|
-
await this.#src.close();
|
|
144
|
-
} finally {
|
|
145
|
-
this.#closed = true;
|
|
146
|
-
}
|
|
131
|
+
await this.#src.close();
|
|
147
132
|
}
|
|
148
133
|
};
|
|
149
|
-
/**
|
|
150
|
-
* Body reader that streams bytes with either a fixed Content-Length or until EOF.
|
|
151
|
-
*/
|
|
152
134
|
var BodyReader = class {
|
|
153
135
|
#src;
|
|
154
136
|
#remaining;
|
|
@@ -156,29 +138,23 @@ var BodyReader = class {
|
|
|
156
138
|
#readSoFar = 0;
|
|
157
139
|
#closed = false;
|
|
158
140
|
close;
|
|
159
|
-
/**
|
|
160
|
-
* BodyReader configuration.
|
|
161
|
-
*
|
|
162
|
-
* @namespace BodyReader
|
|
163
|
-
*/
|
|
164
141
|
static Options;
|
|
165
142
|
constructor(src, contentLength, opts = {}) {
|
|
166
143
|
this.#src = src;
|
|
167
|
-
this.#remaining = contentLength
|
|
144
|
+
this.#remaining = contentLength;
|
|
168
145
|
this.#maxResponseSize = parseMaxBytes(opts.maxBodySize);
|
|
169
|
-
if (this.#maxResponseSize != null && this.#remaining != null && this.#remaining > this.#maxResponseSize) throw new Error(`body too large: content-length=${this.#remaining} > maxResponseSize=${this.#maxResponseSize}`);
|
|
170
146
|
this.close = this.#close.bind(this);
|
|
171
147
|
}
|
|
172
148
|
async read(into) {
|
|
173
149
|
if (this.#closed) return 0;
|
|
174
150
|
if (this.#remaining === 0) return 0;
|
|
175
|
-
let max = into.length;
|
|
176
|
-
if (this.#remaining != null) max = Math.min(max, this.#remaining);
|
|
177
151
|
if (this.#maxResponseSize != null) {
|
|
178
152
|
const remainingLimit = this.#maxResponseSize - this.#readSoFar;
|
|
179
153
|
if (remainingLimit <= 0) throw new Error(`body too large (> ${this.#maxResponseSize} bytes)`);
|
|
180
|
-
|
|
154
|
+
if (into.length > remainingLimit) into = into.subarray(0, remainingLimit);
|
|
181
155
|
}
|
|
156
|
+
let max = into.length;
|
|
157
|
+
if (this.#remaining != null) max = Math.min(max, this.#remaining);
|
|
182
158
|
if (max === 0) return 0;
|
|
183
159
|
const view = max === into.length ? into : into.subarray(0, max);
|
|
184
160
|
let n = 0;
|
|
@@ -199,21 +175,14 @@ var BodyReader = class {
|
|
|
199
175
|
async #close() {
|
|
200
176
|
if (this.#closed) return;
|
|
201
177
|
this.#closed = true;
|
|
202
|
-
|
|
203
|
-
await this.#src.close();
|
|
204
|
-
} finally {
|
|
205
|
-
this.#closed = true;
|
|
206
|
-
}
|
|
178
|
+
await this.#src.close();
|
|
207
179
|
}
|
|
208
180
|
};
|
|
209
|
-
/**
|
|
210
|
-
* RFC 7230 chunked transfer-coding decoder.
|
|
211
|
-
*/
|
|
212
181
|
var ChunkedBodyReader = class {
|
|
213
182
|
#src;
|
|
214
183
|
#buf;
|
|
215
184
|
#codec = new DelimiterCodec(CRLF_BYTES, { strategy: "discard" });
|
|
216
|
-
#
|
|
185
|
+
#readChunkSize;
|
|
217
186
|
#maxLineSize;
|
|
218
187
|
#maxChunkSize;
|
|
219
188
|
#maxResponseSize;
|
|
@@ -222,16 +191,11 @@ var ChunkedBodyReader = class {
|
|
|
222
191
|
#closed = false;
|
|
223
192
|
#state = { kind: "size" };
|
|
224
193
|
close;
|
|
225
|
-
/**
|
|
226
|
-
* ChunkedBodyReader configuration.
|
|
227
|
-
*
|
|
228
|
-
* @namespace ChunkedBodyReader
|
|
229
|
-
*/
|
|
230
194
|
static Options;
|
|
231
195
|
constructor(src, opts = {}) {
|
|
232
196
|
this.#src = src;
|
|
233
197
|
this.#buf = Bytes.alloc(opts.bufferSize);
|
|
234
|
-
this.#
|
|
198
|
+
this.#readChunkSize = opts.readChunkSize ?? 16 * 1024;
|
|
235
199
|
this.#maxLineSize = opts.maxLineSize ?? 64 * 1024;
|
|
236
200
|
this.#maxChunkSize = opts.maxChunkSize ?? 16 * 1024 * 1024;
|
|
237
201
|
this.#maxResponseSize = parseMaxBytes(opts.maxBodySize);
|
|
@@ -323,7 +287,7 @@ var ChunkedBodyReader = class {
|
|
|
323
287
|
}
|
|
324
288
|
}
|
|
325
289
|
async #pull() {
|
|
326
|
-
const into = this.#buf.writeSync(this.#
|
|
290
|
+
const into = this.#buf.writeSync(this.#readChunkSize);
|
|
327
291
|
try {
|
|
328
292
|
const n = await this.#readFromSrc(into);
|
|
329
293
|
this.#buf.disposeWriteSync(n);
|
|
@@ -362,11 +326,7 @@ var ChunkedBodyReader = class {
|
|
|
362
326
|
async #close() {
|
|
363
327
|
if (this.#closed) return;
|
|
364
328
|
this.#closed = true;
|
|
365
|
-
|
|
366
|
-
await this.#src.close();
|
|
367
|
-
} finally {
|
|
368
|
-
this.#closed = true;
|
|
369
|
-
}
|
|
329
|
+
await this.#src.close();
|
|
370
330
|
}
|
|
371
331
|
};
|
|
372
332
|
//#endregion
|