tunnelfetch 1.0.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.
Files changed (96) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +617 -0
  3. package/README.zh-CN.md +470 -0
  4. package/package.json +74 -0
  5. package/src/client/cookies.js +429 -0
  6. package/src/client/decode.js +346 -0
  7. package/src/client/redirect.js +249 -0
  8. package/src/client.js +704 -0
  9. package/src/errors.js +181 -0
  10. package/src/http1/chunked.js +289 -0
  11. package/src/http1/index.js +10 -0
  12. package/src/http1/request.js +143 -0
  13. package/src/http1/response.js +493 -0
  14. package/src/http2/connection.js +1170 -0
  15. package/src/http2/constants.js +129 -0
  16. package/src/http2/frames.js +291 -0
  17. package/src/http2/hpack.js +420 -0
  18. package/src/http2/huffman.js +203 -0
  19. package/src/http2/index.js +21 -0
  20. package/src/index.js +46 -0
  21. package/src/pool.js +256 -0
  22. package/src/proxy/direct.js +62 -0
  23. package/src/proxy/http-connect.js +206 -0
  24. package/src/proxy/index.js +197 -0
  25. package/src/proxy/socks5.js +344 -0
  26. package/src/tls/aead.js +263 -0
  27. package/src/tls/connect.js +407 -0
  28. package/src/tls/constants.js +334 -0
  29. package/src/tls/extensions.js +376 -0
  30. package/src/tls/handshake-messages.js +901 -0
  31. package/src/tls/handshake.js +568 -0
  32. package/src/tls/handshake12.js +507 -0
  33. package/src/tls/index.js +44 -0
  34. package/src/tls/keyschedule.js +473 -0
  35. package/src/tls/record.js +872 -0
  36. package/src/tls/tickets.js +145 -0
  37. package/src/tls/transcript.js +101 -0
  38. package/src/tls/wire.js +224 -0
  39. package/src/transport.js +296 -0
  40. package/src/trust/der.js +551 -0
  41. package/src/trust/index.js +375 -0
  42. package/src/trust/name.js +235 -0
  43. package/src/trust/ocsp.js +759 -0
  44. package/src/trust/path.js +595 -0
  45. package/src/trust/roots.js +454 -0
  46. package/src/trust/x509.js +902 -0
  47. package/src/util/bytes.js +470 -0
  48. package/src/util/deadline.js +266 -0
  49. package/src/warmup-fixture.js +85 -0
  50. package/src/warmup.js +243 -0
  51. package/types/client/cookies.d.ts +159 -0
  52. package/types/client/decode.d.ts +54 -0
  53. package/types/client/redirect.d.ts +96 -0
  54. package/types/client.d.ts +323 -0
  55. package/types/errors.d.ts +141 -0
  56. package/types/http1/chunked.d.ts +48 -0
  57. package/types/http1/index.d.ts +3 -0
  58. package/types/http1/request.d.ts +44 -0
  59. package/types/http1/response.d.ts +183 -0
  60. package/types/http2/connection.d.ts +282 -0
  61. package/types/http2/constants.d.ts +95 -0
  62. package/types/http2/frames.d.ts +116 -0
  63. package/types/http2/hpack.d.ts +99 -0
  64. package/types/http2/huffman.d.ts +21 -0
  65. package/types/http2/index.d.ts +5 -0
  66. package/types/index.d.ts +17 -0
  67. package/types/pool.d.ts +135 -0
  68. package/types/proxy/direct.d.ts +26 -0
  69. package/types/proxy/http-connect.d.ts +37 -0
  70. package/types/proxy/index.d.ts +62 -0
  71. package/types/proxy/socks5.d.ts +47 -0
  72. package/types/tls/aead.d.ts +67 -0
  73. package/types/tls/connect.d.ts +280 -0
  74. package/types/tls/constants.d.ts +275 -0
  75. package/types/tls/extensions.d.ts +195 -0
  76. package/types/tls/handshake-messages.d.ts +430 -0
  77. package/types/tls/handshake.d.ts +90 -0
  78. package/types/tls/handshake12.d.ts +35 -0
  79. package/types/tls/index.d.ts +9 -0
  80. package/types/tls/keyschedule.d.ts +272 -0
  81. package/types/tls/record.d.ts +361 -0
  82. package/types/tls/tickets.d.ts +66 -0
  83. package/types/tls/transcript.d.ts +52 -0
  84. package/types/tls/wire.d.ts +106 -0
  85. package/types/transport.d.ts +222 -0
  86. package/types/trust/der.d.ts +239 -0
  87. package/types/trust/index.d.ts +194 -0
  88. package/types/trust/name.d.ts +33 -0
  89. package/types/trust/ocsp.d.ts +138 -0
  90. package/types/trust/path.d.ts +139 -0
  91. package/types/trust/roots.d.ts +36 -0
  92. package/types/trust/x509.d.ts +401 -0
  93. package/types/util/bytes.d.ts +183 -0
  94. package/types/util/deadline.d.ts +133 -0
  95. package/types/warmup-fixture.d.ts +11 -0
  96. package/types/warmup.d.ts +45 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * One of the stable machine-readable codes in {@link codes}. Exported as a named union so a
3
+ * caller can exhaustively switch on `err.code` and have the compiler point at the case they
4
+ * forgot when a release adds one.
5
+ * @typedef {(typeof codes)[keyof typeof codes]} ErrorCode
6
+ */
7
+ /** Base for everything this package throws. Never thrown directly. */
8
+ export class TunnelFetchError extends Error {
9
+ /**
10
+ * @param {string} code stable machine-readable code, normally an {@link ErrorCode}. Typed as
11
+ * string because one internal code (`UNEXPECTED_EOF`, from the byte layer) deliberately
12
+ * lives outside the public table.
13
+ * @param {string} message human-readable, must name concrete values
14
+ * @param {Record<string, unknown>} [detail]
15
+ */
16
+ constructor(code: string, message: string, detail?: Record<string, unknown>);
17
+ code: string;
18
+ detail: Record<string, unknown> | undefined;
19
+ }
20
+ /** Proxy handshake failed: CONNECT refused, SOCKS5 rejected, auth required. */
21
+ export class ProxyError extends TunnelFetchError {
22
+ }
23
+ /** HTTP/1.1 wire format: malformed message, ambiguous framing, truncated body. */
24
+ export class HttpError extends TunnelFetchError {
25
+ }
26
+ /** TLS record layer, handshake, or a peer alert. */
27
+ export class TlsError extends TunnelFetchError {
28
+ }
29
+ /**
30
+ * HTTP/2 framing, streams, flow control, HPACK, or a peer GOAWAY/RST_STREAM. Separate from
31
+ * HttpError because the wire format and its failure modes are entirely different: an HTTP/1.1
32
+ * message is text framed by lengths and CRLFs, an HTTP/2 stream is a state machine over binary
33
+ * frames sharing one connection, and a caller distinguishing "the /1.1 parser choked" from "an
34
+ * h2 stream was reset" wants two codes, not one.
35
+ */
36
+ export class Http2Error extends TunnelFetchError {
37
+ }
38
+ /**
39
+ * The peer offered something we deliberately do not implement.
40
+ * Separate from TlsError so "we cannot talk to this server" is distinguishable from
41
+ * "the connection broke", which is the difference between a feature request and a bug report.
42
+ */
43
+ export class TlsUnsupportedError extends TlsError {
44
+ }
45
+ /** Certificate chain, trust anchor, name matching, validity, or constraint failure. */
46
+ export class CertificateError extends TunnelFetchError {
47
+ }
48
+ /** A deadline elapsed. `phase` says which one, because the fix differs per phase. */
49
+ export class TimeoutError extends TunnelFetchError {
50
+ }
51
+ /** A configured limit was exceeded (response size, header size, redirect count). */
52
+ export class LimitError extends TunnelFetchError {
53
+ }
54
+ /** The caller asked for something the chosen transport cannot honour. */
55
+ export class ConfigError extends TunnelFetchError {
56
+ }
57
+ export namespace codes {
58
+ let PROXY_CONNECT_REFUSED: "PROXY_CONNECT_REFUSED";
59
+ let PROXY_AUTH_REQUIRED: "PROXY_AUTH_REQUIRED";
60
+ let PROXY_AUTH_FAILED: "PROXY_AUTH_FAILED";
61
+ let PROXY_PROTOCOL: "PROXY_PROTOCOL";
62
+ let PROXY_UNREACHABLE: "PROXY_UNREACHABLE";
63
+ let SOCKS5_NO_ACCEPTABLE_AUTH: "SOCKS5_NO_ACCEPTABLE_AUTH";
64
+ let SOCKS5_REPLY: "SOCKS5_REPLY";
65
+ let SOCKS5_ADDR_TYPE: "SOCKS5_ADDR_TYPE";
66
+ let HTTP_REQUEST_LINE: "HTTP_REQUEST_LINE";
67
+ let HTTP_STATUS_LINE: "HTTP_STATUS_LINE";
68
+ let HTTP_HEADER: "HTTP_HEADER";
69
+ let HTTP_FRAMING_AMBIGUOUS: "HTTP_FRAMING_AMBIGUOUS";
70
+ let HTTP_CHUNK: "HTTP_CHUNK";
71
+ let HTTP_BODY_TRUNCATED: "HTTP_BODY_TRUNCATED";
72
+ let HTTP_TRAILER: "HTTP_TRAILER";
73
+ let HTTP_UPGRADE_UNEXPECTED: "HTTP_UPGRADE_UNEXPECTED";
74
+ let TLS_RECORD: "TLS_RECORD";
75
+ let TLS_ALERT: "TLS_ALERT";
76
+ let TLS_HANDSHAKE: "TLS_HANDSHAKE";
77
+ let TLS_TRUNCATED: "TLS_TRUNCATED";
78
+ let TLS_VERSION_UNSUPPORTED: "TLS_VERSION_UNSUPPORTED";
79
+ let TLS_CIPHER_UNSUPPORTED: "TLS_CIPHER_UNSUPPORTED";
80
+ let TLS_GROUP_UNSUPPORTED: "TLS_GROUP_UNSUPPORTED";
81
+ let TLS_SIGALG_UNSUPPORTED: "TLS_SIGALG_UNSUPPORTED";
82
+ let TLS_EXTENSION_UNSUPPORTED: "TLS_EXTENSION_UNSUPPORTED";
83
+ let TLS_ALPN: "TLS_ALPN";
84
+ let TLS_PSK: "TLS_PSK";
85
+ let TLS_TICKET: "TLS_TICKET";
86
+ let HTTP2_PROTOCOL: "HTTP2_PROTOCOL";
87
+ let HTTP2_FRAME_SIZE: "HTTP2_FRAME_SIZE";
88
+ let HTTP2_SETTINGS: "HTTP2_SETTINGS";
89
+ let HTTP2_FLOW_CONTROL: "HTTP2_FLOW_CONTROL";
90
+ let HTTP2_STREAM_STATE: "HTTP2_STREAM_STATE";
91
+ let HTTP2_STREAM_CLOSED: "HTTP2_STREAM_CLOSED";
92
+ let HTTP2_GOAWAY: "HTTP2_GOAWAY";
93
+ let HTTP2_COMPRESSION: "HTTP2_COMPRESSION";
94
+ let HTTP2_HEADER: "HTTP2_HEADER";
95
+ let HTTP2_PUSH_UNEXPECTED: "HTTP2_PUSH_UNEXPECTED";
96
+ let HTTP2_TRAILER: "HTTP2_TRAILER";
97
+ let CERT_PARSE: "CERT_PARSE";
98
+ let CERT_CHAIN_INCOMPLETE: "CERT_CHAIN_INCOMPLETE";
99
+ let CERT_UNTRUSTED_ROOT: "CERT_UNTRUSTED_ROOT";
100
+ let CERT_SIGNATURE_INVALID: "CERT_SIGNATURE_INVALID";
101
+ let CERT_SIGNATURE_WEAK: "CERT_SIGNATURE_WEAK";
102
+ let CERT_SIGNATURE_UNSUPPORTED: "CERT_SIGNATURE_UNSUPPORTED";
103
+ let CERT_EXPIRED: "CERT_EXPIRED";
104
+ let CERT_NOT_YET_VALID: "CERT_NOT_YET_VALID";
105
+ let CERT_NAME_MISMATCH: "CERT_NAME_MISMATCH";
106
+ let CERT_CONSTRAINT: "CERT_CONSTRAINT";
107
+ let CERT_PIN_MISMATCH: "CERT_PIN_MISMATCH";
108
+ let OCSP_PARSE: "OCSP_PARSE";
109
+ let OCSP_UNVERIFIED: "OCSP_UNVERIFIED";
110
+ let OCSP_MISMATCH: "OCSP_MISMATCH";
111
+ let OCSP_STALE: "OCSP_STALE";
112
+ let OCSP_REVOKED: "OCSP_REVOKED";
113
+ let OCSP_UNKNOWN: "OCSP_UNKNOWN";
114
+ let OCSP_REQUIRED: "OCSP_REQUIRED";
115
+ let HTTP_CONTENT_ENCODING: "HTTP_CONTENT_ENCODING";
116
+ let HTTP_CHARSET: "HTTP_CHARSET";
117
+ let REDIRECT_INVALID_LOCATION: "REDIRECT_INVALID_LOCATION";
118
+ let REDIRECT_LOOP: "REDIRECT_LOOP";
119
+ let REDIRECT_SCHEME: "REDIRECT_SCHEME";
120
+ let COOKIE_INVALID: "COOKIE_INVALID";
121
+ let POOL_CLOSED: "POOL_CLOSED";
122
+ let CONNECTION_CLOSED: "CONNECTION_CLOSED";
123
+ let TIMEOUT_CONNECT: "TIMEOUT_CONNECT";
124
+ let TIMEOUT_HANDSHAKE: "TIMEOUT_HANDSHAKE";
125
+ let TIMEOUT_HEADERS: "TIMEOUT_HEADERS";
126
+ let TIMEOUT_IDLE: "TIMEOUT_IDLE";
127
+ let TIMEOUT_TOTAL: "TIMEOUT_TOTAL";
128
+ let LIMIT_BODY: "LIMIT_BODY";
129
+ let LIMIT_HEADER: "LIMIT_HEADER";
130
+ let LIMIT_REDIRECTS: "LIMIT_REDIRECTS";
131
+ let CONFIG_UNSATISFIABLE: "CONFIG_UNSATISFIABLE";
132
+ let CONFIG_INVALID: "CONFIG_INVALID";
133
+ }
134
+ export function hex8(n: number): string;
135
+ export function hex16(n: number): string;
136
+ /**
137
+ * One of the stable machine-readable codes in {@link codes}. Exported as a named union so a
138
+ * caller can exhaustively switch on `err.code` and have the compiler point at the case they
139
+ * forgot when a release adds one.
140
+ */
141
+ export type ErrorCode = (typeof codes)[keyof typeof codes];
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Every cap is fail-closed: the peer controls chunk sizes and counts, so each one bounds what
3
+ * a hostile sender can make us buffer.
4
+ * @typedef {object} ChunkedOptions
5
+ * @property {number} [maxBytes] total payload cap, default unlimited
6
+ * @property {number} [maxChunkSize] per-chunk cap, default 64 MiB
7
+ * @property {number} [maxTrailerBytes] trailer-section cap, default 8192
8
+ */
9
+ /**
10
+ * Decode a chunked body from `reader`.
11
+ *
12
+ * Returns `{ stream, trailers }`:
13
+ * - `stream`: ReadableStream<Uint8Array> of the decoded payload octets.
14
+ * - `trailers`: Promise<Headers|null>. Resolves with the trailer fields once the terminal
15
+ * chunk and its trailing CRLF have been fully consumed — i.e. the reader is positioned
16
+ * exactly after the body, which is the signal a connection pool needs before reuse.
17
+ * Resolves null if the stream is cancelled before that point (position unknown, do not
18
+ * reuse). Rejects with the same error the stream errors with on a protocol violation.
19
+ *
20
+ * Reads only what the chunked grammar covers; any bytes after the terminal CRLF stay in
21
+ * `reader` for the next message.
22
+ *
23
+ * @param {import('../util/bytes.js').ByteReader} reader
24
+ * @param {ChunkedOptions} [opts]
25
+ * @returns {{ stream: ReadableStream<Uint8Array>, trailers: Promise<Headers | null> }}
26
+ */
27
+ export function decodeChunked(reader: import("../util/bytes.js").ByteReader, { maxBytes, maxChunkSize, maxTrailerBytes }?: ChunkedOptions): {
28
+ stream: ReadableStream<Uint8Array>;
29
+ trailers: Promise<Headers | null>;
30
+ };
31
+ /**
32
+ * Every cap is fail-closed: the peer controls chunk sizes and counts, so each one bounds what
33
+ * a hostile sender can make us buffer.
34
+ */
35
+ export type ChunkedOptions = {
36
+ /**
37
+ * total payload cap, default unlimited
38
+ */
39
+ maxBytes?: number | undefined;
40
+ /**
41
+ * per-chunk cap, default 64 MiB
42
+ */
43
+ maxChunkSize?: number | undefined;
44
+ /**
45
+ * trailer-section cap, default 8192
46
+ */
47
+ maxTrailerBytes?: number | undefined;
48
+ };
@@ -0,0 +1,3 @@
1
+ export { serializeRequestHead } from "./request.js";
2
+ export { decodeChunked } from "./chunked.js";
3
+ export { readResponseHead, bodyFraming, readResponseBody } from "./response.js";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A request head as this serialiser consumes it. Nothing optional is invented: what is absent
3
+ * here is absent on the wire.
4
+ * @typedef {object} RequestHead
5
+ * @property {string} method RFC 9110 token, sent verbatim (no case-folding)
6
+ * @property {string} target request-target, already encoded: origin-form (`/path?q`),
7
+ * absolute-form, authority-form (CONNECT) or `*`
8
+ * @property {Headers | Iterable<[string, string]>} [headers]
9
+ * @property {'1.0' | '1.1'} [httpVersion] default '1.1'
10
+ */
11
+ /**
12
+ * Serialise a request head: request line, header fields, terminating blank line.
13
+ * Throws HttpError on any input the RFC 9110 grammar rejects — notably CR/LF in a value,
14
+ * which is header injection, never something to sanitise.
15
+ *
16
+ * `headers` may be a WHATWG Headers instance or any iterable of [name, value] pairs.
17
+ * Pair iterables are written in caller order, unsorted — order can matter to real servers.
18
+ * Note that a Headers instance has already lost the caller's order by spec (it iterates
19
+ * lowercased and sorted); we serialise its iteration order as-is.
20
+ *
21
+ * @param {RequestHead} req
22
+ * @returns {Uint8Array}
23
+ */
24
+ export function serializeRequestHead({ method, target, headers, httpVersion }: RequestHead): Uint8Array;
25
+ /**
26
+ * A request head as this serialiser consumes it. Nothing optional is invented: what is absent
27
+ * here is absent on the wire.
28
+ */
29
+ export type RequestHead = {
30
+ /**
31
+ * RFC 9110 token, sent verbatim (no case-folding)
32
+ */
33
+ method: string;
34
+ /**
35
+ * request-target, already encoded: origin-form (`/path?q`),
36
+ * absolute-form, authority-form (CONNECT) or `*`
37
+ */
38
+ target: string;
39
+ headers?: Headers | Iterable<[string, string]> | undefined;
40
+ /**
41
+ * default '1.1'
42
+ */
43
+ httpVersion?: "1.0" | "1.1" | undefined;
44
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * One skipped 1xx head. Kept because Early Hints (103) carry Link headers a caller may want;
3
+ * everything else about a 1xx is noise by definition.
4
+ * @typedef {object} InformationalHead
5
+ * @property {'1.0' | '1.1'} httpVersion
6
+ * @property {number} status
7
+ * @property {string} statusText
8
+ * @property {Headers} headers
9
+ */
10
+ /**
11
+ * The parsed response head. `setCookie` repeats the raw Set-Cookie values because the Headers
12
+ * class folds duplicates with ", ", which destroys cookie dates — no jar can be built from the
13
+ * folded form.
14
+ * @typedef {object} ResponseHead
15
+ * @property {'1.0' | '1.1'} httpVersion only versions the status-line grammar admits
16
+ * @property {number} status
17
+ * @property {string} statusText may be empty; `HTTP/1.1 200` is a legal status line
18
+ * @property {Headers} headers
19
+ * @property {string[]} setCookie one entry per Set-Cookie header, unfolded
20
+ * @property {InformationalHead[]} informational 1xx heads skipped before the real response
21
+ */
22
+ /**
23
+ * @typedef {object} ReadHeadOptions
24
+ * @property {number} [maxHeaderBytes] budget for the ENTIRE head phase, default 65536
25
+ */
26
+ /**
27
+ * Read the response head: status line and header fields, plus any preceding informational
28
+ * (1xx) responses, which are legal noise before the real response (100 Continue, 103 Early
29
+ * Hints). They are skipped — 1xx never has a body — and returned in `informational` so a
30
+ * caller can surface Early Hints. 101 is fatal: this client never offers an upgrade, so a
31
+ * peer switching protocols means the bytes that follow are not HTTP and cannot be framed.
32
+ * Malformed heads throw HttpError; an oversized head throws LimitError.
33
+ *
34
+ * `maxHeaderBytes` bounds the ENTIRE head phase, informational heads included; a per-head
35
+ * budget would let a peer stream 1xx responses forever.
36
+ *
37
+ * @param {import('../util/bytes.js').ByteReader} reader
38
+ * @param {ReadHeadOptions} [opts]
39
+ * @returns {Promise<ResponseHead>}
40
+ */
41
+ export function readResponseHead(reader: import("../util/bytes.js").ByteReader, { maxHeaderBytes }?: ReadHeadOptions): Promise<ResponseHead>;
42
+ /**
43
+ * How a response body is delimited. The four kinds are exhaustive: RFC 9112 §6.3 admits no
44
+ * fifth, and everything ambiguous throws before a kind is chosen.
45
+ * @typedef {'none' | 'content-length' | 'chunked' | 'until-close'} FramingKind
46
+ */
47
+ /**
48
+ * The framing decision. `length` is present exactly when `kind` is 'content-length'.
49
+ * @typedef {object} Framing
50
+ * @property {FramingKind} kind
51
+ * @property {number} [length] declared byte count, content-length framing only
52
+ * @property {boolean} keepAliveEligible whether the socket MAY be reused after the body ends
53
+ * as framed; see bodyFraming for why this is the load-bearing bit
54
+ */
55
+ /**
56
+ * Decide how the response body is delimited, per RFC 9112 §6.3, in its order. Ambiguous
57
+ * framing (TE and CL together, disagreeing duplicate CLs, an undecodable transfer coding)
58
+ * throws HttpError with HTTP_FRAMING_AMBIGUOUS — every one of those is a smuggling vector.
59
+ *
60
+ * `keepAliveEligible` is the load-bearing bit: it is true only when the body has a determinate
61
+ * end (`none`, `content-length`, `chunked`). A connection pool must never reuse a socket after
62
+ * an `until-close` body — with no marked end, "the body" is just "whatever arrived", and the
63
+ * next request on that socket would read the previous response's tail as its own head. That is
64
+ * the response-to-the-wrong-request bug, and this flag is the only thing standing between the
65
+ * pool and it. (A 2xx CONNECT reply is also ineligible: the socket is a tunnel now, not HTTP.)
66
+ *
67
+ * @param {{ status: number, method?: string, headers: Headers }} res the head fields framing
68
+ * depends on; a full ResponseHead satisfies it
69
+ * @returns {Framing}
70
+ */
71
+ export function bodyFraming({ status, method, headers }: {
72
+ status: number;
73
+ method?: string;
74
+ headers: Headers;
75
+ }): Framing;
76
+ /**
77
+ * A response body stream plus the completion contract a connection pool needs. The two extra
78
+ * properties are documented on readResponseBody, which is the only producer.
79
+ * @typedef {ReadableStream<Uint8Array> & { completed: Promise<boolean>,
80
+ * trailers: Promise<Headers | null> }} BodyStream
81
+ */
82
+ /**
83
+ * @typedef {object} ReadBodyOptions
84
+ * @property {number} [maxBytes] fail-closed cap on total payload bytes, default unlimited
85
+ */
86
+ /**
87
+ * Stream the response body according to `framing`.
88
+ *
89
+ * The returned ReadableStream<Uint8Array> carries two extra properties — the completion
90
+ * contract a connection pool needs:
91
+ *
92
+ * - `completed`: Promise<boolean>. Resolves `true` only when the body ended exactly as framed
93
+ * (the reader is positioned at the first byte after the body). Resolves `false` if the
94
+ * consumer cancelled early (position unknown). Rejects with the stream's error on a protocol
95
+ * violation (truncation, over-limit). The pool must await `completed === true` AND require
96
+ * `framing.keepAliveEligible` before reusing the socket; anything else and the next request
97
+ * reads this response's tail. Note it settles as the body is CONSUMED — an unread stream
98
+ * settles nothing (except for bodies that are complete at creation: `none` and length 0).
99
+ * - `trailers`: Promise<Headers|null>. Chunked trailers, or null for other framings / cancel.
100
+ *
101
+ * Bytes are streamed through, never buffered whole; `maxBytes` bounds the total.
102
+ *
103
+ * @param {import('../util/bytes.js').ByteReader} reader
104
+ * @param {Framing} framing
105
+ * @param {ReadBodyOptions} [opts]
106
+ * @returns {BodyStream}
107
+ */
108
+ export function readResponseBody(reader: import("../util/bytes.js").ByteReader, framing: Framing, { maxBytes }?: ReadBodyOptions): BodyStream;
109
+ /**
110
+ * One skipped 1xx head. Kept because Early Hints (103) carry Link headers a caller may want;
111
+ * everything else about a 1xx is noise by definition.
112
+ */
113
+ export type InformationalHead = {
114
+ httpVersion: "1.0" | "1.1";
115
+ status: number;
116
+ statusText: string;
117
+ headers: Headers;
118
+ };
119
+ /**
120
+ * The parsed response head. `setCookie` repeats the raw Set-Cookie values because the Headers
121
+ * class folds duplicates with ", ", which destroys cookie dates — no jar can be built from the
122
+ * folded form.
123
+ */
124
+ export type ResponseHead = {
125
+ /**
126
+ * only versions the status-line grammar admits
127
+ */
128
+ httpVersion: "1.0" | "1.1";
129
+ status: number;
130
+ /**
131
+ * may be empty; `HTTP/1.1 200` is a legal status line
132
+ */
133
+ statusText: string;
134
+ headers: Headers;
135
+ /**
136
+ * one entry per Set-Cookie header, unfolded
137
+ */
138
+ setCookie: string[];
139
+ /**
140
+ * 1xx heads skipped before the real response
141
+ */
142
+ informational: InformationalHead[];
143
+ };
144
+ export type ReadHeadOptions = {
145
+ /**
146
+ * budget for the ENTIRE head phase, default 65536
147
+ */
148
+ maxHeaderBytes?: number | undefined;
149
+ };
150
+ /**
151
+ * How a response body is delimited. The four kinds are exhaustive: RFC 9112 §6.3 admits no
152
+ * fifth, and everything ambiguous throws before a kind is chosen.
153
+ */
154
+ export type FramingKind = "none" | "content-length" | "chunked" | "until-close";
155
+ /**
156
+ * The framing decision. `length` is present exactly when `kind` is 'content-length'.
157
+ */
158
+ export type Framing = {
159
+ kind: FramingKind;
160
+ /**
161
+ * declared byte count, content-length framing only
162
+ */
163
+ length?: number | undefined;
164
+ /**
165
+ * whether the socket MAY be reused after the body ends
166
+ * as framed; see bodyFraming for why this is the load-bearing bit
167
+ */
168
+ keepAliveEligible: boolean;
169
+ };
170
+ /**
171
+ * A response body stream plus the completion contract a connection pool needs. The two extra
172
+ * properties are documented on readResponseBody, which is the only producer.
173
+ */
174
+ export type BodyStream = ReadableStream<Uint8Array> & {
175
+ completed: Promise<boolean>;
176
+ trailers: Promise<Headers | null>;
177
+ };
178
+ export type ReadBodyOptions = {
179
+ /**
180
+ * fail-closed cap on total payload bytes, default unlimited
181
+ */
182
+ maxBytes?: number | undefined;
183
+ };