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.
- package/LICENSE +28 -0
- package/README.md +617 -0
- package/README.zh-CN.md +470 -0
- package/package.json +74 -0
- package/src/client/cookies.js +429 -0
- package/src/client/decode.js +346 -0
- package/src/client/redirect.js +249 -0
- package/src/client.js +704 -0
- package/src/errors.js +181 -0
- package/src/http1/chunked.js +289 -0
- package/src/http1/index.js +10 -0
- package/src/http1/request.js +143 -0
- package/src/http1/response.js +493 -0
- package/src/http2/connection.js +1170 -0
- package/src/http2/constants.js +129 -0
- package/src/http2/frames.js +291 -0
- package/src/http2/hpack.js +420 -0
- package/src/http2/huffman.js +203 -0
- package/src/http2/index.js +21 -0
- package/src/index.js +46 -0
- package/src/pool.js +256 -0
- package/src/proxy/direct.js +62 -0
- package/src/proxy/http-connect.js +206 -0
- package/src/proxy/index.js +197 -0
- package/src/proxy/socks5.js +344 -0
- package/src/tls/aead.js +263 -0
- package/src/tls/connect.js +407 -0
- package/src/tls/constants.js +334 -0
- package/src/tls/extensions.js +376 -0
- package/src/tls/handshake-messages.js +901 -0
- package/src/tls/handshake.js +568 -0
- package/src/tls/handshake12.js +507 -0
- package/src/tls/index.js +44 -0
- package/src/tls/keyschedule.js +473 -0
- package/src/tls/record.js +872 -0
- package/src/tls/tickets.js +145 -0
- package/src/tls/transcript.js +101 -0
- package/src/tls/wire.js +224 -0
- package/src/transport.js +296 -0
- package/src/trust/der.js +551 -0
- package/src/trust/index.js +375 -0
- package/src/trust/name.js +235 -0
- package/src/trust/ocsp.js +759 -0
- package/src/trust/path.js +595 -0
- package/src/trust/roots.js +454 -0
- package/src/trust/x509.js +902 -0
- package/src/util/bytes.js +470 -0
- package/src/util/deadline.js +266 -0
- package/src/warmup-fixture.js +85 -0
- package/src/warmup.js +243 -0
- package/types/client/cookies.d.ts +159 -0
- package/types/client/decode.d.ts +54 -0
- package/types/client/redirect.d.ts +96 -0
- package/types/client.d.ts +323 -0
- package/types/errors.d.ts +141 -0
- package/types/http1/chunked.d.ts +48 -0
- package/types/http1/index.d.ts +3 -0
- package/types/http1/request.d.ts +44 -0
- package/types/http1/response.d.ts +183 -0
- package/types/http2/connection.d.ts +282 -0
- package/types/http2/constants.d.ts +95 -0
- package/types/http2/frames.d.ts +116 -0
- package/types/http2/hpack.d.ts +99 -0
- package/types/http2/huffman.d.ts +21 -0
- package/types/http2/index.d.ts +5 -0
- package/types/index.d.ts +17 -0
- package/types/pool.d.ts +135 -0
- package/types/proxy/direct.d.ts +26 -0
- package/types/proxy/http-connect.d.ts +37 -0
- package/types/proxy/index.d.ts +62 -0
- package/types/proxy/socks5.d.ts +47 -0
- package/types/tls/aead.d.ts +67 -0
- package/types/tls/connect.d.ts +280 -0
- package/types/tls/constants.d.ts +275 -0
- package/types/tls/extensions.d.ts +195 -0
- package/types/tls/handshake-messages.d.ts +430 -0
- package/types/tls/handshake.d.ts +90 -0
- package/types/tls/handshake12.d.ts +35 -0
- package/types/tls/index.d.ts +9 -0
- package/types/tls/keyschedule.d.ts +272 -0
- package/types/tls/record.d.ts +361 -0
- package/types/tls/tickets.d.ts +66 -0
- package/types/tls/transcript.d.ts +52 -0
- package/types/tls/wire.d.ts +106 -0
- package/types/transport.d.ts +222 -0
- package/types/trust/der.d.ts +239 -0
- package/types/trust/index.d.ts +194 -0
- package/types/trust/name.d.ts +33 -0
- package/types/trust/ocsp.d.ts +138 -0
- package/types/trust/path.d.ts +139 -0
- package/types/trust/roots.d.ts +36 -0
- package/types/trust/x509.d.ts +401 -0
- package/types/util/bytes.d.ts +183 -0
- package/types/util/deadline.d.ts +133 -0
- package/types/warmup-fixture.d.ts +11 -0
- package/types/warmup.d.ts +45 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a cookie date per RFC 6265 s5.1.1 — NOT Date.parse. Date.parse accepts formats the
|
|
3
|
+
* RFC rejects, rejects formats the RFC accepts (two-digit years, odd delimiters), and differs
|
|
4
|
+
* between engines; cookies from 1990s-era servers still use every shape the RFC grandfathers.
|
|
5
|
+
*
|
|
6
|
+
* The algorithm: split into tokens on "delimiters", then find — in any order, first match per
|
|
7
|
+
* category wins — a time (hh:mm:ss), a day (1-2 digits), a month (3-letter name), and a year
|
|
8
|
+
* (2-4 digits). Each token may carry trailing non-digit junk after the match.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} s
|
|
11
|
+
* @returns {number|null} epoch milliseconds UTC, or null if the string is not a cookie date.
|
|
12
|
+
*/
|
|
13
|
+
export function parseCookieDate(s: string): number | null;
|
|
14
|
+
/**
|
|
15
|
+
* RFC 6265 s5.1.4: the default path is the request path up to (not including) its last '/'.
|
|
16
|
+
* @param {string} requestPath
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function defaultPath(requestPath: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* RFC 6265 s5.1.4 path-match.
|
|
22
|
+
* @param {string} requestPath
|
|
23
|
+
* @param {string} cookiePath
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
export function pathMatches(requestPath: string, cookiePath: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* RFC 6265 s5.1.3 domain-match: exact, or host ends with '.' + domain.
|
|
29
|
+
* @param {string} host
|
|
30
|
+
* @param {string} cookieDomain
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
export function domainMatches(host: string, cookieDomain: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* A stored cookie, as entries() exposes it. Records are live jar state, not copies.
|
|
36
|
+
* @typedef {object} Cookie
|
|
37
|
+
* @property {string} name
|
|
38
|
+
* @property {string} value quotes already stripped
|
|
39
|
+
* @property {string} domain lowercased; the Domain attribute, or the request host
|
|
40
|
+
* @property {boolean} hostOnly true when no Domain attribute applied — exact-host match only
|
|
41
|
+
* @property {string} path
|
|
42
|
+
* @property {boolean} secure
|
|
43
|
+
* @property {boolean} httpOnly
|
|
44
|
+
* @property {string | null} sameSite lowercased attribute value, stored verbatim — servers send
|
|
45
|
+
* values outside strict/lax/none and this jar does not enforce SameSite anyway
|
|
46
|
+
* @property {number} expiry epoch ms; Infinity for a session cookie
|
|
47
|
+
* @property {number} creation epoch ms from the injected clock, kept across overwrites (s5.3)
|
|
48
|
+
* @property {number} seq creation-order tiebreak for the frozen-clock runtime
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {object} CookieJarOptions
|
|
52
|
+
* @property {() => number} [now] injectable clock returning epoch ms. On the target runtime
|
|
53
|
+
* Date.now() freezes for a whole execution slice, so expiry must be testable via this knob.
|
|
54
|
+
* @property {number} [maxCookies] global cap, default 3000
|
|
55
|
+
* @property {number} [maxPerDomain] per-domain cap, default 50
|
|
56
|
+
*/
|
|
57
|
+
export class CookieJar {
|
|
58
|
+
/**
|
|
59
|
+
* @param {CookieJarOptions} [options]
|
|
60
|
+
* The caps exist because this jar lives inside a long-lived Worker isolate: an unbounded
|
|
61
|
+
* jar fed by a hostile or merely enthusiastic server is a slow memory leak, so overflow
|
|
62
|
+
* evicts the oldest cookies instead of growing.
|
|
63
|
+
*/
|
|
64
|
+
constructor({ now, maxCookies, maxPerDomain }?: CookieJarOptions);
|
|
65
|
+
_now: () => number;
|
|
66
|
+
_maxCookies: number;
|
|
67
|
+
_maxPerDomain: number;
|
|
68
|
+
/** @type {Map<string, Cookie>} key "domain|path|name" -> cookie record */
|
|
69
|
+
_cookies: Map<string, Cookie>;
|
|
70
|
+
_rejected: number;
|
|
71
|
+
/** Count of Set-Cookie values ignored per RFC rules — the observability hook. */
|
|
72
|
+
get rejected(): number;
|
|
73
|
+
get size(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Ingest the Set-Cookie values of one response. Never throws on a bad cookie — rejection is
|
|
76
|
+
* silent per RFC 6265, counted in `rejected`.
|
|
77
|
+
* @param {string|URL} url the request URL the response belongs to
|
|
78
|
+
* @param {string[]} setCookieValues one array entry per Set-Cookie header
|
|
79
|
+
* @returns {void}
|
|
80
|
+
*/
|
|
81
|
+
setFromResponse(url: string | URL, setCookieValues: string[]): void;
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} host
|
|
84
|
+
* @param {boolean} requestSecure
|
|
85
|
+
* @param {string} requestPath
|
|
86
|
+
* @param {string} setCookie
|
|
87
|
+
* @returns {boolean} stored or deliberately deleted (true) vs ignored (false)
|
|
88
|
+
*/
|
|
89
|
+
_setOne(host: string, requestSecure: boolean, requestPath: string, setCookie: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Evict expired cookies first, then the oldest by creation, per-domain then globally.
|
|
92
|
+
* @param {string} domain
|
|
93
|
+
*/
|
|
94
|
+
_enforceCaps(domain: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* The Cookie header value for a request, or null if no cookie matches.
|
|
97
|
+
* @param {string|URL} url
|
|
98
|
+
* @returns {string | null}
|
|
99
|
+
*/
|
|
100
|
+
headerFor(url: string | URL): string | null;
|
|
101
|
+
/**
|
|
102
|
+
* Everything currently stored, for tests and debugging. Records are live; do not mutate.
|
|
103
|
+
* @returns {Cookie[]}
|
|
104
|
+
*/
|
|
105
|
+
entries(): Cookie[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A stored cookie, as entries() exposes it. Records are live jar state, not copies.
|
|
109
|
+
*/
|
|
110
|
+
export type Cookie = {
|
|
111
|
+
name: string;
|
|
112
|
+
/**
|
|
113
|
+
* quotes already stripped
|
|
114
|
+
*/
|
|
115
|
+
value: string;
|
|
116
|
+
/**
|
|
117
|
+
* lowercased; the Domain attribute, or the request host
|
|
118
|
+
*/
|
|
119
|
+
domain: string;
|
|
120
|
+
/**
|
|
121
|
+
* true when no Domain attribute applied — exact-host match only
|
|
122
|
+
*/
|
|
123
|
+
hostOnly: boolean;
|
|
124
|
+
path: string;
|
|
125
|
+
secure: boolean;
|
|
126
|
+
httpOnly: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* lowercased attribute value, stored verbatim — servers send
|
|
129
|
+
* values outside strict/lax/none and this jar does not enforce SameSite anyway
|
|
130
|
+
*/
|
|
131
|
+
sameSite: string | null;
|
|
132
|
+
/**
|
|
133
|
+
* epoch ms; Infinity for a session cookie
|
|
134
|
+
*/
|
|
135
|
+
expiry: number;
|
|
136
|
+
/**
|
|
137
|
+
* epoch ms from the injected clock, kept across overwrites (s5.3)
|
|
138
|
+
*/
|
|
139
|
+
creation: number;
|
|
140
|
+
/**
|
|
141
|
+
* creation-order tiebreak for the frozen-clock runtime
|
|
142
|
+
*/
|
|
143
|
+
seq: number;
|
|
144
|
+
};
|
|
145
|
+
export type CookieJarOptions = {
|
|
146
|
+
/**
|
|
147
|
+
* injectable clock returning epoch ms. On the target runtime
|
|
148
|
+
* Date.now() freezes for a whole execution slice, so expiry must be testable via this knob.
|
|
149
|
+
*/
|
|
150
|
+
now?: (() => number) | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* global cap, default 3000
|
|
153
|
+
*/
|
|
154
|
+
maxCookies?: number | undefined;
|
|
155
|
+
/**
|
|
156
|
+
* per-domain cap, default 50
|
|
157
|
+
*/
|
|
158
|
+
maxPerDomain?: number | undefined;
|
|
159
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undo the response's Content-Encoding.
|
|
3
|
+
*
|
|
4
|
+
* @param {ReadableStream<Uint8Array>} stream the raw body
|
|
5
|
+
* @param {string|null|undefined} contentEncoding the Content-Encoding header value; a
|
|
6
|
+
* comma-separated list names codings in the order the SERVER applied them, so decoding
|
|
7
|
+
* applies them in reverse.
|
|
8
|
+
* @returns {ReadableStream<Uint8Array>} decoded bytes
|
|
9
|
+
*/
|
|
10
|
+
export function decodeBody(stream: ReadableStream<Uint8Array>, contentEncoding: string | null | undefined): ReadableStream<Uint8Array>;
|
|
11
|
+
/**
|
|
12
|
+
* Extract the charset parameter from a Content-Type value, handling quoting and other
|
|
13
|
+
* parameters: `text/html; boundary=x; charset="ISO-8859-4"` -> 'iso-8859-4'.
|
|
14
|
+
* @param {string | null | undefined} contentType
|
|
15
|
+
* @returns {string|null} lowercased charset label, or null when none is declared
|
|
16
|
+
*/
|
|
17
|
+
export function charsetFromContentType(contentType: string | null | undefined): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Decide the charset for a response body.
|
|
20
|
+
*
|
|
21
|
+
* Precedence: BOM > Content-Type charset parameter > (text/html only) meta prescan > utf-8.
|
|
22
|
+
* The BOM outranks even an explicit header because it describes the actual bytes, and servers
|
|
23
|
+
* that recode content routinely forget to update the header; this is WHATWG "decode" order.
|
|
24
|
+
* The utf-8 default matches Response.text() in fetch — for a client whose callers are code,
|
|
25
|
+
* matching fetch is worth more than matching the legacy HTML default of windows-1252.
|
|
26
|
+
*
|
|
27
|
+
* @param {string|null|undefined} contentType
|
|
28
|
+
* @param {Uint8Array} [bodyPrefix] the first bytes of the body (>= 1024 to satisfy the prescan)
|
|
29
|
+
* @returns {string} a charset label for decodeText
|
|
30
|
+
*/
|
|
31
|
+
export function charsetFor(contentType: string | null | undefined, bodyPrefix?: Uint8Array): string;
|
|
32
|
+
/**
|
|
33
|
+
* Decode bytes with a charset label.
|
|
34
|
+
*
|
|
35
|
+
* TextDecoder implements the WHATWG encoding registry, which is the alias table every browser
|
|
36
|
+
* uses. Note one alias that looks like a bug and is not: `iso-8859-1` (and `latin1`, `ascii`)
|
|
37
|
+
* maps to windows-1252, per WHATWG — the bytes 0x80-0x9F decode to the punctuation everyone
|
|
38
|
+
* actually means, not C1 controls. A BOM matching the charset is stripped (TextDecoder default),
|
|
39
|
+
* which is also what Response.text() does.
|
|
40
|
+
*
|
|
41
|
+
* Throws HttpError (HTTP_CHARSET) for a label outside the WHATWG encoding registry.
|
|
42
|
+
*
|
|
43
|
+
* @param {Uint8Array} bytes
|
|
44
|
+
* @param {string} [charset]
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export function decodeText(bytes: Uint8Array, charset?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* The exact Accept-Encoding value the request layer must send. The target runtime's
|
|
50
|
+
* DecompressionStream supports ONLY gzip / deflate / deflate-raw (verified empirically);
|
|
51
|
+
* advertising `br` or `zstd` would invite bytes we can never decode, turning every response
|
|
52
|
+
* from a brotli-preferring CDN into garbage. Keep this list and decodeBody in lockstep.
|
|
53
|
+
*/
|
|
54
|
+
export const ACCEPT_ENCODING: "gzip, deflate";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {number} status
|
|
3
|
+
* @param {string} [method] accepted for API symmetry; the status alone decides, because even a
|
|
4
|
+
* combination we will rewrite (303 + POST) is still a redirect — it just mutates the method.
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function shouldRedirect(status: number, method?: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a Location header against the current URL, enforcing the scheme allow-list.
|
|
10
|
+
* Throws HttpError for a missing/unparseable Location or a non-http(s) scheme.
|
|
11
|
+
* @param {URL} currentUrl
|
|
12
|
+
* @param {string|null|undefined} location
|
|
13
|
+
* @returns {URL}
|
|
14
|
+
*/
|
|
15
|
+
export function resolveLocation(currentUrl: URL, location: string | null | undefined): URL;
|
|
16
|
+
/**
|
|
17
|
+
* A request as the redirect engine consumes it. Bodies stay in whatever form the caller holds
|
|
18
|
+
* them; this layer only decides whether they survive the hop, never reads them.
|
|
19
|
+
* @typedef {Uint8Array | string | ReadableStream<Uint8Array> | null} RedirectBody
|
|
20
|
+
* @typedef {object} RedirectableRequest
|
|
21
|
+
* @property {string} method
|
|
22
|
+
* @property {string | URL} url
|
|
23
|
+
* @property {Headers | Record<string, string>} [headers]
|
|
24
|
+
* @property {RedirectBody} [body]
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The follow-up request. `url` and `headers` are always normalised instances; `body` is the
|
|
28
|
+
* caller's own value passed through, or null when the method rewrite dropped it.
|
|
29
|
+
* @typedef {object} NextRequest
|
|
30
|
+
* @property {string} method
|
|
31
|
+
* @property {URL} url
|
|
32
|
+
* @property {Headers} headers credential and body-describing headers already stripped per the
|
|
33
|
+
* rules above
|
|
34
|
+
* @property {RedirectBody} body
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {object} NextRequestOptions
|
|
38
|
+
* @property {number} [maxRedirects] default 20
|
|
39
|
+
* @property {string[]} [history] pass the SAME array across every hop of one logical fetch; it
|
|
40
|
+
* is both the loop detector and the hop counter
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Compute the follow-up request for a redirect response. Throws rather than returning a
|
|
44
|
+
* failure: LimitError past `maxRedirects`, HttpError for loops and bad Locations, ConfigError
|
|
45
|
+
* for a non-redirect status or an unreplayable stream body on 307/308.
|
|
46
|
+
*
|
|
47
|
+
* @param {RedirectableRequest} current
|
|
48
|
+
* @param {{ status: number, headers: Headers|Record<string,string> }} response
|
|
49
|
+
* @param {NextRequestOptions} [options]
|
|
50
|
+
* @returns {NextRequest}
|
|
51
|
+
*/
|
|
52
|
+
export function nextRequest(current: RedirectableRequest, response: {
|
|
53
|
+
status: number;
|
|
54
|
+
headers: Headers | Record<string, string>;
|
|
55
|
+
}, options?: NextRequestOptions): NextRequest;
|
|
56
|
+
export const DEFAULT_MAX_REDIRECTS: 20;
|
|
57
|
+
/**
|
|
58
|
+
* A request as the redirect engine consumes it. Bodies stay in whatever form the caller holds
|
|
59
|
+
* them; this layer only decides whether they survive the hop, never reads them.
|
|
60
|
+
*/
|
|
61
|
+
export type RedirectBody = Uint8Array | string | ReadableStream<Uint8Array> | null;
|
|
62
|
+
/**
|
|
63
|
+
* A request as the redirect engine consumes it. Bodies stay in whatever form the caller holds
|
|
64
|
+
* them; this layer only decides whether they survive the hop, never reads them.
|
|
65
|
+
*/
|
|
66
|
+
export type RedirectableRequest = {
|
|
67
|
+
method: string;
|
|
68
|
+
url: string | URL;
|
|
69
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
70
|
+
body?: RedirectBody | undefined;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The follow-up request. `url` and `headers` are always normalised instances; `body` is the
|
|
74
|
+
* caller's own value passed through, or null when the method rewrite dropped it.
|
|
75
|
+
*/
|
|
76
|
+
export type NextRequest = {
|
|
77
|
+
method: string;
|
|
78
|
+
url: URL;
|
|
79
|
+
/**
|
|
80
|
+
* credential and body-describing headers already stripped per the
|
|
81
|
+
* rules above
|
|
82
|
+
*/
|
|
83
|
+
headers: Headers;
|
|
84
|
+
body: RedirectBody;
|
|
85
|
+
};
|
|
86
|
+
export type NextRequestOptions = {
|
|
87
|
+
/**
|
|
88
|
+
* default 20
|
|
89
|
+
*/
|
|
90
|
+
maxRedirects?: number | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* pass the SAME array across every hop of one logical fetch; it
|
|
93
|
+
* is both the loop detector and the hop counter
|
|
94
|
+
*/
|
|
95
|
+
history?: string[] | undefined;
|
|
96
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A standalone fetch bound to a configuration, matching httpx's module-level helpers. Creates and
|
|
3
|
+
* closes a Client per call, so no connection is reused; use `new Client()` when reuse matters.
|
|
4
|
+
*
|
|
5
|
+
* @param {ClientOptions} [options]
|
|
6
|
+
* @returns {FetchLike}
|
|
7
|
+
*/
|
|
8
|
+
export function createFetch(options?: ClientOptions): FetchLike;
|
|
9
|
+
/**
|
|
10
|
+
* Replace `globalThis.fetch`, for libraries that only accept the global. Returns the undo.
|
|
11
|
+
* Never called automatically, and never on import.
|
|
12
|
+
*
|
|
13
|
+
* @param {ClientOptions} [options]
|
|
14
|
+
* @returns {() => void} uninstall; idempotent, and a no-op if someone else has since taken the global
|
|
15
|
+
*/
|
|
16
|
+
export function install(options?: ClientOptions): () => void;
|
|
17
|
+
/**
|
|
18
|
+
* A `fetch`-shaped function. Deliberately the platform's own signature: being assignable to
|
|
19
|
+
* `typeof fetch` is what lets an SDK accept this in place of the global without adapting.
|
|
20
|
+
* @typedef {(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>} FetchLike
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* What a Response carries about the connection that produced it, under the non-standard
|
|
24
|
+
* `tunnelfetch` property.
|
|
25
|
+
* @typedef {object} ResponseDetail
|
|
26
|
+
* @property {string} url
|
|
27
|
+
* @property {boolean} proxied
|
|
28
|
+
* @property {string | null} proxy the proxy actually used, credentials omitted
|
|
29
|
+
* @property {import('./tls/connect.js').TlsSessionInfo | null} tls null for cleartext
|
|
30
|
+
* @property {'1.0' | '1.1' | '2'} httpVersion '2' when ALPN negotiated HTTP/2
|
|
31
|
+
* @property {'none' | 'content-length' | 'chunked' | 'until-close' | 'h2'} framing 'h2' when the
|
|
32
|
+
* body was delimited by an HTTP/2 END_STREAM rather than by any HTTP/1.1 framing rule
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Limits on what a peer may make us buffer. Each is a fail-closed cap, not a hint.
|
|
36
|
+
* @typedef {object} Limits
|
|
37
|
+
* @property {number} [maxHeaderBytes] response head, default 65536
|
|
38
|
+
* @property {number} [maxProxyReplyBytes] proxy CONNECT reply head, default 32768
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {object} ClientOptions
|
|
42
|
+
* @property {import('./proxy/index.js').ConnectFn} [connect] Socket factory. Required for any
|
|
43
|
+
* request the platform's own `fetch` cannot serve — which is every proxied request, and every
|
|
44
|
+
* request asking for a trust policy `fetch` cannot express.
|
|
45
|
+
* @property {string | import('./proxy/index.js').ProxyConfig | null} [proxy] URL string or
|
|
46
|
+
* config object; `http:`, `https:`, `socks5:` and `socks5h:`.
|
|
47
|
+
* @property {import('./trust/index.js').TrustConfig} [trust] certificate policy, httpx's
|
|
48
|
+
* `verify=`. Default `{ mode: 'system' }`.
|
|
49
|
+
* @property {import('./tls/connect.js').TlsOptions} [tls] handshake knobs.
|
|
50
|
+
* @property {import('./util/deadline.js').DeadlineOptions} [timeouts] connect / handshake /
|
|
51
|
+
* headers / idle / total, in ms. The idle gap is the control; total is a backstop.
|
|
52
|
+
* @property {boolean} [cookies] enable a per-Client cookie jar.
|
|
53
|
+
* @property {import('./client/cookies.js').CookieJar} [jar] supply a jar directly, e.g. to share
|
|
54
|
+
* one across Clients or to persist it.
|
|
55
|
+
* @property {number} [maxRedirects] default 20.
|
|
56
|
+
* @property {number} [maxBodyBytes] enforced from Content-Length before a byte is read.
|
|
57
|
+
* @property {boolean} [decompress] gzip/deflate. Default true. Never `br`; the runtime cannot
|
|
58
|
+
* decompress it, so it is never advertised either.
|
|
59
|
+
* @property {boolean} [keepAlive] default true.
|
|
60
|
+
* @property {boolean} [http2] offer HTTP/2 via ALPN and speak it when the server selects it.
|
|
61
|
+
* Default true. The goal is ACCESS, not speed — some sites treat HTTP/1.1 as a bot signal — and
|
|
62
|
+
* on a CPU-billed runtime h2 costs MORE than h1 (HPACK is extra work). Set false to offer only
|
|
63
|
+
* `http/1.1`. There is no fallback-and-retry either way: the server's ALPN pick is followed.
|
|
64
|
+
* @property {boolean} [forceTunnel] never delegate to the platform's fetch, even when it could
|
|
65
|
+
* serve the request. Mainly for exercising this stack against origins that do not need it.
|
|
66
|
+
* @property {FetchLike} [nativeFetch] delegation target; defaults to `globalThis.fetch`.
|
|
67
|
+
* @property {{ maxPerKey?: number, maxTotal?: number }} [pool] connection pool sizing.
|
|
68
|
+
* @property {Limits} [limits]
|
|
69
|
+
* @property {import('./tls/connect.js').TlsDeps} [deps] injectable randomness and key generation.
|
|
70
|
+
* @property {AbortSignal} [signal] aborts every request this Client makes.
|
|
71
|
+
* @property {number} [now] epoch ms override, for certificate validity in tests.
|
|
72
|
+
*/
|
|
73
|
+
export class Client {
|
|
74
|
+
/**
|
|
75
|
+
* @param {ClientOptions} [options]
|
|
76
|
+
*/
|
|
77
|
+
constructor(options?: ClientOptions);
|
|
78
|
+
options: {
|
|
79
|
+
/**
|
|
80
|
+
* Socket factory. Required for any
|
|
81
|
+
* request the platform's own `fetch` cannot serve — which is every proxied request, and every
|
|
82
|
+
* request asking for a trust policy `fetch` cannot express.
|
|
83
|
+
*/
|
|
84
|
+
connect?: import("./proxy/index.js").ConnectFn | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* URL string or
|
|
87
|
+
* config object; `http:`, `https:`, `socks5:` and `socks5h:`.
|
|
88
|
+
*/
|
|
89
|
+
proxy?: string | import("./proxy/index.js").ProxyConfig | null | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* certificate policy, httpx's
|
|
92
|
+
* `verify=`. Default `{ mode: 'system' }`.
|
|
93
|
+
*/
|
|
94
|
+
trust?: import("./trust/index.js").TrustConfig | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* handshake knobs.
|
|
97
|
+
*/
|
|
98
|
+
tls?: import("./tls/connect.js").TlsOptions | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* connect / handshake /
|
|
101
|
+
* headers / idle / total, in ms. The idle gap is the control; total is a backstop.
|
|
102
|
+
*/
|
|
103
|
+
timeouts?: import("./util/deadline.js").DeadlineOptions | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* enable a per-Client cookie jar.
|
|
106
|
+
*/
|
|
107
|
+
cookies?: boolean | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* supply a jar directly, e.g. to share
|
|
110
|
+
* one across Clients or to persist it.
|
|
111
|
+
*/
|
|
112
|
+
jar?: CookieJar | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* default 20.
|
|
115
|
+
*/
|
|
116
|
+
maxRedirects?: number | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* enforced from Content-Length before a byte is read.
|
|
119
|
+
*/
|
|
120
|
+
maxBodyBytes?: number | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* gzip/deflate. Default true. Never `br`; the runtime cannot
|
|
123
|
+
* decompress it, so it is never advertised either.
|
|
124
|
+
*/
|
|
125
|
+
decompress?: boolean | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* default true.
|
|
128
|
+
*/
|
|
129
|
+
keepAlive?: boolean | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* offer HTTP/2 via ALPN and speak it when the server selects it.
|
|
132
|
+
* Default true. The goal is ACCESS, not speed — some sites treat HTTP/1.1 as a bot signal — and
|
|
133
|
+
* on a CPU-billed runtime h2 costs MORE than h1 (HPACK is extra work). Set false to offer only
|
|
134
|
+
* `http/1.1`. There is no fallback-and-retry either way: the server's ALPN pick is followed.
|
|
135
|
+
*/
|
|
136
|
+
http2?: boolean | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* never delegate to the platform's fetch, even when it could
|
|
139
|
+
* serve the request. Mainly for exercising this stack against origins that do not need it.
|
|
140
|
+
*/
|
|
141
|
+
forceTunnel?: boolean | undefined;
|
|
142
|
+
/**
|
|
143
|
+
* delegation target; defaults to `globalThis.fetch`.
|
|
144
|
+
*/
|
|
145
|
+
nativeFetch?: FetchLike | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* connection pool sizing.
|
|
148
|
+
*/
|
|
149
|
+
pool?: {
|
|
150
|
+
maxPerKey?: number;
|
|
151
|
+
maxTotal?: number;
|
|
152
|
+
} | undefined;
|
|
153
|
+
limits?: Limits | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* injectable randomness and key generation.
|
|
156
|
+
*/
|
|
157
|
+
deps?: import("./tls/connect.js").TlsDeps | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* aborts every request this Client makes.
|
|
160
|
+
*/
|
|
161
|
+
signal?: AbortSignal | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* epoch ms override, for certificate validity in tests.
|
|
164
|
+
*/
|
|
165
|
+
now?: number | undefined;
|
|
166
|
+
};
|
|
167
|
+
pool: ConnectionPool;
|
|
168
|
+
/** @type {Map<string, import('./http2/connection.js').Http2Connection>} */
|
|
169
|
+
_h2: Map<string, import("./http2/connection.js").Http2Connection>;
|
|
170
|
+
/** @type {Set<import('./http2/connection.js').Http2Connection>} */
|
|
171
|
+
_h2conns: Set<import("./http2/connection.js").Http2Connection>;
|
|
172
|
+
jar: CookieJar | null;
|
|
173
|
+
tickets: TicketStore;
|
|
174
|
+
_closed: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* @param {RequestInfo | URL} input
|
|
177
|
+
* @param {RequestInit} [init]
|
|
178
|
+
* @returns {Promise<Response & { tunnelfetch?: ResponseDetail }>}
|
|
179
|
+
*/
|
|
180
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response & {
|
|
181
|
+
tunnelfetch?: ResponseDetail;
|
|
182
|
+
}>;
|
|
183
|
+
/** Release every pooled socket and shared HTTP/2 connection. A Client that is finished must be
|
|
184
|
+
* closed or sockets leak for the isolate's lifetime. */
|
|
185
|
+
close(): Promise<void>;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* A `fetch`-shaped function. Deliberately the platform's own signature: being assignable to
|
|
189
|
+
* `typeof fetch` is what lets an SDK accept this in place of the global without adapting.
|
|
190
|
+
*/
|
|
191
|
+
export type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
192
|
+
/**
|
|
193
|
+
* What a Response carries about the connection that produced it, under the non-standard
|
|
194
|
+
* `tunnelfetch` property.
|
|
195
|
+
*/
|
|
196
|
+
export type ResponseDetail = {
|
|
197
|
+
url: string;
|
|
198
|
+
proxied: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* the proxy actually used, credentials omitted
|
|
201
|
+
*/
|
|
202
|
+
proxy: string | null;
|
|
203
|
+
/**
|
|
204
|
+
* null for cleartext
|
|
205
|
+
*/
|
|
206
|
+
tls: import("./tls/connect.js").TlsSessionInfo | null;
|
|
207
|
+
/**
|
|
208
|
+
* '2' when ALPN negotiated HTTP/2
|
|
209
|
+
*/
|
|
210
|
+
httpVersion: "1.0" | "1.1" | "2";
|
|
211
|
+
/**
|
|
212
|
+
* 'h2' when the
|
|
213
|
+
* body was delimited by an HTTP/2 END_STREAM rather than by any HTTP/1.1 framing rule
|
|
214
|
+
*/
|
|
215
|
+
framing: "none" | "content-length" | "chunked" | "until-close" | "h2";
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Limits on what a peer may make us buffer. Each is a fail-closed cap, not a hint.
|
|
219
|
+
*/
|
|
220
|
+
export type Limits = {
|
|
221
|
+
/**
|
|
222
|
+
* response head, default 65536
|
|
223
|
+
*/
|
|
224
|
+
maxHeaderBytes?: number | undefined;
|
|
225
|
+
/**
|
|
226
|
+
* proxy CONNECT reply head, default 32768
|
|
227
|
+
*/
|
|
228
|
+
maxProxyReplyBytes?: number | undefined;
|
|
229
|
+
};
|
|
230
|
+
export type ClientOptions = {
|
|
231
|
+
/**
|
|
232
|
+
* Socket factory. Required for any
|
|
233
|
+
* request the platform's own `fetch` cannot serve — which is every proxied request, and every
|
|
234
|
+
* request asking for a trust policy `fetch` cannot express.
|
|
235
|
+
*/
|
|
236
|
+
connect?: import("./proxy/index.js").ConnectFn | undefined;
|
|
237
|
+
/**
|
|
238
|
+
* URL string or
|
|
239
|
+
* config object; `http:`, `https:`, `socks5:` and `socks5h:`.
|
|
240
|
+
*/
|
|
241
|
+
proxy?: string | import("./proxy/index.js").ProxyConfig | null | undefined;
|
|
242
|
+
/**
|
|
243
|
+
* certificate policy, httpx's
|
|
244
|
+
* `verify=`. Default `{ mode: 'system' }`.
|
|
245
|
+
*/
|
|
246
|
+
trust?: import("./trust/index.js").TrustConfig | undefined;
|
|
247
|
+
/**
|
|
248
|
+
* handshake knobs.
|
|
249
|
+
*/
|
|
250
|
+
tls?: import("./tls/connect.js").TlsOptions | undefined;
|
|
251
|
+
/**
|
|
252
|
+
* connect / handshake /
|
|
253
|
+
* headers / idle / total, in ms. The idle gap is the control; total is a backstop.
|
|
254
|
+
*/
|
|
255
|
+
timeouts?: import("./util/deadline.js").DeadlineOptions | undefined;
|
|
256
|
+
/**
|
|
257
|
+
* enable a per-Client cookie jar.
|
|
258
|
+
*/
|
|
259
|
+
cookies?: boolean | undefined;
|
|
260
|
+
/**
|
|
261
|
+
* supply a jar directly, e.g. to share
|
|
262
|
+
* one across Clients or to persist it.
|
|
263
|
+
*/
|
|
264
|
+
jar?: CookieJar | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* default 20.
|
|
267
|
+
*/
|
|
268
|
+
maxRedirects?: number | undefined;
|
|
269
|
+
/**
|
|
270
|
+
* enforced from Content-Length before a byte is read.
|
|
271
|
+
*/
|
|
272
|
+
maxBodyBytes?: number | undefined;
|
|
273
|
+
/**
|
|
274
|
+
* gzip/deflate. Default true. Never `br`; the runtime cannot
|
|
275
|
+
* decompress it, so it is never advertised either.
|
|
276
|
+
*/
|
|
277
|
+
decompress?: boolean | undefined;
|
|
278
|
+
/**
|
|
279
|
+
* default true.
|
|
280
|
+
*/
|
|
281
|
+
keepAlive?: boolean | undefined;
|
|
282
|
+
/**
|
|
283
|
+
* offer HTTP/2 via ALPN and speak it when the server selects it.
|
|
284
|
+
* Default true. The goal is ACCESS, not speed — some sites treat HTTP/1.1 as a bot signal — and
|
|
285
|
+
* on a CPU-billed runtime h2 costs MORE than h1 (HPACK is extra work). Set false to offer only
|
|
286
|
+
* `http/1.1`. There is no fallback-and-retry either way: the server's ALPN pick is followed.
|
|
287
|
+
*/
|
|
288
|
+
http2?: boolean | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* never delegate to the platform's fetch, even when it could
|
|
291
|
+
* serve the request. Mainly for exercising this stack against origins that do not need it.
|
|
292
|
+
*/
|
|
293
|
+
forceTunnel?: boolean | undefined;
|
|
294
|
+
/**
|
|
295
|
+
* delegation target; defaults to `globalThis.fetch`.
|
|
296
|
+
*/
|
|
297
|
+
nativeFetch?: FetchLike | undefined;
|
|
298
|
+
/**
|
|
299
|
+
* connection pool sizing.
|
|
300
|
+
*/
|
|
301
|
+
pool?: {
|
|
302
|
+
maxPerKey?: number;
|
|
303
|
+
maxTotal?: number;
|
|
304
|
+
} | undefined;
|
|
305
|
+
limits?: Limits | undefined;
|
|
306
|
+
/**
|
|
307
|
+
* injectable randomness and key generation.
|
|
308
|
+
*/
|
|
309
|
+
deps?: import("./tls/connect.js").TlsDeps | undefined;
|
|
310
|
+
/**
|
|
311
|
+
* aborts every request this Client makes.
|
|
312
|
+
*/
|
|
313
|
+
signal?: AbortSignal | undefined;
|
|
314
|
+
/**
|
|
315
|
+
* epoch ms override, for certificate validity in tests.
|
|
316
|
+
*/
|
|
317
|
+
now?: number | undefined;
|
|
318
|
+
};
|
|
319
|
+
import { CookieJar } from './client/cookies.js';
|
|
320
|
+
import { ConnectionPool } from './pool.js';
|
|
321
|
+
import { TicketStore } from './tls/tickets.js';
|
|
322
|
+
import { utf8 } from './util/bytes.js';
|
|
323
|
+
export { CookieJar, ConnectionPool, utf8 };
|