-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathproxy.ts
107 lines (86 loc) · 3.38 KB
/
proxy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { Options, type Agents } from 'got';
import http2, { auto } from 'http2-wrapper';
import { URL } from 'node:url';
import { HttpProxyAgent, HttpRegularProxyAgent, HttpsProxyAgent } from '../agent/h1-proxy-agent.js';
import { TransformHeadersAgent } from '../agent/transform-headers-agent.js';
const {
HttpOverHttp2,
HttpsOverHttp2,
Http2OverHttp2,
Http2OverHttps,
Http2OverHttp,
} = http2.proxies;
export async function proxyHook(options: Options): Promise<void> {
const { context: { proxyUrl } } = options;
if (proxyUrl) {
const parsedProxy = new URL(proxyUrl as string);
validateProxyProtocol(parsedProxy.protocol);
options.agent = await getAgents(parsedProxy, options.https.rejectUnauthorized!);
}
}
export class ProxyError extends Error {}
function validateProxyProtocol(protocol: string) {
const isSupported = protocol === 'http:' || protocol === 'https:';
if (!isSupported) {
throw new ProxyError(`Proxy URL protocol "${protocol}" is not supported. Please use HTTP or HTTPS.`);
}
}
async function getAgents(parsedProxyUrl: URL, rejectUnauthorized: boolean) {
// Sockets must not be reused, the proxy server may rotate upstream proxies as well.
// `http2-wrapper` Agent options
const wrapperOptions = {
proxyOptions: {
url: parsedProxyUrl,
// Based on the got https.rejectUnauthorized option
rejectUnauthorized,
},
// The sockets won't be reused, no need to keep them
maxFreeSockets: 0,
maxEmptySessions: 0,
};
// Native `http.Agent` options
const nativeOptions = {
proxy: parsedProxyUrl,
// The sockets won't be reused, no need to keep them
maxFreeSockets: 0,
};
let agent: Agents;
if (parsedProxyUrl.protocol === 'https:') {
let alpnProtocol = 'http/1.1';
try {
const result = await auto.resolveProtocol({
host: parsedProxyUrl.hostname,
port: parsedProxyUrl.port,
rejectUnauthorized,
ALPNProtocols: ['h2', 'http/1.1'],
servername: parsedProxyUrl.hostname,
});
alpnProtocol = result.alpnProtocol;
} catch {
// Some proxies don't support CONNECT protocol, use http/1.1
}
const proxyIsHttp2 = alpnProtocol === 'h2';
if (proxyIsHttp2) {
agent = {
http: new TransformHeadersAgent(new HttpOverHttp2(wrapperOptions)),
https: new TransformHeadersAgent(new HttpsOverHttp2(wrapperOptions)),
http2: new Http2OverHttp2(wrapperOptions),
};
} else {
// Upstream proxies hang up connections on CONNECT + unsecure HTTP
agent = {
http: new TransformHeadersAgent(new HttpProxyAgent(nativeOptions)),
https: new TransformHeadersAgent(new HttpsProxyAgent(nativeOptions)),
http2: new Http2OverHttps(wrapperOptions),
};
}
} else {
// Upstream proxies hang up connections on CONNECT + unsecure HTTP
agent = {
http: new TransformHeadersAgent(new HttpRegularProxyAgent(nativeOptions)),
https: new TransformHeadersAgent(new HttpsProxyAgent(nativeOptions)),
http2: new Http2OverHttp(wrapperOptions),
};
}
return agent;
}