http-proxy.js
3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'use strict';
/**
* Minimal HTTP/S proxy client
*/
var net = require('net');
var tls = require('tls');
var urllib = require('url');
module.exports = proxyConnect;
/**
* Establishes proxied connection to destinationPort
*
* proxyConnect("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 proxyConnect(proxyUrl, destinationPort, destinationHost, callback) {
var proxy = urllib.parse(proxyUrl);
// create a socket connection to the proxy server
var options;
var connect;
var 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
var finished = false;
var tempSocketErr = function (err) {
if (finished) {
return;
}
finished = true;
try {
socket.destroy();
} catch (E) {
// ignore
}
callback(err);
};
socket = connect(options, function () {
if (finished) {
return;
}
var reqHeaders = {
Host: destinationHost + ':' + destinationPort,
Connection: 'close'
};
if (proxy.auth) {
reqHeaders['Proxy-Authorization'] = 'Basic ' + new Buffer(proxy.auth).toString('base64');
}
socket.write(
// HTTP method
'CONNECT ' + destinationHost + ':' + destinationPort + ' HTTP/1.1\r\n' +
// HTTP request headers
Object.keys(reqHeaders).map(function (key) {
return key + ': ' + reqHeaders[key];
}).join('\r\n') +
// End request
'\r\n\r\n');
var headers = '';
var onSocketData = function (chunk) {
var match;
var 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(new Buffer(remainder, 'binary'));
}
// proxy connection is now established
finished = true;
socket.removeListener('error', tempSocketErr);
// 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] || '')));
}
return callback(null, socket);
}
};
socket.on('data', onSocketData);
});
socket.once('error', tempSocketErr);
}