@npy/fetch 0.1.3 → 0.1.4
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 +21 -0
- package/_internal/consts.cjs +4 -0
- package/_internal/consts.d.cts +3 -0
- package/_internal/consts.d.ts +3 -0
- package/_internal/consts.js +4 -0
- package/_internal/decode-stream-error.cjs +18 -0
- package/{src/_internal/decode-stream-error.ts → _internal/decode-stream-error.d.cts} +2 -7
- package/_internal/decode-stream-error.d.ts +11 -0
- package/_internal/decode-stream-error.js +18 -0
- package/_internal/error-mapping.cjs +44 -0
- package/_internal/error-mapping.d.cts +15 -0
- package/_internal/error-mapping.d.ts +15 -0
- package/_internal/error-mapping.js +41 -0
- package/_internal/guards.cjs +23 -0
- package/_internal/guards.d.cts +15 -0
- package/_internal/guards.d.ts +15 -0
- package/_internal/guards.js +15 -0
- package/_internal/net.cjs +95 -0
- package/_internal/net.d.cts +11 -0
- package/_internal/net.d.ts +11 -0
- package/_internal/net.js +92 -0
- package/_internal/promises.cjs +18 -0
- package/_internal/promises.d.cts +1 -0
- package/_internal/promises.d.ts +1 -0
- package/_internal/promises.js +18 -0
- package/_internal/streams.cjs +37 -0
- package/_internal/streams.d.cts +21 -0
- package/_internal/streams.d.ts +21 -0
- package/_internal/streams.js +36 -0
- package/_internal/symbols.cjs +4 -0
- package/_internal/symbols.d.cts +1 -0
- package/_internal/symbols.d.ts +1 -0
- package/_internal/symbols.js +4 -0
- package/_virtual/_rolldown/runtime.cjs +23 -0
- package/agent-pool.cjs +96 -0
- package/agent-pool.d.cts +2 -0
- package/agent-pool.d.ts +2 -0
- package/agent-pool.js +95 -0
- package/agent.cjs +260 -0
- package/agent.d.cts +3 -0
- package/agent.d.ts +3 -0
- package/agent.js +259 -0
- package/body.cjs +105 -0
- package/body.d.cts +12 -0
- package/body.d.ts +12 -0
- package/body.js +102 -0
- package/dialers/index.d.cts +3 -0
- package/dialers/index.d.ts +3 -0
- package/dialers/proxy.cjs +56 -0
- package/dialers/proxy.d.cts +27 -0
- package/dialers/proxy.d.ts +27 -0
- package/dialers/proxy.js +55 -0
- package/dialers/tcp.cjs +92 -0
- package/dialers/tcp.d.cts +57 -0
- package/dialers/tcp.d.ts +57 -0
- package/dialers/tcp.js +89 -0
- package/encoding.cjs +114 -0
- package/encoding.d.cts +35 -0
- package/encoding.d.ts +35 -0
- package/encoding.js +110 -0
- package/errors.cjs +275 -0
- package/errors.d.cts +110 -0
- package/errors.d.ts +110 -0
- package/errors.js +259 -0
- package/fetch.cjs +353 -0
- package/fetch.d.cts +58 -0
- package/fetch.d.ts +58 -0
- package/fetch.js +350 -0
- package/http-client.cjs +75 -0
- package/http-client.d.cts +39 -0
- package/http-client.d.ts +39 -0
- package/http-client.js +75 -0
- package/index.cjs +49 -0
- package/index.d.cts +14 -0
- package/index.d.ts +14 -0
- package/index.js +11 -0
- package/io/_utils.cjs +56 -0
- package/io/_utils.d.cts +10 -0
- package/io/_utils.d.ts +10 -0
- package/io/_utils.js +51 -0
- package/io/buf-writer.cjs +149 -0
- package/io/buf-writer.d.cts +13 -0
- package/io/buf-writer.d.ts +13 -0
- package/io/buf-writer.js +148 -0
- package/io/io.cjs +199 -0
- package/io/io.d.cts +5 -0
- package/io/io.d.ts +5 -0
- package/io/io.js +198 -0
- package/io/readers.cjs +337 -0
- package/io/readers.d.cts +69 -0
- package/io/readers.d.ts +69 -0
- package/io/readers.js +333 -0
- package/io/writers.cjs +196 -0
- package/io/writers.d.cts +22 -0
- package/io/writers.d.ts +22 -0
- package/io/writers.js +195 -0
- package/package.json +30 -25
- package/{src/types/agent.ts → types/agent.d.cts} +21 -47
- package/types/agent.d.ts +72 -0
- package/{src/types/dialer.ts → types/dialer.d.cts} +9 -19
- package/types/dialer.d.ts +30 -0
- package/types/index.d.cts +2 -0
- package/types/index.d.ts +2 -0
- package/bun.lock +0 -68
- package/examples/custom-proxy-client.ts +0 -32
- package/examples/http-client.ts +0 -47
- package/examples/proxy.ts +0 -16
- package/examples/simple.ts +0 -15
- package/src/_internal/consts.ts +0 -3
- package/src/_internal/error-mapping.ts +0 -160
- package/src/_internal/guards.ts +0 -78
- package/src/_internal/net.ts +0 -173
- package/src/_internal/promises.ts +0 -22
- package/src/_internal/streams.ts +0 -52
- package/src/_internal/symbols.ts +0 -1
- package/src/agent-pool.ts +0 -157
- package/src/agent.ts +0 -408
- package/src/body.ts +0 -179
- package/src/dialers/index.ts +0 -3
- package/src/dialers/proxy.ts +0 -102
- package/src/dialers/tcp.ts +0 -162
- package/src/encoding.ts +0 -222
- package/src/errors.ts +0 -357
- package/src/fetch.ts +0 -626
- package/src/http-client.ts +0 -111
- package/src/index.ts +0 -14
- package/src/io/_utils.ts +0 -82
- package/src/io/buf-writer.ts +0 -183
- package/src/io/io.ts +0 -322
- package/src/io/readers.ts +0 -576
- package/src/io/writers.ts +0 -331
- package/src/types/index.ts +0 -2
- package/tests/agent-pool.test.ts +0 -111
- package/tests/agent.test.ts +0 -134
- package/tests/body.test.ts +0 -228
- package/tests/errors.test.ts +0 -152
- package/tests/fetch.test.ts +0 -421
- package/tests/io-options.test.ts +0 -127
- package/tests/multipart.test.ts +0 -348
- package/tests/test-utils.ts +0 -335
- package/tsconfig.json +0 -15
package/src/agent-pool.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { createPool } from "generic-pool";
|
|
2
|
-
import { createAgent } from "./agent";
|
|
3
|
-
import { AutoDialer } from "./dialers";
|
|
4
|
-
import { UnsupportedProtocolError } from "./errors";
|
|
5
|
-
import type { Agent, AgentPool } from "./types/agent";
|
|
6
|
-
|
|
7
|
-
const defaultEvictionInterval = 10_000;
|
|
8
|
-
const defaultMax = Number.MAX_SAFE_INTEGER;
|
|
9
|
-
const defaultIdleTimeout = 30_000;
|
|
10
|
-
|
|
11
|
-
export function createAgentPool(
|
|
12
|
-
baseUrl: string,
|
|
13
|
-
options: AgentPool.Options = {},
|
|
14
|
-
): AgentPool {
|
|
15
|
-
const poolUrl = new URL(baseUrl);
|
|
16
|
-
|
|
17
|
-
const evictionRunIntervalMillis =
|
|
18
|
-
options.poolIdleTimeout !== false
|
|
19
|
-
? Math.min(
|
|
20
|
-
options.poolIdleTimeout || defaultEvictionInterval,
|
|
21
|
-
defaultEvictionInterval,
|
|
22
|
-
)
|
|
23
|
-
: 0;
|
|
24
|
-
const max = options.poolMaxPerHost
|
|
25
|
-
? Math.max(1, options.poolMaxPerHost)
|
|
26
|
-
: defaultMax;
|
|
27
|
-
const softIdleTimeoutMillis =
|
|
28
|
-
options.poolIdleTimeout !== false
|
|
29
|
-
? Math.max(1, options.poolIdleTimeout || defaultIdleTimeout)
|
|
30
|
-
: -1;
|
|
31
|
-
const min =
|
|
32
|
-
softIdleTimeoutMillis > 0 && options.poolMaxIdlePerHost
|
|
33
|
-
? Math.max(0, options.poolMaxIdlePerHost)
|
|
34
|
-
: 0;
|
|
35
|
-
|
|
36
|
-
if (poolUrl.protocol !== "http:" && poolUrl.protocol !== "https:") {
|
|
37
|
-
throw new UnsupportedProtocolError(poolUrl.protocol, {
|
|
38
|
-
origin: poolUrl.origin,
|
|
39
|
-
scheme: poolUrl.protocol,
|
|
40
|
-
host: poolUrl.hostname,
|
|
41
|
-
port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : undefined,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const dialer = options.dialer ?? new AutoDialer();
|
|
46
|
-
const connectOptions = options.connect ?? {};
|
|
47
|
-
const ioOptions = options.io;
|
|
48
|
-
|
|
49
|
-
const pool = createPool<Agent>(
|
|
50
|
-
{
|
|
51
|
-
async create() {
|
|
52
|
-
return createAgent(dialer, baseUrl, {
|
|
53
|
-
connect: connectOptions,
|
|
54
|
-
io: ioOptions,
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
async destroy(agent) {
|
|
58
|
-
agent.close();
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
autostart: false,
|
|
63
|
-
evictionRunIntervalMillis,
|
|
64
|
-
softIdleTimeoutMillis,
|
|
65
|
-
max,
|
|
66
|
-
min,
|
|
67
|
-
},
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
let releaseAgentFns: Array<(forceClose?: boolean) => Promise<void>> = [];
|
|
71
|
-
let closePromise: Promise<void> | undefined;
|
|
72
|
-
|
|
73
|
-
async function send(sendOptions: Agent.SendOptions): Promise<Response> {
|
|
74
|
-
let agent: Agent | undefined;
|
|
75
|
-
let agentReleased = false;
|
|
76
|
-
|
|
77
|
-
const releaseAgentFn = async (forceClose = false) => {
|
|
78
|
-
if (!agent || agentReleased) return;
|
|
79
|
-
agentReleased = true;
|
|
80
|
-
releaseAgentFns = releaseAgentFns.filter(
|
|
81
|
-
(release) => release !== releaseAgentFn,
|
|
82
|
-
);
|
|
83
|
-
if (forceClose) agent.close();
|
|
84
|
-
if (pool.isBorrowedResource(agent)) await pool.release(agent);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
releaseAgentFns.push(releaseAgentFn);
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
agent = await pool.acquire();
|
|
91
|
-
const responsePromise = agent.send(sendOptions);
|
|
92
|
-
|
|
93
|
-
void agent.whenIdle().then(
|
|
94
|
-
() => releaseAgentFn(),
|
|
95
|
-
() => releaseAgentFn(true),
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
return responsePromise;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
await releaseAgentFn(true);
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function close(): Promise<void> {
|
|
106
|
-
if (closePromise) return closePromise;
|
|
107
|
-
|
|
108
|
-
const promise = (async () => {
|
|
109
|
-
const pendingReleases = releaseAgentFns;
|
|
110
|
-
releaseAgentFns = [];
|
|
111
|
-
|
|
112
|
-
const results = await Promise.allSettled([
|
|
113
|
-
...pendingReleases.map((release) => release(true)),
|
|
114
|
-
(async () => {
|
|
115
|
-
try {
|
|
116
|
-
await pool.drain();
|
|
117
|
-
} finally {
|
|
118
|
-
await pool.clear();
|
|
119
|
-
}
|
|
120
|
-
})(),
|
|
121
|
-
]);
|
|
122
|
-
|
|
123
|
-
const errors = results.flatMap((result) =>
|
|
124
|
-
result.status === "rejected" ? [result.reason] : [],
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
if (errors.length === 1) throw errors[0];
|
|
128
|
-
|
|
129
|
-
if (errors.length > 1) {
|
|
130
|
-
throw new AggregateError(
|
|
131
|
-
errors,
|
|
132
|
-
"Failed to close agent pool cleanly",
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
})();
|
|
136
|
-
|
|
137
|
-
closePromise = promise;
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
await promise;
|
|
141
|
-
} finally {
|
|
142
|
-
if (closePromise === promise) closePromise = undefined;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
[Symbol.asyncDispose]: close,
|
|
148
|
-
close,
|
|
149
|
-
hostname: poolUrl.hostname,
|
|
150
|
-
port: poolUrl.port
|
|
151
|
-
? Number.parseInt(poolUrl.port, 10)
|
|
152
|
-
: poolUrl.protocol === "https:"
|
|
153
|
-
? 443
|
|
154
|
-
: 80,
|
|
155
|
-
send,
|
|
156
|
-
};
|
|
157
|
-
}
|
package/src/agent.ts
DELETED
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import { Deferred } from "@fuman/utils";
|
|
2
|
-
import { toConnectError, toSendError } from "./_internal/error-mapping";
|
|
3
|
-
import { raceSignal } from "./_internal/promises";
|
|
4
|
-
import { bodyErrorMapperSymbol } from "./_internal/symbols";
|
|
5
|
-
import {
|
|
6
|
-
AgentBusyError,
|
|
7
|
-
AgentClosedError,
|
|
8
|
-
OriginMismatchError,
|
|
9
|
-
RequestAbortedError,
|
|
10
|
-
UnsupportedAlpnProtocolError,
|
|
11
|
-
UnsupportedMethodError,
|
|
12
|
-
UnsupportedProtocolError,
|
|
13
|
-
} from "./errors";
|
|
14
|
-
import { readResponse, writeRequest } from "./io/io";
|
|
15
|
-
import type { Agent } from "./types/agent";
|
|
16
|
-
import type { Dialer } from "./types/dialer";
|
|
17
|
-
|
|
18
|
-
const PORT_MAP = {
|
|
19
|
-
"http:": 80,
|
|
20
|
-
"https:": 443,
|
|
21
|
-
} as const;
|
|
22
|
-
|
|
23
|
-
const DEFAULT_ALPN_PROTOCOLS = ["http/1.1"] as const;
|
|
24
|
-
|
|
25
|
-
function resolvedDeferred(): Deferred<void> {
|
|
26
|
-
const deferred = new Deferred<void>();
|
|
27
|
-
deferred.resolve();
|
|
28
|
-
return deferred;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
32
|
-
return signal ? raceSignal(promise, signal) : promise;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isTlsConnection(
|
|
36
|
-
conn: Dialer.ConnectionLike,
|
|
37
|
-
): conn is Dialer.ConnectionLike & { getAlpnProtocol(): string | null } {
|
|
38
|
-
return (
|
|
39
|
-
"getAlpnProtocol" in conn && typeof conn.getAlpnProtocol === "function"
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function createAgent(
|
|
44
|
-
dialer: Dialer,
|
|
45
|
-
baseUrl: string,
|
|
46
|
-
options: Agent.Options = {},
|
|
47
|
-
): Agent {
|
|
48
|
-
const base = new URL(baseUrl);
|
|
49
|
-
|
|
50
|
-
if (base.protocol !== "http:" && base.protocol !== "https:") {
|
|
51
|
-
throw new UnsupportedProtocolError(base.protocol, {
|
|
52
|
-
origin: base.origin,
|
|
53
|
-
scheme: base.protocol,
|
|
54
|
-
host: base.hostname,
|
|
55
|
-
port: base.port ? Number.parseInt(base.port, 10) : undefined,
|
|
56
|
-
url: base.toString(),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const secure = base.protocol === "https:";
|
|
61
|
-
const hostname = base.hostname;
|
|
62
|
-
const port = base.port
|
|
63
|
-
? Number.parseInt(base.port, 10)
|
|
64
|
-
: PORT_MAP[base.protocol];
|
|
65
|
-
|
|
66
|
-
if (!Number.isFinite(port) || port <= 0 || port > 65535) {
|
|
67
|
-
throw new TypeError(`Invalid port in base URL: ${baseUrl}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const target: Dialer.Target = {
|
|
71
|
-
address: hostname,
|
|
72
|
-
port,
|
|
73
|
-
secure,
|
|
74
|
-
sni: secure ? hostname : undefined,
|
|
75
|
-
alpnProtocols: secure ? [...DEFAULT_ALPN_PROTOCOLS] : undefined,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const connectOptions = options.connect ?? {};
|
|
79
|
-
const readerOptions = options.io?.reader ?? {};
|
|
80
|
-
const writerOptions = options.io?.writer ?? {};
|
|
81
|
-
|
|
82
|
-
let conn: Dialer.ConnectionLike | undefined;
|
|
83
|
-
let connectPromise: Promise<Dialer.ConnectionLike> | undefined;
|
|
84
|
-
|
|
85
|
-
let closed = false;
|
|
86
|
-
let isBusy = false;
|
|
87
|
-
let lastUsedTime = Date.now();
|
|
88
|
-
let idleDeferred = resolvedDeferred();
|
|
89
|
-
|
|
90
|
-
function createBaseErrorContext() {
|
|
91
|
-
return {
|
|
92
|
-
origin: base.origin,
|
|
93
|
-
scheme: base.protocol,
|
|
94
|
-
host: hostname,
|
|
95
|
-
port,
|
|
96
|
-
} as const;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function createRequestErrorContext(url: URL, method?: string) {
|
|
100
|
-
return {
|
|
101
|
-
...createBaseErrorContext(),
|
|
102
|
-
url: url.toString(),
|
|
103
|
-
method,
|
|
104
|
-
} as const;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function markIdle(): void {
|
|
108
|
-
isBusy = false;
|
|
109
|
-
lastUsedTime = Date.now();
|
|
110
|
-
idleDeferred.resolve();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function disposeConn(): void {
|
|
114
|
-
const current = conn;
|
|
115
|
-
conn = undefined;
|
|
116
|
-
if (!current) return;
|
|
117
|
-
try {
|
|
118
|
-
current.close();
|
|
119
|
-
} catch {}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function forceClose(): void {
|
|
123
|
-
if (closed) return;
|
|
124
|
-
closed = true;
|
|
125
|
-
disposeConn();
|
|
126
|
-
if (!isBusy) markIdle();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function assertUsable(): void {
|
|
130
|
-
if (closed) throw new AgentClosedError(createBaseErrorContext());
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function assertSameOrigin(url: URL): void {
|
|
134
|
-
if (url.origin !== base.origin) {
|
|
135
|
-
throw new OriginMismatchError(base.origin, url.origin, {
|
|
136
|
-
...createBaseErrorContext(),
|
|
137
|
-
url: url.toString(),
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function configureConnection(nextConn: Dialer.ConnectionLike): void {
|
|
143
|
-
nextConn.setNoDelay(connectOptions.noDelay ?? true);
|
|
144
|
-
if (connectOptions.keepAlive !== null) {
|
|
145
|
-
nextConn.setKeepAlive(connectOptions.keepAlive ?? true);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function connect(
|
|
150
|
-
signal?: AbortSignal,
|
|
151
|
-
): Promise<Dialer.ConnectionLike> {
|
|
152
|
-
assertUsable();
|
|
153
|
-
if (conn) return conn;
|
|
154
|
-
if (connectPromise) return withSignal(connectPromise, signal);
|
|
155
|
-
|
|
156
|
-
let timedOut = false;
|
|
157
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
158
|
-
const abortController = new AbortController();
|
|
159
|
-
|
|
160
|
-
const onAbort = () => abortController.abort(signal?.reason);
|
|
161
|
-
const cleanup = () => {
|
|
162
|
-
if (timeoutId !== undefined) {
|
|
163
|
-
clearTimeout(timeoutId);
|
|
164
|
-
timeoutId = undefined;
|
|
165
|
-
}
|
|
166
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
if (signal) {
|
|
170
|
-
if (signal.aborted) abortController.abort(signal.reason);
|
|
171
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
connectOptions.timeout != null &&
|
|
176
|
-
Number.isFinite(connectOptions.timeout) &&
|
|
177
|
-
connectOptions.timeout > 0
|
|
178
|
-
) {
|
|
179
|
-
timeoutId = setTimeout(() => {
|
|
180
|
-
timedOut = true;
|
|
181
|
-
abortController.abort(
|
|
182
|
-
new DOMException("Connection timed out", "TimeoutError"),
|
|
183
|
-
);
|
|
184
|
-
}, connectOptions.timeout);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
connectPromise = (async () => {
|
|
188
|
-
try {
|
|
189
|
-
const nextConn = await dialer.dial(target, {
|
|
190
|
-
signal: abortController.signal,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
if (closed) {
|
|
194
|
-
try {
|
|
195
|
-
nextConn.close();
|
|
196
|
-
} catch {}
|
|
197
|
-
throw new AgentClosedError(createBaseErrorContext());
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
configureConnection(nextConn);
|
|
201
|
-
|
|
202
|
-
if (secure && isTlsConnection(nextConn)) {
|
|
203
|
-
const alpn = nextConn.getAlpnProtocol();
|
|
204
|
-
if (alpn != null && alpn !== "" && alpn !== "http/1.1") {
|
|
205
|
-
try {
|
|
206
|
-
nextConn.close();
|
|
207
|
-
} catch {}
|
|
208
|
-
throw new UnsupportedAlpnProtocolError(
|
|
209
|
-
alpn,
|
|
210
|
-
createBaseErrorContext(),
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
conn = nextConn;
|
|
216
|
-
return nextConn;
|
|
217
|
-
} catch (error) {
|
|
218
|
-
throw toConnectError(error, {
|
|
219
|
-
signal,
|
|
220
|
-
timedOut,
|
|
221
|
-
context: createBaseErrorContext(),
|
|
222
|
-
});
|
|
223
|
-
} finally {
|
|
224
|
-
cleanup();
|
|
225
|
-
connectPromise = undefined;
|
|
226
|
-
}
|
|
227
|
-
})();
|
|
228
|
-
|
|
229
|
-
return withSignal(connectPromise, signal);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function executeRequest(
|
|
233
|
-
sendOptions: Agent.SendOptions,
|
|
234
|
-
mapBodyError?: (err: unknown) => unknown,
|
|
235
|
-
): Promise<Response> {
|
|
236
|
-
assertUsable();
|
|
237
|
-
|
|
238
|
-
const url =
|
|
239
|
-
typeof sendOptions.url === "string"
|
|
240
|
-
? new URL(sendOptions.url)
|
|
241
|
-
: sendOptions.url;
|
|
242
|
-
|
|
243
|
-
const method = sendOptions.method.toUpperCase();
|
|
244
|
-
const errorContext = createRequestErrorContext(url, method);
|
|
245
|
-
|
|
246
|
-
if (sendOptions.signal?.aborted) {
|
|
247
|
-
throw new RequestAbortedError(
|
|
248
|
-
sendOptions.signal.reason,
|
|
249
|
-
errorContext,
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
if (isBusy) throw new AgentBusyError(errorContext);
|
|
253
|
-
assertSameOrigin(url);
|
|
254
|
-
if (method === "CONNECT")
|
|
255
|
-
throw new UnsupportedMethodError("CONNECT", errorContext);
|
|
256
|
-
|
|
257
|
-
isBusy = true;
|
|
258
|
-
idleDeferred = new Deferred<void>();
|
|
259
|
-
|
|
260
|
-
let finalized = false;
|
|
261
|
-
let activeConn: Dialer.ConnectionLike | undefined;
|
|
262
|
-
|
|
263
|
-
const finalize = (reusable: boolean) => {
|
|
264
|
-
if (finalized) return;
|
|
265
|
-
finalized = true;
|
|
266
|
-
if (!reusable || closed) {
|
|
267
|
-
if (conn === activeConn) disposeConn();
|
|
268
|
-
else if (activeConn) {
|
|
269
|
-
try {
|
|
270
|
-
activeConn.close();
|
|
271
|
-
} catch {}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
markIdle();
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const abortListener = () => {
|
|
278
|
-
if (activeConn) {
|
|
279
|
-
if (conn === activeConn) conn = undefined;
|
|
280
|
-
try {
|
|
281
|
-
activeConn.close();
|
|
282
|
-
} catch {}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
activeConn = await connect(sendOptions.signal);
|
|
288
|
-
|
|
289
|
-
sendOptions.signal?.addEventListener("abort", abortListener, {
|
|
290
|
-
once: true,
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
await withSignal(
|
|
295
|
-
writeRequest(
|
|
296
|
-
activeConn,
|
|
297
|
-
{
|
|
298
|
-
url,
|
|
299
|
-
method,
|
|
300
|
-
headers: sendOptions.headers,
|
|
301
|
-
body: sendOptions.body ?? null,
|
|
302
|
-
signal: sendOptions.signal,
|
|
303
|
-
},
|
|
304
|
-
writerOptions,
|
|
305
|
-
),
|
|
306
|
-
sendOptions.signal,
|
|
307
|
-
);
|
|
308
|
-
} catch (error) {
|
|
309
|
-
throw toSendError(error, {
|
|
310
|
-
signal: sendOptions.signal,
|
|
311
|
-
context: errorContext,
|
|
312
|
-
phase: "request",
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const isHeadRequest = method === "HEAD";
|
|
317
|
-
const shouldIgnoreBody = (status: number) =>
|
|
318
|
-
isHeadRequest ||
|
|
319
|
-
(status >= 100 && status < 200) ||
|
|
320
|
-
status === 204 ||
|
|
321
|
-
status === 304;
|
|
322
|
-
|
|
323
|
-
let response: Response;
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
response = await withSignal(
|
|
327
|
-
readResponse(
|
|
328
|
-
activeConn,
|
|
329
|
-
readerOptions,
|
|
330
|
-
shouldIgnoreBody,
|
|
331
|
-
(reusable) => {
|
|
332
|
-
sendOptions.signal?.removeEventListener(
|
|
333
|
-
"abort",
|
|
334
|
-
abortListener,
|
|
335
|
-
);
|
|
336
|
-
finalize(reusable);
|
|
337
|
-
},
|
|
338
|
-
mapBodyError,
|
|
339
|
-
),
|
|
340
|
-
sendOptions.signal,
|
|
341
|
-
);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
throw toSendError(error, {
|
|
344
|
-
signal: sendOptions.signal,
|
|
345
|
-
context: errorContext,
|
|
346
|
-
phase: "response",
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return response;
|
|
351
|
-
} catch (error) {
|
|
352
|
-
sendOptions.signal?.removeEventListener("abort", abortListener);
|
|
353
|
-
|
|
354
|
-
if (activeConn) {
|
|
355
|
-
if (conn === activeConn) conn = undefined;
|
|
356
|
-
try {
|
|
357
|
-
activeConn.close();
|
|
358
|
-
} catch {}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
finalize(false);
|
|
362
|
-
throw error;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async function send(sendOptions: Agent.SendOptions): Promise<Response> {
|
|
367
|
-
const url =
|
|
368
|
-
typeof sendOptions.url === "string"
|
|
369
|
-
? new URL(sendOptions.url)
|
|
370
|
-
: sendOptions.url;
|
|
371
|
-
const errorContext = createRequestErrorContext(
|
|
372
|
-
url,
|
|
373
|
-
sendOptions.method.toUpperCase(),
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
const mapBodyError =
|
|
377
|
-
(
|
|
378
|
-
sendOptions as {
|
|
379
|
-
[bodyErrorMapperSymbol]?: (err: unknown) => unknown;
|
|
380
|
-
}
|
|
381
|
-
)[bodyErrorMapperSymbol] ??
|
|
382
|
-
((error: unknown) =>
|
|
383
|
-
toSendError(error, {
|
|
384
|
-
signal: sendOptions.signal,
|
|
385
|
-
context: errorContext,
|
|
386
|
-
phase: "body",
|
|
387
|
-
}));
|
|
388
|
-
|
|
389
|
-
return executeRequest(sendOptions, mapBodyError);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return {
|
|
393
|
-
[Symbol.dispose]: forceClose,
|
|
394
|
-
close: forceClose,
|
|
395
|
-
hostname,
|
|
396
|
-
port,
|
|
397
|
-
send,
|
|
398
|
-
whenIdle(): Promise<void> {
|
|
399
|
-
return idleDeferred.promise;
|
|
400
|
-
},
|
|
401
|
-
get isIdle(): boolean {
|
|
402
|
-
return !isBusy;
|
|
403
|
-
},
|
|
404
|
-
get lastUsed(): number {
|
|
405
|
-
return lastUsedTime;
|
|
406
|
-
},
|
|
407
|
-
};
|
|
408
|
-
}
|