| "use strict"; |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| var desc = Object.getOwnPropertyDescriptor(m, k); |
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { |
| desc = { enumerable: true, get: function() { return m[k]; } }; |
| } |
| Object.defineProperty(o, k2, desc); |
| }) : (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| o[k2] = m[k]; |
| })); |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| }) : function(o, v) { |
| o["default"] = v; |
| }); |
| var __importStar = (this && this.__importStar) || function (mod) { |
| if (mod && mod.__esModule) return mod; |
| var result = {}; |
| if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); |
| __setModuleDefault(result, mod); |
| return result; |
| }; |
| var __importDefault = (this && this.__importDefault) || function (mod) { |
| return (mod && mod.__esModule) ? mod : { "default": mod }; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.HttpsProxyAgent = void 0; |
| const net = __importStar(require("net")); |
| const tls = __importStar(require("tls")); |
| const assert_1 = __importDefault(require("assert")); |
| const debug_1 = __importDefault(require("debug")); |
| const agent_base_1 = require("agent-base"); |
| const url_1 = require("url"); |
| const parse_proxy_response_1 = require("./parse-proxy-response"); |
| const debug = (0, debug_1.default)('https-proxy-agent'); |
| const setServernameFromNonIpHost = (options) => { |
| if (options.servername === undefined && |
| options.host && |
| !net.isIP(options.host)) { |
| return { |
| ...options, |
| servername: options.host, |
| }; |
| } |
| return options; |
| }; |
| /** |
| * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to |
| * the specified "HTTP(s) proxy server" in order to proxy HTTPS requests. |
| * |
| * Outgoing HTTP requests are first tunneled through the proxy server using the |
| * `CONNECT` HTTP request method to establish a connection to the proxy server, |
| * and then the proxy server connects to the destination target and issues the |
| * HTTP request from the proxy server. |
| * |
| * `https:` requests have their socket connection upgraded to TLS once |
| * the connection to the proxy server has been established. |
| */ |
| class HttpsProxyAgent extends agent_base_1.Agent { |
| constructor(proxy, opts) { |
| super(opts); |
| this.options = { path: undefined }; |
| this.proxy = typeof proxy === 'string' ? new url_1.URL(proxy) : proxy; |
| this.proxyHeaders = opts?.headers ?? {}; |
| debug('Creating new HttpsProxyAgent instance: %o', this.proxy.href); |
| // Trim off the brackets from IPv6 addresses |
| const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ''); |
| const port = this.proxy.port |
| ? parseInt(this.proxy.port, 10) |
| : this.proxy.protocol === 'https:' |
| ? 443 |
| : 80; |
| this.connectOpts = { |
| // Attempt to negotiate http/1.1 for proxy servers that support http/2 |
| ALPNProtocols: ['http/1.1'], |
| ...(opts ? omit(opts, 'headers') : null), |
| host, |
| port, |
| }; |
| } |
| /** |
| * Called when the node-core HTTP client library is creating a |
| * new HTTP request. |
| */ |
| async connect(req, opts) { |
| const { proxy } = this; |
| if (!opts.host) { |
| throw new TypeError('No "host" provided'); |
| } |
| // Create a socket connection to the proxy server. |
| let socket; |
| if (proxy.protocol === 'https:') { |
| debug('Creating `tls.Socket`: %o', this.connectOpts); |
| socket = tls.connect(setServernameFromNonIpHost(this.connectOpts)); |
| } |
| else { |
| debug('Creating `net.Socket`: %o', this.connectOpts); |
| socket = net.connect(this.connectOpts); |
| } |
| const headers = typeof this.proxyHeaders === 'function' |
| ? this.proxyHeaders() |
| : { ...this.proxyHeaders }; |
| const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; |
| let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`; |
| // Inject the `Proxy-Authorization` header if necessary. |
| if (proxy.username || proxy.password) { |
| const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; |
| headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`; |
| } |
| headers.Host = `${host}:${opts.port}`; |
| if (!headers['Proxy-Connection']) { |
| headers['Proxy-Connection'] = this.keepAlive |
| ? 'Keep-Alive' |
| : 'close'; |
| } |
| for (const name of Object.keys(headers)) { |
| payload += `${name}: ${headers[name]}\r\n`; |
| } |
| const proxyResponsePromise = (0, parse_proxy_response_1.parseProxyResponse)(socket); |
| socket.write(`${payload}\r\n`); |
| const { connect, buffered } = await proxyResponsePromise; |
| req.emit('proxyConnect', connect); |
| this.emit('proxyConnect', connect, req); |
| if (connect.statusCode === 200) { |
| req.once('socket', resume); |
| if (opts.secureEndpoint) { |
| // The proxy is connecting to a TLS server, so upgrade |
| // this socket connection to a TLS connection. |
| debug('Upgrading socket connection to TLS'); |
| return tls.connect({ |
| ...omit(setServernameFromNonIpHost(opts), 'host', 'path', 'port'), |
| socket, |
| }); |
| } |
| return socket; |
| } |
| // Some other status code that's not 200... need to re-play the HTTP |
| // header "data" events onto the socket once the HTTP machinery is |
| // attached so that the node core `http` can parse and handle the |
| // error status code. |
| // Close the original socket, and a new "fake" socket is returned |
| // instead, so that the proxy doesn't get the HTTP request |
| // written to it (which may contain `Authorization` headers or other |
| // sensitive data). |
| // |
| // See: https://hackerone.com/reports/541502 |
| socket.destroy(); |
| const fakeSocket = new net.Socket({ writable: false }); |
| fakeSocket.readable = true; |
| // Need to wait for the "socket" event to re-play the "data" events. |
| req.once('socket', (s) => { |
| debug('Replaying proxy buffer for failed request'); |
| (0, assert_1.default)(s.listenerCount('data') > 0); |
| // Replay the "buffered" Buffer onto the fake `socket`, since at |
| // this point the HTTP module machinery has been hooked up for |
| // the user. |
| s.push(buffered); |
| s.push(null); |
| }); |
| return fakeSocket; |
| } |
| } |
| HttpsProxyAgent.protocols = ['http', 'https']; |
| exports.HttpsProxyAgent = HttpsProxyAgent; |
| function resume(socket) { |
| socket.resume(); |
| } |
| function omit(obj, ...keys) { |
| const ret = {}; |
| let key; |
| for (key in obj) { |
| if (!keys.includes(key)) { |
| ret[key] = obj[key]; |
| } |
| } |
| return ret; |
| } |
| //# sourceMappingURL=index.js.map |