1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
12 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
13 | };
|
14 | Object.defineProperty(exports, "__esModule", { value: true });
|
15 | const net_1 = __importDefault(require("net"));
|
16 | const tls_1 = __importDefault(require("tls"));
|
17 | const url_1 = __importDefault(require("url"));
|
18 | const assert_1 = __importDefault(require("assert"));
|
19 | const debug_1 = __importDefault(require("debug"));
|
20 | const agent_base_1 = require("agent-base");
|
21 | const parse_proxy_response_1 = __importDefault(require("./parse-proxy-response"));
|
22 | const debug = debug_1.default('https-proxy-agent:agent');
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | class HttpsProxyAgent extends agent_base_1.Agent {
|
38 | constructor(_opts) {
|
39 | let opts;
|
40 | if (typeof _opts === 'string') {
|
41 | opts = url_1.default.parse(_opts);
|
42 | }
|
43 | else {
|
44 | opts = _opts;
|
45 | }
|
46 | if (!opts) {
|
47 | throw new Error('an HTTP(S) proxy server `host` and `port` must be specified!');
|
48 | }
|
49 | debug('creating new HttpsProxyAgent instance: %o', opts);
|
50 | super(opts);
|
51 | const proxy = Object.assign({}, opts);
|
52 |
|
53 |
|
54 | this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol);
|
55 |
|
56 | proxy.host = proxy.hostname || proxy.host;
|
57 | if (typeof proxy.port === 'string') {
|
58 | proxy.port = parseInt(proxy.port, 10);
|
59 | }
|
60 | if (!proxy.port && proxy.host) {
|
61 | proxy.port = this.secureProxy ? 443 : 80;
|
62 | }
|
63 |
|
64 |
|
65 | if (this.secureProxy && !('ALPNProtocols' in proxy)) {
|
66 | proxy.ALPNProtocols = ['http 1.1'];
|
67 | }
|
68 | if (proxy.host && proxy.path) {
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | delete proxy.path;
|
74 | delete proxy.pathname;
|
75 | }
|
76 | this.proxy = proxy;
|
77 | }
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | callback(req, opts) {
|
85 | return __awaiter(this, void 0, void 0, function* () {
|
86 | const { proxy, secureProxy } = this;
|
87 |
|
88 | let socket;
|
89 | if (secureProxy) {
|
90 | debug('Creating `tls.Socket`: %o', proxy);
|
91 | socket = tls_1.default.connect(proxy);
|
92 | }
|
93 | else {
|
94 | debug('Creating `net.Socket`: %o', proxy);
|
95 | socket = net_1.default.connect(proxy);
|
96 | }
|
97 | const headers = Object.assign({}, proxy.headers);
|
98 | const hostname = `${opts.host}:${opts.port}`;
|
99 | let payload = `CONNECT ${hostname} HTTP/1.1\r\n`;
|
100 |
|
101 | if (proxy.auth) {
|
102 | headers['Proxy-Authorization'] = `Basic ${Buffer.from(proxy.auth).toString('base64')}`;
|
103 | }
|
104 |
|
105 |
|
106 | let { host, port, secureEndpoint } = opts;
|
107 | if (!isDefaultPort(port, secureEndpoint)) {
|
108 | host += `:${port}`;
|
109 | }
|
110 | headers.Host = host;
|
111 | headers.Connection = 'close';
|
112 | for (const name of Object.keys(headers)) {
|
113 | payload += `${name}: ${headers[name]}\r\n`;
|
114 | }
|
115 | const proxyResponsePromise = parse_proxy_response_1.default(socket);
|
116 | socket.write(`${payload}\r\n`);
|
117 | const { statusCode, buffered } = yield proxyResponsePromise;
|
118 | if (statusCode === 200) {
|
119 | req.once('socket', resume);
|
120 | if (opts.secureEndpoint) {
|
121 |
|
122 |
|
123 | debug('Upgrading socket connection to TLS');
|
124 | const servername = opts.servername || opts.host;
|
125 | return tls_1.default.connect(Object.assign(Object.assign({}, omit(opts, 'host', 'hostname', 'path', 'port')), { socket,
|
126 | servername }));
|
127 | }
|
128 | return socket;
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | socket.destroy();
|
141 | const fakeSocket = new net_1.default.Socket({ writable: false });
|
142 | fakeSocket.readable = true;
|
143 |
|
144 | req.once('socket', (s) => {
|
145 | debug('replaying proxy buffer for failed request');
|
146 | assert_1.default(s.listenerCount('data') > 0);
|
147 |
|
148 |
|
149 |
|
150 | s.push(buffered);
|
151 | s.push(null);
|
152 | });
|
153 | return fakeSocket;
|
154 | });
|
155 | }
|
156 | }
|
157 | exports.default = HttpsProxyAgent;
|
158 | function resume(socket) {
|
159 | socket.resume();
|
160 | }
|
161 | function isDefaultPort(port, secure) {
|
162 | return Boolean((!secure && port === 80) || (secure && port === 443));
|
163 | }
|
164 | function isHTTPS(protocol) {
|
165 | return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false;
|
166 | }
|
167 | function omit(obj, ...keys) {
|
168 | const ret = {};
|
169 | let key;
|
170 | for (key in obj) {
|
171 | if (!keys.includes(key)) {
|
172 | ret[key] = obj[key];
|
173 | }
|
174 | }
|
175 | return ret;
|
176 | }
|
177 |
|
\ | No newline at end of file |