massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-json / node_modules / https-proxy-agent / index.js
1 /**
2  * Module dependencies.
3  */
4
5 var net = require('net');
6 var tls = require('tls');
7 var url = require('url');
8 var assert = require('assert');
9 var Agent = require('agent-base');
10 var inherits = require('util').inherits;
11 var debug = require('debug')('https-proxy-agent');
12
13 /**
14  * Module exports.
15  */
16
17 module.exports = HttpsProxyAgent;
18
19 /**
20  * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to the
21  * specified "HTTP(s) proxy server" in order to proxy HTTPS requests.
22  *
23  * @api public
24  */
25
26 function HttpsProxyAgent(opts) {
27         if (!(this instanceof HttpsProxyAgent)) return new HttpsProxyAgent(opts);
28         if ('string' == typeof opts) opts = url.parse(opts);
29         if (!opts)
30                 throw new Error(
31                         'an HTTP(S) proxy server `host` and `port` must be specified!'
32                 );
33         debug('creating new HttpsProxyAgent instance: %o', opts);
34         Agent.call(this, opts);
35
36         var proxy = Object.assign({}, opts);
37
38         // if `true`, then connect to the proxy server over TLS. defaults to `false`.
39         this.secureProxy = proxy.protocol
40                 ? /^https:?$/i.test(proxy.protocol)
41                 : false;
42
43         // prefer `hostname` over `host`, and set the `port` if needed
44         proxy.host = proxy.hostname || proxy.host;
45         proxy.port = +proxy.port || (this.secureProxy ? 443 : 80);
46
47         // ALPN is supported by Node.js >= v5.
48         // attempt to negotiate http/1.1 for proxy servers that support http/2
49         if (this.secureProxy && !('ALPNProtocols' in proxy)) {
50                 proxy.ALPNProtocols = ['http 1.1'];
51         }
52
53         if (proxy.host && proxy.path) {
54                 // if both a `host` and `path` are specified then it's most likely the
55                 // result of a `url.parse()` call... we need to remove the `path` portion so
56                 // that `net.connect()` doesn't attempt to open that as a unix socket file.
57                 delete proxy.path;
58                 delete proxy.pathname;
59         }
60
61         this.proxy = proxy;
62         this.defaultPort = 443;
63 }
64 inherits(HttpsProxyAgent, Agent);
65
66 /**
67  * Called when the node-core HTTP client library is creating a new HTTP request.
68  *
69  * @api public
70  */
71
72 HttpsProxyAgent.prototype.callback = function connect(req, opts, fn) {
73         var proxy = this.proxy;
74
75         // create a socket connection to the proxy server
76         var socket;
77         if (this.secureProxy) {
78                 socket = tls.connect(proxy);
79         } else {
80                 socket = net.connect(proxy);
81         }
82
83         // we need to buffer any HTTP traffic that happens with the proxy before we get
84         // the CONNECT response, so that if the response is anything other than an "200"
85         // response code, then we can re-play the "data" events on the socket once the
86         // HTTP parser is hooked up...
87         var buffers = [];
88         var buffersLength = 0;
89
90         function read() {
91                 var b = socket.read();
92                 if (b) ondata(b);
93                 else socket.once('readable', read);
94         }
95
96         function cleanup() {
97                 socket.removeListener('end', onend);
98                 socket.removeListener('error', onerror);
99                 socket.removeListener('close', onclose);
100                 socket.removeListener('readable', read);
101         }
102
103         function onclose(err) {
104                 debug('onclose had error %o', err);
105         }
106
107         function onend() {
108                 debug('onend');
109         }
110
111         function onerror(err) {
112                 cleanup();
113                 fn(err);
114         }
115
116         function ondata(b) {
117                 buffers.push(b);
118                 buffersLength += b.length;
119                 var buffered = Buffer.concat(buffers, buffersLength);
120                 var str = buffered.toString('ascii');
121
122                 if (!~str.indexOf('\r\n\r\n')) {
123                         // keep buffering
124                         debug('have not received end of HTTP headers yet...');
125                         read();
126                         return;
127                 }
128
129                 var firstLine = str.substring(0, str.indexOf('\r\n'));
130                 var statusCode = +firstLine.split(' ')[1];
131                 debug('got proxy server response: %o', firstLine);
132
133                 if (200 == statusCode) {
134                         // 200 Connected status code!
135                         var sock = socket;
136
137                         // nullify the buffered data since we won't be needing it
138                         buffers = buffered = null;
139
140                         if (opts.secureEndpoint) {
141                                 // since the proxy is connecting to an SSL server, we have
142                                 // to upgrade this socket connection to an SSL connection
143                                 debug(
144                                         'upgrading proxy-connected socket to TLS connection: %o',
145                                         opts.host
146                                 );
147                                 opts.socket = socket;
148                                 opts.servername = opts.servername || opts.host;
149                                 opts.host = null;
150                                 opts.hostname = null;
151                                 opts.port = null;
152                                 sock = tls.connect(opts);
153                         }
154
155                         cleanup();
156                         req.once('socket', resume);
157                         fn(null, sock);
158                 } else {
159                         // some other status code that's not 200... need to re-play the HTTP header
160                         // "data" events onto the socket once the HTTP machinery is attached so
161                         // that the node core `http` can parse and handle the error status code
162                         cleanup();
163
164                         // the original socket is closed, and a new closed socket is
165                         // returned instead, so that the proxy doesn't get the HTTP request
166                         // written to it (which may contain `Authorization` headers or other
167                         // sensitive data).
168                         //
169                         // See: https://hackerone.com/reports/541502
170                         socket.destroy();
171                         socket = new net.Socket();
172                         socket.readable = true;
173
174
175                         // save a reference to the concat'd Buffer for the `onsocket` callback
176                         buffers = buffered;
177
178                         // need to wait for the "socket" event to re-play the "data" events
179                         req.once('socket', onsocket);
180
181                         fn(null, socket);
182                 }
183         }
184
185         function onsocket(socket) {
186                 debug('replaying proxy buffer for failed request');
187                 assert(socket.listenerCount('data') > 0);
188
189                 // replay the "buffers" Buffer onto the `socket`, since at this point
190                 // the HTTP module machinery has been hooked up for the user
191                 socket.push(buffers);
192
193                 // nullify the cached Buffer instance
194                 buffers = null;
195         }
196
197         socket.on('error', onerror);
198         socket.on('close', onclose);
199         socket.on('end', onend);
200
201         read();
202
203         var hostname = opts.host + ':' + opts.port;
204         var msg = 'CONNECT ' + hostname + ' HTTP/1.1\r\n';
205
206         var headers = Object.assign({}, proxy.headers);
207         if (proxy.auth) {
208                 headers['Proxy-Authorization'] =
209                         'Basic ' + Buffer.from(proxy.auth).toString('base64');
210         }
211
212         // the Host header should only include the port
213         // number when it is a non-standard port
214         var host = opts.host;
215         if (!isDefaultPort(opts.port, opts.secureEndpoint)) {
216                 host += ':' + opts.port;
217         }
218         headers['Host'] = host;
219
220         headers['Connection'] = 'close';
221         Object.keys(headers).forEach(function(name) {
222                 msg += name + ': ' + headers[name] + '\r\n';
223         });
224
225         socket.write(msg + '\r\n');
226 };
227
228 /**
229  * Resumes a socket.
230  *
231  * @param {(net.Socket|tls.Socket)} socket The socket to resume
232  * @api public
233  */
234
235 function resume(socket) {
236         socket.resume();
237 }
238
239 function isDefaultPort(port, secure) {
240         return Boolean((!secure && port === 80) || (secure && port === 443));
241 }