• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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