1"use strict"; 2var __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}; 11var __importDefault = (this && this.__importDefault) || function (mod) { 12 return (mod && mod.__esModule) ? mod : { "default": mod }; 13}; 14Object.defineProperty(exports, "__esModule", { value: true }); 15const net_1 = __importDefault(require("net")); 16const tls_1 = __importDefault(require("tls")); 17const url_1 = __importDefault(require("url")); 18const debug_1 = __importDefault(require("debug")); 19const once_1 = __importDefault(require("@tootallnate/once")); 20const agent_base_1 = require("agent-base"); 21const debug = (0, debug_1.default)('http-proxy-agent'); 22function isHTTPS(protocol) { 23 return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false; 24} 25/** 26 * The `HttpProxyAgent` implements an HTTP Agent subclass that connects 27 * to the specified "HTTP proxy server" in order to proxy HTTP requests. 28 * 29 * @api public 30 */ 31class HttpProxyAgent extends agent_base_1.Agent { 32 constructor(_opts) { 33 let opts; 34 if (typeof _opts === 'string') { 35 opts = url_1.default.parse(_opts); 36 } 37 else { 38 opts = _opts; 39 } 40 if (!opts) { 41 throw new Error('an HTTP(S) proxy server `host` and `port` must be specified!'); 42 } 43 debug('Creating new HttpProxyAgent instance: %o', opts); 44 super(opts); 45 const proxy = Object.assign({}, opts); 46 // If `true`, then connect to the proxy server over TLS. 47 // Defaults to `false`. 48 this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol); 49 // Prefer `hostname` over `host`, and set the `port` if needed. 50 proxy.host = proxy.hostname || proxy.host; 51 if (typeof proxy.port === 'string') { 52 proxy.port = parseInt(proxy.port, 10); 53 } 54 if (!proxy.port && proxy.host) { 55 proxy.port = this.secureProxy ? 443 : 80; 56 } 57 if (proxy.host && proxy.path) { 58 // If both a `host` and `path` are specified then it's most likely 59 // the result of a `url.parse()` call... we need to remove the 60 // `path` portion so that `net.connect()` doesn't attempt to open 61 // that as a Unix socket file. 62 delete proxy.path; 63 delete proxy.pathname; 64 } 65 this.proxy = proxy; 66 } 67 /** 68 * Called when the node-core HTTP client library is creating a 69 * new HTTP request. 70 * 71 * @api protected 72 */ 73 callback(req, opts) { 74 return __awaiter(this, void 0, void 0, function* () { 75 const { proxy, secureProxy } = this; 76 const parsed = url_1.default.parse(req.path); 77 if (!parsed.protocol) { 78 parsed.protocol = 'http:'; 79 } 80 if (!parsed.hostname) { 81 parsed.hostname = opts.hostname || opts.host || null; 82 } 83 if (parsed.port == null && typeof opts.port) { 84 parsed.port = String(opts.port); 85 } 86 if (parsed.port === '80') { 87 // if port is 80, then we can remove the port so that the 88 // ":80" portion is not on the produced URL 89 parsed.port = ''; 90 } 91 // Change the `http.ClientRequest` instance's "path" field 92 // to the absolute path of the URL that will be requested. 93 req.path = url_1.default.format(parsed); 94 // Inject the `Proxy-Authorization` header if necessary. 95 if (proxy.auth) { 96 req.setHeader('Proxy-Authorization', `Basic ${Buffer.from(proxy.auth).toString('base64')}`); 97 } 98 // Create a socket connection to the proxy server. 99 let socket; 100 if (secureProxy) { 101 debug('Creating `tls.Socket`: %o', proxy); 102 socket = tls_1.default.connect(proxy); 103 } 104 else { 105 debug('Creating `net.Socket`: %o', proxy); 106 socket = net_1.default.connect(proxy); 107 } 108 // At this point, the http ClientRequest's internal `_header` field 109 // might have already been set. If this is the case then we'll need 110 // to re-generate the string since we just changed the `req.path`. 111 if (req._header) { 112 let first; 113 let endOfHeaders; 114 debug('Regenerating stored HTTP header string for request'); 115 req._header = null; 116 req._implicitHeader(); 117 if (req.output && req.output.length > 0) { 118 // Node < 12 119 debug('Patching connection write() output buffer with updated header'); 120 first = req.output[0]; 121 endOfHeaders = first.indexOf('\r\n\r\n') + 4; 122 req.output[0] = req._header + first.substring(endOfHeaders); 123 debug('Output buffer: %o', req.output); 124 } 125 else if (req.outputData && req.outputData.length > 0) { 126 // Node >= 12 127 debug('Patching connection write() output buffer with updated header'); 128 first = req.outputData[0].data; 129 endOfHeaders = first.indexOf('\r\n\r\n') + 4; 130 req.outputData[0].data = 131 req._header + first.substring(endOfHeaders); 132 debug('Output buffer: %o', req.outputData[0].data); 133 } 134 } 135 // Wait for the socket's `connect` event, so that this `callback()` 136 // function throws instead of the `http` request machinery. This is 137 // important for i.e. `PacProxyAgent` which determines a failed proxy 138 // connection via the `callback()` function throwing. 139 yield (0, once_1.default)(socket, 'connect'); 140 return socket; 141 }); 142 } 143} 144exports.default = HttpProxyAgent; 145//# sourceMappingURL=agent.js.map