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,282 @@
1
+ /**
2
+ * Build the ordered HPACK field list for a request, pseudo-headers first in curl's order
3
+ * (:method, :scheme, :authority, :path). :path is emitted "without indexing" and the rest
4
+ * "incremental", matching the captured curl encoding.
5
+ *
6
+ * @param {{ method: string, scheme: string, authority: string, path: string,
7
+ * headers: Array<[string, string]> }} req
8
+ * @returns {import('./hpack.js').HpackField[]}
9
+ */
10
+ export function buildRequestFields({ method, scheme, authority, path, headers }: {
11
+ method: string;
12
+ scheme: string;
13
+ authority: string;
14
+ path: string;
15
+ headers: Array<[string, string]>;
16
+ }): import("./hpack.js").HpackField[];
17
+ /**
18
+ * @typedef {ReadableStream<Uint8Array> & { completed: Promise<boolean>,
19
+ * trailers: Promise<Headers | null> }} BodyStream
20
+ */
21
+ /**
22
+ * @typedef {object} Http2ResponseHead
23
+ * @property {number} status
24
+ * @property {string} statusText always '' — HTTP/2 has no reason phrase
25
+ * @property {Headers} headers
26
+ * @property {string[]} setCookie one entry per set-cookie field, kept separate like the h1 path
27
+ * @property {'2'} httpVersion
28
+ * @property {BodyStream} body
29
+ */
30
+ /**
31
+ * Raised by request() when the connection cannot take the stream but the request PROVABLY was not
32
+ * processed (going away, or refused). It mirrors h1's serverNeverSawIt: only a request the server
33
+ * demonstrably never saw may be re-sent, so client.js can safely open a fresh connection and retry.
34
+ */
35
+ export class Http2Retryable extends Http2Error {
36
+ }
37
+ /**
38
+ * @typedef {object} Http2ConnectionOptions
39
+ * @property {import('../transport.js').ConnectionInfo} [info] provenance attached to responses
40
+ * @property {number} [initialWindowSize] our SETTINGS_INITIAL_WINDOW_SIZE (receive window per
41
+ * stream). Defaults to curl's 10 MiB; tests lower it to exercise flow control.
42
+ * @property {number} [connectionWindow] the connection receive window we open with a WINDOW_UPDATE
43
+ * right after SETTINGS. Defaults to curl's 1000 MiB.
44
+ * @property {number} [maxConcurrentStreams] our advertised SETTINGS_MAX_CONCURRENT_STREAMS.
45
+ * @property {number} [maxHeaderTableSize] our advertised SETTINGS_HEADER_TABLE_SIZE.
46
+ * @property {number} [maxHeaderListSize] self-protection cap on a decoded response header list.
47
+ * @property {(err: Error | null) => void} [onClose] called once when the connection dies, so a
48
+ * registry can drop it.
49
+ */
50
+ export class Http2Connection {
51
+ /**
52
+ * @param {import('./frames.js').ByteDuplex | { readable: ReadableStream<Uint8Array>,
53
+ * writable: WritableStream<Uint8Array> }} duplex plaintext transport (a TLS session's
54
+ * plaintextDuplex, or a raw socket for cleartext h2 in tests)
55
+ * @param {Http2ConnectionOptions} [opts]
56
+ */
57
+ constructor(duplex: import("./frames.js").ByteDuplex | {
58
+ readable: ReadableStream<Uint8Array>;
59
+ writable: WritableStream<Uint8Array>;
60
+ }, opts?: Http2ConnectionOptions);
61
+ _reader: ByteReader;
62
+ _writer: ByteWriter;
63
+ _closeTransport: () => any;
64
+ info: import("../transport.js").ConnectionInfo | null;
65
+ _ourInitialWindow: number;
66
+ _ourConnWindow: number;
67
+ _ourMaxConcurrent: number;
68
+ _ourHeaderTableSize: number;
69
+ _peerInitialWindow: number;
70
+ _peerMaxFrameSize: number;
71
+ _peerMaxConcurrent: number;
72
+ _peerHeaderTableSize: number;
73
+ _connRecvWindow: number;
74
+ _connConsumed: number;
75
+ _connSendWindow: number;
76
+ /** @type {Array<() => void>} wakers for senders blocked on the connection window */
77
+ _connSendWaiters: Array<() => void>;
78
+ _decoder: HpackDecoder;
79
+ /** @type {Map<number, any>} live streams by id */
80
+ _streams: Map<number, any>;
81
+ _nextStreamId: number;
82
+ _lastPeerStreamId: number;
83
+ _continuation: {
84
+ streamId: any;
85
+ fragments: Uint8Array<ArrayBuffer>[];
86
+ endStream: boolean;
87
+ } | null;
88
+ _expectFirstSettings: boolean;
89
+ _fatal: any;
90
+ _goaway: {
91
+ lastStreamId: number;
92
+ errorCode: number;
93
+ } | null;
94
+ _closed: boolean;
95
+ _onClose: ((err: Error | null) => void) | null;
96
+ _writeChain: Promise<void>;
97
+ /** Whether a new request may be dispatched onto this connection right now. */
98
+ canDispatch(): boolean;
99
+ get activeStreams(): number;
100
+ _sendPreface(): void;
101
+ /** Serialise a wire write behind every previous one, so a header block is never split by another
102
+ * frame and the preface always leads. Mirrors the record layer's write discipline. */
103
+ _write(bytes: any): Promise<void>;
104
+ /**
105
+ * Open a stream and send a request. Resolves once the response header block (the first non-1xx
106
+ * HEADERS) has arrived; the body streams after.
107
+ *
108
+ * @param {object} req
109
+ * @param {string} req.method
110
+ * @param {string} req.scheme
111
+ * @param {string} req.authority
112
+ * @param {string} req.path
113
+ * @param {Array<[string, string]>} req.headers already lowercased, connection-specific ones removed
114
+ * @param {Uint8Array | null} [req.body] buffered whole by the caller, so send flow control is simple
115
+ * @param {AbortSignal} [req.signal] aborting it RST_STREAMs the stream and rejects its promises;
116
+ * this is how a per-request deadline tears down exactly one stream without touching the others
117
+ * @returns {Promise<Http2ResponseHead>}
118
+ */
119
+ request({ method, scheme, authority, path, headers, body, signal }: {
120
+ method: string;
121
+ scheme: string;
122
+ authority: string;
123
+ path: string;
124
+ headers: Array<[string, string]>;
125
+ body?: Uint8Array<ArrayBufferLike> | null | undefined;
126
+ signal?: AbortSignal | undefined;
127
+ }): Promise<Http2ResponseHead>;
128
+ _createStream(id: any): {
129
+ id: any;
130
+ head: {
131
+ promise: Promise<any>;
132
+ resolve: undefined;
133
+ reject: undefined;
134
+ settled: boolean;
135
+ };
136
+ responseReceived: boolean;
137
+ /** @type {Uint8Array[]} */
138
+ recvQueue: Uint8Array[];
139
+ recvEnded: boolean;
140
+ /** @type {Error | null} */
141
+ bodyError: Error | null;
142
+ /** @type {(() => void) | null} */
143
+ pullWaiter: (() => void) | null;
144
+ completed: {
145
+ promise: Promise<any>;
146
+ resolve: undefined;
147
+ reject: undefined;
148
+ settled: boolean;
149
+ };
150
+ trailers: {
151
+ promise: Promise<any>;
152
+ resolve: undefined;
153
+ reject: undefined;
154
+ settled: boolean;
155
+ };
156
+ /** @type {Headers | null} */
157
+ trailerFields: Headers | null;
158
+ recvWindow: number;
159
+ recvConsumed: number;
160
+ sendWindow: number;
161
+ /** @type {Array<() => void>} */
162
+ sendWaiters: Array<() => void>;
163
+ localEnded: boolean;
164
+ cancelled: boolean;
165
+ closed: boolean;
166
+ rstSent: boolean;
167
+ };
168
+ /** Send a header block as HEADERS plus CONTINUATION frames if it overflows one frame. The whole
169
+ * run is one write, so no other frame can interleave it (RFC 9113 s6.10). */
170
+ _sendHeaderBlock(streamId: any, block: any, endStream: any): void;
171
+ /** Send a request body as DATA frames, respecting stream and connection send windows. */
172
+ _sendBody(streamId: any, body: any, stream: any): Promise<void>;
173
+ /** Block until either the stream or the connection send window grows (a WINDOW_UPDATE arrives). */
174
+ _awaitSendWindow(stream: any): Promise<any>;
175
+ _wakeSendWaiters(stream: any): void;
176
+ _makeBodyStream(stream: any): ReadableStream<any> & {
177
+ completed: any;
178
+ trailers: any;
179
+ };
180
+ _wakePull(stream: any): void;
181
+ /** Called as the CONSUMER drains `n` bytes: reopen the stream and connection receive windows,
182
+ * batched so a byte-at-a-time consumer does not produce a WINDOW_UPDATE storm. */
183
+ _consumeStream(stream: any, n: any): void;
184
+ _replenish(stream: any, n: any): void;
185
+ _replenishConn(n: any): void;
186
+ _readLoop(): Promise<void>;
187
+ _dispatchFrame(frame: any): void;
188
+ _onSettings(flags: any, streamId: any, payload: any): void;
189
+ _onHeaders(flags: any, streamId: any, payload: any): void;
190
+ _onContinuation(flags: any, payload: any): void;
191
+ /** A full header block has been assembled: HPACK-decode it (connection-fatal on failure, since
192
+ * HPACK state is shared) and route it to the stream as a response head or as trailers. */
193
+ _completeHeaderBlock(streamId: any, block: any, endStream: any): void;
194
+ _deliverResponseHead(stream: any, pairs: any, endStream: any): void;
195
+ _deliverTrailers(stream: any, pairs: any, endStream: any): void;
196
+ _onData(flags: any, streamId: any, payload: any): void;
197
+ _onWindowUpdate(streamId: any, payload: any): void;
198
+ _onRstStream(streamId: any, payload: any): void;
199
+ _onPing(flags: any, streamId: any, payload: any): void;
200
+ _onGoaway(payload: any): void;
201
+ /** Forget a stream: drop it from the table and wake anything blocked on it. */
202
+ _removeStream(stream: any): void;
203
+ /** Reject the caller's promises for a stream. Idempotent via the deferreds' settled flags. */
204
+ _rejectStream(stream: any, err: any): void;
205
+ /** Send RST_STREAM once, telling the peer to stop spending bandwidth on a stream we gave up on. */
206
+ _sendRst(stream: any, errorCode: any): void;
207
+ /** A stream finished cleanly once both halves ended: our request fully sent, END_STREAM received. */
208
+ _maybeCloseStream(stream: any): void;
209
+ /** A failure originating with the PEER (its RST_STREAM, its GOAWAY, a connection death): reject
210
+ * the caller, never RST back — RFC 9113 s5.4.2 forbids answering a reset with a reset. */
211
+ _failStream(stream: any, err: any): void;
212
+ /** A failure originating with US (malformed response, our timeout, flow-control overrun we caught):
213
+ * tell the peer with RST_STREAM, then reject the caller. */
214
+ _resetStream(stream: any, errorCode: any, err: any): void;
215
+ _die(err: any): void;
216
+ /**
217
+ * Graceful shutdown: GOAWAY(NO_ERROR), then close the transport. Any live stream is failed.
218
+ *
219
+ * The GOAWAY and the writer close are BEST-EFFORT and are not awaited: a peer that has stopped
220
+ * reading applies backpressure that never clears, and awaiting a courtesy frame into a full
221
+ * buffer would hang close() forever — the exact trap the record layer avoids with its grace
222
+ * window. Only the transport close is awaited, because that is what actually releases the socket.
223
+ */
224
+ close(): Promise<void>;
225
+ _settleResolve(d: any, value: any): void;
226
+ _settleReject(d: any, err: any): void;
227
+ }
228
+ export type BodyStream = ReadableStream<Uint8Array> & {
229
+ completed: Promise<boolean>;
230
+ trailers: Promise<Headers | null>;
231
+ };
232
+ export type Http2ResponseHead = {
233
+ status: number;
234
+ /**
235
+ * always '' — HTTP/2 has no reason phrase
236
+ */
237
+ statusText: string;
238
+ headers: Headers;
239
+ /**
240
+ * one entry per set-cookie field, kept separate like the h1 path
241
+ */
242
+ setCookie: string[];
243
+ httpVersion: "2";
244
+ body: BodyStream;
245
+ };
246
+ export type Http2ConnectionOptions = {
247
+ /**
248
+ * provenance attached to responses
249
+ */
250
+ info?: import("../transport.js").ConnectionInfo | undefined;
251
+ /**
252
+ * our SETTINGS_INITIAL_WINDOW_SIZE (receive window per
253
+ * stream). Defaults to curl's 10 MiB; tests lower it to exercise flow control.
254
+ */
255
+ initialWindowSize?: number | undefined;
256
+ /**
257
+ * the connection receive window we open with a WINDOW_UPDATE
258
+ * right after SETTINGS. Defaults to curl's 1000 MiB.
259
+ */
260
+ connectionWindow?: number | undefined;
261
+ /**
262
+ * our advertised SETTINGS_MAX_CONCURRENT_STREAMS.
263
+ */
264
+ maxConcurrentStreams?: number | undefined;
265
+ /**
266
+ * our advertised SETTINGS_HEADER_TABLE_SIZE.
267
+ */
268
+ maxHeaderTableSize?: number | undefined;
269
+ /**
270
+ * self-protection cap on a decoded response header list.
271
+ */
272
+ maxHeaderListSize?: number | undefined;
273
+ /**
274
+ * called once when the connection dies, so a
275
+ * registry can drop it.
276
+ */
277
+ onClose?: ((err: Error | null) => void) | undefined;
278
+ };
279
+ import { Http2Error } from '../errors.js';
280
+ import { ByteReader } from '../util/bytes.js';
281
+ import { ByteWriter } from '../util/bytes.js';
282
+ import { HpackDecoder } from './hpack.js';
@@ -0,0 +1,95 @@
1
+ /** The 24-byte client connection preface (RFC 9113 s3.4). Sent before any frame. */
2
+ export const CONNECTION_PREFACE: Uint8Array<ArrayBuffer>;
3
+ export namespace FRAME {
4
+ let DATA: number;
5
+ let HEADERS: number;
6
+ let PRIORITY: number;
7
+ let RST_STREAM: number;
8
+ let SETTINGS: number;
9
+ let PUSH_PROMISE: number;
10
+ let PING: number;
11
+ let GOAWAY: number;
12
+ let WINDOW_UPDATE: number;
13
+ let CONTINUATION: number;
14
+ }
15
+ /** Reverse map for diagnostics: an unknown type still gets a number. */
16
+ export const FRAME_NAME: {
17
+ [k: string]: string;
18
+ };
19
+ export namespace FLAG {
20
+ export let END_STREAM: number;
21
+ export let ACK: number;
22
+ export let END_HEADERS: number;
23
+ export let PADDED: number;
24
+ let PRIORITY_1: number;
25
+ export { PRIORITY_1 as PRIORITY };
26
+ }
27
+ export namespace SETTINGS {
28
+ let HEADER_TABLE_SIZE: number;
29
+ let ENABLE_PUSH: number;
30
+ let MAX_CONCURRENT_STREAMS: number;
31
+ let INITIAL_WINDOW_SIZE: number;
32
+ let MAX_FRAME_SIZE: number;
33
+ let MAX_HEADER_LIST_SIZE: number;
34
+ }
35
+ export const SETTINGS_NAME: {
36
+ [k: string]: string;
37
+ };
38
+ export namespace H2_ERROR {
39
+ let NO_ERROR: number;
40
+ let PROTOCOL_ERROR: number;
41
+ let INTERNAL_ERROR: number;
42
+ let FLOW_CONTROL_ERROR: number;
43
+ let SETTINGS_TIMEOUT: number;
44
+ let STREAM_CLOSED: number;
45
+ let FRAME_SIZE_ERROR: number;
46
+ let REFUSED_STREAM: number;
47
+ let CANCEL: number;
48
+ let COMPRESSION_ERROR: number;
49
+ let CONNECT_ERROR: number;
50
+ let ENHANCE_YOUR_CALM: number;
51
+ let INADEQUATE_SECURITY: number;
52
+ let HTTP_1_1_REQUIRED: number;
53
+ }
54
+ export const H2_ERROR_NAME: {
55
+ [k: string]: string;
56
+ };
57
+ /** The default SETTINGS_MAX_FRAME_SIZE and its floor (RFC 9113 s6.5.2): 2^14. A peer may raise
58
+ * its own limit, but until it says so in SETTINGS we must not send a frame larger than this. */
59
+ export const DEFAULT_MAX_FRAME_SIZE: 16384;
60
+ /** The ceiling a peer may set MAX_FRAME_SIZE to: 2^24 - 1. Beyond it, SETTINGS is a PROTOCOL_ERROR. */
61
+ export const MAX_ALLOWED_FRAME_SIZE: 16777215;
62
+ /** The default per-stream/-connection flow-control window before any SETTINGS/WINDOW_UPDATE. */
63
+ export const DEFAULT_INITIAL_WINDOW: 65535;
64
+ /** The largest a flow-control window may reach; exceeding it is a FLOW_CONTROL_ERROR (s6.9.1). */
65
+ export const MAX_WINDOW: 2147483647;
66
+ /**
67
+ * The SETTINGS we advertise, and the ORDER we advertise them in. Both are matched to curl
68
+ * 8.7.1 / nghttp2 1.69.0 as captured on the wire:
69
+ *
70
+ * SETTINGS: MAX_CONCURRENT_STREAMS=100, INITIAL_WINDOW_SIZE=10485760, ENABLE_PUSH=0
71
+ * emitted in the id order 3, 4, 2 — and nothing else (no HEADER_TABLE_SIZE, no MAX_FRAME_SIZE,
72
+ * no MAX_HEADER_LIST_SIZE), so those stay at their protocol defaults exactly as curl leaves them.
73
+ *
74
+ * These are the values the SERVER sees and fingerprints on, so they are held fixed here rather
75
+ * than exposed as knobs. The receive-side buffering they imply is bounded elsewhere (see the
76
+ * flow-control note in connection.js): a large advertised window is a fingerprint choice, not a
77
+ * promise to buffer that much before applying backpressure.
78
+ */
79
+ export const CLIENT_INITIAL_WINDOW_SIZE: 10485760;
80
+ export const CLIENT_MAX_CONCURRENT_STREAMS: 100;
81
+ /** curl raises the CONNECTION receive window to exactly 1000 MiB with one WINDOW_UPDATE on
82
+ * stream 0 right after SETTINGS. The increment below is 1000 MiB - 65535, i.e. what takes the
83
+ * default 65535 window up to 1048576000. Sent in the same preface flight, same order as curl. */
84
+ export const CLIENT_CONNECTION_WINDOW: 1048576000;
85
+ export const CLIENT_CONNECTION_WINDOW_INCREMENT: number;
86
+ /**
87
+ * The order pseudo-headers are emitted in a request HEADERS block. curl/nghttp2 sends
88
+ * :method, :scheme, :authority, :path — captured, not guessed. h2 fingerprinters read this order
89
+ * (the "m,s,a,p" tail of an Akamai-style h2 fingerprint), so it is fixed to match.
90
+ */
91
+ export const PSEUDO_HEADER_ORDER: string[];
92
+ /** ALPN identifiers. Offering both and following the server's pick is the whole ALPN contract;
93
+ * there is no reconnect-and-retry path if the server picks the other one. */
94
+ export const ALPN_H2: "h2";
95
+ export const ALPN_HTTP11: "http/1.1";
@@ -0,0 +1,116 @@
1
+ /**
2
+ * One frame off the wire. `payload` aliases the reader's buffer and must not be retained.
3
+ * @typedef {object} Frame
4
+ * @property {number} type
5
+ * @property {number} flags
6
+ * @property {number} streamId
7
+ * @property {Uint8Array} payload
8
+ */
9
+ /**
10
+ * Read exactly one frame. Enforces our advertised SETTINGS_MAX_FRAME_SIZE on receipt: a peer
11
+ * that sends a larger frame commits a FRAME_SIZE_ERROR (RFC 9113 s4.2), and refusing at the
12
+ * header — before reading the body — is what bounds how much a hostile peer can make us buffer.
13
+ * Returns null only on a clean EOF exactly at a frame boundary.
14
+ *
15
+ * @param {import('../util/bytes.js').ByteReader} reader
16
+ * @param {number} maxFrameSize the largest payload we will accept
17
+ * @returns {Promise<Frame | null>}
18
+ */
19
+ export function readFrame(reader: import("../util/bytes.js").ByteReader, maxFrameSize?: number): Promise<Frame | null>;
20
+ /**
21
+ * Serialise one frame (header + payload) into a single buffer.
22
+ * @param {number} type
23
+ * @param {number} flags
24
+ * @param {number} streamId
25
+ * @param {Uint8Array} payload
26
+ * @returns {Uint8Array}
27
+ */
28
+ export function serializeFrame(type: number, flags: number, streamId: number, payload?: Uint8Array): Uint8Array;
29
+ /**
30
+ * A SETTINGS frame. `entries` is an array of [id, value]; order is preserved because it is part
31
+ * of the client fingerprint. An ACK carries no payload and sets the ACK flag.
32
+ * @param {Array<[number, number]>} entries
33
+ * @param {boolean} [ack]
34
+ * @returns {Uint8Array}
35
+ */
36
+ export function settingsFrame(entries: Array<[number, number]>, ack?: boolean): Uint8Array;
37
+ /** A WINDOW_UPDATE frame (RFC 9113 s6.9). streamId 0 is connection-level. */
38
+ export function windowUpdateFrame(streamId: any, increment: any): Uint8Array<ArrayBufferLike>;
39
+ /** A RST_STREAM frame (RFC 9113 s6.4). */
40
+ export function rstStreamFrame(streamId: any, errorCode: any): Uint8Array<ArrayBufferLike>;
41
+ /** A PING frame (RFC 9113 s6.7). The 8-byte opaque data is echoed on ACK. */
42
+ export function pingFrame(opaque: any, ack?: boolean): Uint8Array<ArrayBufferLike>;
43
+ /** A GOAWAY frame (RFC 9113 s6.8). */
44
+ export function goawayFrame(lastStreamId: any, errorCode: any, debug?: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBufferLike>;
45
+ /** A HEADERS frame carrying a full (already-fragmented-if-needed) block. No PADDED, no PRIORITY —
46
+ * a client that emits neither is exactly what curl does. */
47
+ export function headersFrame(streamId: any, block: any, { endStream, endHeaders }?: {
48
+ endStream?: boolean | undefined;
49
+ endHeaders?: boolean | undefined;
50
+ }): Uint8Array<ArrayBufferLike>;
51
+ /** A CONTINUATION frame (RFC 9113 s6.10), for a header block that overflows one frame. */
52
+ export function continuationFrame(streamId: any, block: any, endHeaders: any): Uint8Array<ArrayBufferLike>;
53
+ /** A DATA frame (RFC 9113 s6.1). No padding is ever emitted. */
54
+ export function dataFrame(streamId: any, data: any, endStream: any): Uint8Array<ArrayBufferLike>;
55
+ /**
56
+ * Strip a DATA/HEADERS PADDED frame's pad length and padding, returning the meaningful slice.
57
+ * The pad length byte and padding both count against flow control (the caller handles that);
58
+ * this only removes them from the bytes handed onward. A pad length >= the remaining payload is
59
+ * a PROTOCOL_ERROR (RFC 9113 s6.1).
60
+ * @param {Uint8Array} payload
61
+ * @returns {{ data: Uint8Array, padLength: number }}
62
+ */
63
+ export function stripPadding(payload: Uint8Array): {
64
+ data: Uint8Array;
65
+ padLength: number;
66
+ };
67
+ /**
68
+ * The header block fragment inside a HEADERS payload, after removing padding (PADDED) and the
69
+ * priority fields (PRIORITY). Priority is deprecated (RFC 9113 s5.3.2) and its fields are
70
+ * discarded, not acted on.
71
+ * @param {Uint8Array} payload
72
+ * @param {number} flags
73
+ * @returns {Uint8Array}
74
+ */
75
+ export function headersBlockFragment(payload: Uint8Array, flags: number): Uint8Array;
76
+ /**
77
+ * Parse a SETTINGS payload into [id, value] pairs. Length must be a multiple of 6
78
+ * (RFC 9113 s6.5); anything else is a FRAME_SIZE_ERROR.
79
+ * @param {Uint8Array} payload
80
+ * @returns {Array<[number, number]>}
81
+ */
82
+ export function parseSettings(payload: Uint8Array): Array<[number, number]>;
83
+ /**
84
+ * Parse a WINDOW_UPDATE payload. Must be exactly 4 bytes; a zero increment is a protocol error
85
+ * (RFC 9113 s6.9), surfaced by the caller which knows the stream context.
86
+ * @param {Uint8Array} payload
87
+ * @returns {number} the 31-bit increment
88
+ */
89
+ export function parseWindowUpdate(payload: Uint8Array): number;
90
+ /**
91
+ * Parse a RST_STREAM payload (exactly 4 bytes: an error code).
92
+ * @param {Uint8Array} payload
93
+ * @returns {number}
94
+ */
95
+ export function parseRstStream(payload: Uint8Array): number;
96
+ /**
97
+ * Parse a GOAWAY payload (RFC 9113 s6.8): last-stream-id, error code, optional debug data.
98
+ * @param {Uint8Array} payload
99
+ * @returns {{ lastStreamId: number, errorCode: number, debug: Uint8Array }}
100
+ */
101
+ export function parseGoaway(payload: Uint8Array): {
102
+ lastStreamId: number;
103
+ errorCode: number;
104
+ debug: Uint8Array;
105
+ };
106
+ /** The fixed frame header size (RFC 9113 s4.1): 24-bit length, type, flags, 31-bit stream id. */
107
+ export const FRAME_HEADER_SIZE: 9;
108
+ /**
109
+ * One frame off the wire. `payload` aliases the reader's buffer and must not be retained.
110
+ */
111
+ export type Frame = {
112
+ type: number;
113
+ flags: number;
114
+ streamId: number;
115
+ payload: Uint8Array;
116
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @typedef {object} HpackField
3
+ * @property {string} name already lowercased by the caller — HPACK does not case-fold, and
4
+ * HTTP/2 forbids uppercase field names on the wire (RFC 9113 s8.2.1)
5
+ * @property {string} value
6
+ * @property {'incremental' | 'without' | 'never'} [indexing] how to represent it when it is not a
7
+ * full static match. Default 'incremental', which is what curl uses for most fields.
8
+ */
9
+ /**
10
+ * Encode an ordered list of header fields into one HPACK block. Order is preserved exactly, which
11
+ * is load-bearing: HTTP/2 requires all pseudo-headers before regular ones, and the pseudo-header
12
+ * ORDER is part of the client fingerprint the caller is matching.
13
+ *
14
+ * @param {HpackField[]} fields
15
+ * @returns {Uint8Array}
16
+ */
17
+ export function encodeHeaderBlock(fields: HpackField[]): Uint8Array;
18
+ /** Default SETTINGS_HEADER_TABLE_SIZE (RFC 9113 s6.5.2): the size we advertise, so the size the
19
+ * peer's encoder may fill in our decoder. A size update above this is a COMPRESSION_ERROR. */
20
+ export const DEFAULT_HEADER_TABLE_SIZE: 4096;
21
+ /**
22
+ * @typedef {object} HpackDecoderOptions
23
+ * @property {number} [maxTableSize] the SETTINGS_HEADER_TABLE_SIZE we advertised; the dynamic
24
+ * table may not exceed it and a size update above it is fatal. Default 4096.
25
+ * @property {number} [maxHeaderListSize] fail-closed cap on the decoded header list (sum of
26
+ * name+value+32 per field). Default 262144. We do not advertise this to the peer, so it is a
27
+ * self-protection bound, not a promise; over it is a LimitError.
28
+ */
29
+ /**
30
+ * A stateful HPACK decoder. One per connection per direction: the dynamic table is shared state
31
+ * across every header block on the connection, so a single decoder instance must live as long as
32
+ * the connection does.
33
+ */
34
+ export class HpackDecoder {
35
+ /** @param {HpackDecoderOptions} [opts] */
36
+ constructor({ maxTableSize, maxHeaderListSize }?: HpackDecoderOptions);
37
+ /** @type {Array<[string, string, number]>} name, value, entry-size — newest at index 0 */
38
+ _dynamic: Array<[string, string, number]>;
39
+ _size: number;
40
+ _maxSize: number;
41
+ _limit: number;
42
+ _maxHeaderListSize: number;
43
+ /** Current number of entries, exposed for tests that assert eviction. */
44
+ get dynamicLength(): number;
45
+ /**
46
+ * Look up index `i` (RFC 7541 s2.3.3: 1..STATIC_COUNT is static, above that is dynamic,
47
+ * newest first). Fails closed on 0 and on anything past the end of the dynamic table.
48
+ * @param {number} i
49
+ * @returns {[string, string]}
50
+ */
51
+ _lookup(i: number): [string, string];
52
+ /** Insert into the dynamic table, evicting oldest entries until it fits (RFC 7541 s4.4). */
53
+ _insert(name: any, value: any): void;
54
+ /** Apply a dynamic table size update (RFC 7541 s6.3), refusing one above what we advertised. */
55
+ _resize(newSize: any): void;
56
+ /**
57
+ * Decode one header block into an ordered list of [name, value] pairs.
58
+ * @param {Uint8Array} block
59
+ * @returns {Array<[string, string]>}
60
+ */
61
+ decode(block: Uint8Array): Array<[string, string]>;
62
+ /**
63
+ * Shared body of the three literal representations: an optionally indexed name, then a value.
64
+ * @param {(name: string, value: string) => void} add
65
+ * @param {null | ((name: string, value: string) => void)} index insert into the dynamic table, or null
66
+ */
67
+ _literal(block: any, pos: any, prefixBits: any, _fieldIndex: any, add: (name: string, value: string) => void, index: null | ((name: string, value: string) => void)): {
68
+ pos: number;
69
+ };
70
+ }
71
+ export type HpackField = {
72
+ /**
73
+ * already lowercased by the caller — HPACK does not case-fold, and
74
+ * HTTP/2 forbids uppercase field names on the wire (RFC 9113 s8.2.1)
75
+ */
76
+ name: string;
77
+ value: string;
78
+ /**
79
+ * how to represent it when it is not a
80
+ * full static match. Default 'incremental', which is what curl uses for most fields.
81
+ */
82
+ indexing?: "incremental" | "without" | "never" | undefined;
83
+ };
84
+ export type HpackDecoderOptions = {
85
+ /**
86
+ * the SETTINGS_HEADER_TABLE_SIZE we advertised; the dynamic
87
+ * table may not exceed it and a size update above it is fatal. Default 4096.
88
+ */
89
+ maxTableSize?: number | undefined;
90
+ /**
91
+ * fail-closed cap on the decoded header list (sum of
92
+ * name+value+32 per field). Default 262144. We do not advertise this to the peer, so it is a
93
+ * self-protection bound, not a promise; over it is a LimitError.
94
+ */
95
+ maxHeaderListSize?: number | undefined;
96
+ };
97
+ /** RFC 7541 Appendix A. Index 0 is a placeholder so table[i] is the RFC's entry i (1-based). */
98
+ export const STATIC_TABLE: string[][];
99
+ export const STATIC_COUNT: number;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The number of bytes `bytes` would occupy Huffman-encoded, so a caller can choose the shorter
3
+ * of literal and Huffman exactly the way nghttp2 does (this is what makes curl leave the accept
4
+ * value un-encoded when its Huffman form is not shorter).
5
+ * @param {Uint8Array} bytes
6
+ * @returns {number}
7
+ */
8
+ export function huffmanEncodedLength(bytes: Uint8Array): number;
9
+ /**
10
+ * Huffman-encode octets, padding the final byte with the most-significant bits of EOS (all ones)
11
+ * per RFC 7541 s5.2.
12
+ * @param {Uint8Array} bytes
13
+ * @returns {Uint8Array}
14
+ */
15
+ export function huffmanEncode(bytes: Uint8Array): Uint8Array;
16
+ /**
17
+ * Huffman-decode octets, failing closed on every error RFC 7541 s5.2 names.
18
+ * @param {Uint8Array} bytes
19
+ * @returns {Uint8Array}
20
+ */
21
+ export function huffmanDecode(bytes: Uint8Array): Uint8Array;
@@ -0,0 +1,5 @@
1
+ export { Http2Connection, Http2Retryable, buildRequestFields } from "./connection.js";
2
+ export { HpackDecoder, encodeHeaderBlock } from "./hpack.js";
3
+ export { huffmanDecode, huffmanEncode } from "./huffman.js";
4
+ export { readFrame, serializeFrame, settingsFrame, windowUpdateFrame, dataFrame, headersFrame, parseSettings } from "./frames.js";
5
+ export { ALPN_H2, ALPN_HTTP11 } from "./constants.js";
@@ -0,0 +1,17 @@
1
+ export { warmup } from "./warmup.js";
2
+ export { CookieJar } from "./client/cookies.js";
3
+ /**
4
+ * A ready-made fetch with default options: no proxy, system trust, one connection per call.
5
+ * Equivalent to httpx.get() versus httpx.Client() — reach for `new Client()` when connection
6
+ * reuse or a cookie jar matters, which for anything crawl-shaped it does.
7
+ *
8
+ * A `connect` function must still be supplied per call (or via `createFetch`) for any request the
9
+ * platform's own fetch cannot serve; see the README for why that is not defaulted.
10
+ */
11
+ export const fetch: import("./client.js").FetchLike;
12
+ export { Client, createFetch, install } from "./client.js";
13
+ export { ConnectionPool, poolKey } from "./pool.js";
14
+ export { openConnection, targetFromUrl, nativeFetchCanServe } from "./transport.js";
15
+ export { openTunnel, parseProxy } from "./proxy/index.js";
16
+ export { verifyChain, rootStoreProvenance } from "./trust/index.js";
17
+ export { TunnelFetchError, ProxyError, HttpError, TlsError, TlsUnsupportedError, Http2Error, CertificateError, TimeoutError, LimitError, ConfigError, codes } from "./errors.js";