123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- 'use strict';
- /**
- * Minimal HTTP/S proxy client
- */
- const net = require('net');
- const tls = require('tls');
- const urllib = require('url');
- /**
- * Establishes proxied connection to destinationPort
- *
- * httpProxyClient("http://localhost:3128/", 80, "google.com", function(err, socket){
- * socket.write("GET / HTTP/1.0\r\n\r\n");
- * });
- *
- * @param {String} proxyUrl proxy configuration, etg "http://proxy.host:3128/"
- * @param {Number} destinationPort Port to open in destination host
- * @param {String} destinationHost Destination hostname
- * @param {Function} callback Callback to run with the rocket object once connection is established
- */
- function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
- let proxy = urllib.parse(proxyUrl);
- // create a socket connection to the proxy server
- let options;
- let connect;
- let socket;
- options = {
- host: proxy.hostname,
- port: Number(proxy.port) ? Number(proxy.port) : proxy.protocol === 'https:' ? 443 : 80
- };
- if (proxy.protocol === 'https:') {
- // we can use untrusted proxies as long as we verify actual SMTP certificates
- options.rejectUnauthorized = false;
- connect = tls.connect.bind(tls);
- } else {
- connect = net.connect.bind(net);
- }
- // Error harness for initial connection. Once connection is established, the responsibility
- // to handle errors is passed to whoever uses this socket
- let finished = false;
- let tempSocketErr = function(err) {
- if (finished) {
- return;
- }
- finished = true;
- try {
- socket.destroy();
- } catch (E) {
- // ignore
- }
- callback(err);
- };
- socket = connect(
- options,
- () => {
- if (finished) {
- return;
- }
- let reqHeaders = {
- Host: destinationHost + ':' + destinationPort,
- Connection: 'close'
- };
- if (proxy.auth) {
- reqHeaders['Proxy-Authorization'] = 'Basic ' + Buffer.from(proxy.auth).toString('base64');
- }
- socket.write(
- // HTTP method
- 'CONNECT ' +
- destinationHost +
- ':' +
- destinationPort +
- ' HTTP/1.1\r\n' +
- // HTTP request headers
- Object.keys(reqHeaders)
- .map(key => key + ': ' + reqHeaders[key])
- .join('\r\n') +
- // End request
- '\r\n\r\n'
- );
- let headers = '';
- let onSocketData = chunk => {
- let match;
- let remainder;
- if (finished) {
- return;
- }
- headers += chunk.toString('binary');
- if ((match = headers.match(/\r\n\r\n/))) {
- socket.removeListener('data', onSocketData);
- remainder = headers.substr(match.index + match[0].length);
- headers = headers.substr(0, match.index);
- if (remainder) {
- socket.unshift(Buffer.from(remainder, 'binary'));
- }
- // proxy connection is now established
- finished = true;
- // check response code
- match = headers.match(/^HTTP\/\d+\.\d+ (\d+)/i);
- if (!match || (match[1] || '').charAt(0) !== '2') {
- try {
- socket.destroy();
- } catch (E) {
- // ignore
- }
- return callback(new Error('Invalid response from proxy' + ((match && ': ' + match[1]) || '')));
- }
- socket.removeListener('error', tempSocketErr);
- return callback(null, socket);
- }
- };
- socket.on('data', onSocketData);
- }
- );
- socket.once('error', tempSocketErr);
- }
- module.exports = httpProxyClient;
|