Permalink
Please sign in to comment.
Showing
with
6 additions
and 233 deletions.
- +4 −5 main.js
- +2 −1 package.json
- +0 −227 tunnel.js
@@ -1,227 +0,0 @@ | ||
-'use strict' | ||
- | ||
-var net = require('net') | ||
- , tls = require('tls') | ||
- , http = require('http') | ||
- , https = require('https') | ||
- , events = require('events') | ||
- , assert = require('assert') | ||
- , util = require('util') | ||
- ; | ||
- | ||
-exports.httpOverHttp = httpOverHttp | ||
-exports.httpsOverHttp = httpsOverHttp | ||
-exports.httpOverHttps = httpOverHttps | ||
-exports.httpsOverHttps = httpsOverHttps | ||
- | ||
- | ||
-function httpOverHttp(options) { | ||
- var agent = new TunnelingAgent(options) | ||
- agent.request = http.request | ||
- return agent | ||
-} | ||
- | ||
-function httpsOverHttp(options) { | ||
- var agent = new TunnelingAgent(options) | ||
- agent.request = http.request | ||
- agent.createSocket = createSecureSocket | ||
- return agent | ||
-} | ||
- | ||
-function httpOverHttps(options) { | ||
- var agent = new TunnelingAgent(options) | ||
- agent.request = https.request | ||
- return agent | ||
-} | ||
- | ||
-function httpsOverHttps(options) { | ||
- var agent = new TunnelingAgent(options) | ||
- agent.request = https.request | ||
- agent.createSocket = createSecureSocket | ||
- return agent | ||
-} | ||
- | ||
- | ||
-function TunnelingAgent(options) { | ||
- var self = this | ||
- self.options = options || {} | ||
- self.proxyOptions = self.options.proxy || {} | ||
- self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets | ||
- self.requests = [] | ||
- self.sockets = [] | ||
- | ||
- self.on('free', function onFree(socket, host, port) { | ||
- for (var i = 0, len = self.requests.length; i < len; ++i) { | ||
- var pending = self.requests[i] | ||
- if (pending.host === host && pending.port === port) { | ||
- // Detect the request to connect same origin server, | ||
- // reuse the connection. | ||
- self.requests.splice(i, 1) | ||
- pending.request.onSocket(socket) | ||
- return | ||
- } | ||
- } | ||
- socket.destroy() | ||
- self.removeSocket(socket) | ||
- }) | ||
-} | ||
-util.inherits(TunnelingAgent, events.EventEmitter) | ||
- | ||
-TunnelingAgent.prototype.addRequest = function addRequest(req, host, port) { | ||
- var self = this | ||
- | ||
- if (self.sockets.length >= this.maxSockets) { | ||
- // We are over limit so we'll add it to the queue. | ||
- self.requests.push({host: host, port: port, request: req}) | ||
- return | ||
- } | ||
- | ||
- // If we are under maxSockets create a new one. | ||
- self.createSocket({host: host, port: port, request: req}, function(socket) { | ||
- socket.on('free', onFree) | ||
- socket.on('close', onCloseOrRemove) | ||
- socket.on('agentRemove', onCloseOrRemove) | ||
- req.onSocket(socket) | ||
- | ||
- function onFree() { | ||
- self.emit('free', socket, host, port) | ||
- } | ||
- | ||
- function onCloseOrRemove(err) { | ||
- self.removeSocket() | ||
- socket.removeListener('free', onFree) | ||
- socket.removeListener('close', onCloseOrRemove) | ||
- socket.removeListener('agentRemove', onCloseOrRemove) | ||
- } | ||
- }) | ||
-} | ||
- | ||
-TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { | ||
- var self = this | ||
- var placeholder = {} | ||
- self.sockets.push(placeholder) | ||
- | ||
- var connectOptions = mergeOptions({}, self.proxyOptions, | ||
- { method: 'CONNECT' | ||
- , path: options.host + ':' + options.port | ||
- , agent: false | ||
- } | ||
- ) | ||
- if (connectOptions.proxyAuth) { | ||
- connectOptions.headers = connectOptions.headers || {} | ||
- connectOptions.headers['Proxy-Authorization'] = 'Basic ' + | ||
- new Buffer(connectOptions.proxyAuth).toString('base64') | ||
- } | ||
- | ||
- debug('making CONNECT request') | ||
- var connectReq = self.request(connectOptions) | ||
- connectReq.useChunkedEncodingByDefault = false // for v0.6 | ||
- connectReq.once('response', onResponse) // for v0.6 | ||
- connectReq.once('upgrade', onUpgrade) // for v0.6 | ||
- connectReq.once('connect', onConnect) // for v0.7 or later | ||
- connectReq.once('error', onError) | ||
- connectReq.end() | ||
- | ||
- function onResponse(res) { | ||
- // Very hacky. This is necessary to avoid http-parser leaks. | ||
- res.upgrade = true | ||
- } | ||
- | ||
- function onUpgrade(res, socket, head) { | ||
- // Hacky. | ||
- process.nextTick(function() { | ||
- onConnect(res, socket, head) | ||
- }) | ||
- } | ||
- | ||
- function onConnect(res, socket, head) { | ||
- connectReq.removeAllListeners() | ||
- socket.removeAllListeners() | ||
- | ||
- if (res.statusCode === 200) { | ||
- assert.equal(head.length, 0) | ||
- debug('tunneling connection has established') | ||
- self.sockets[self.sockets.indexOf(placeholder)] = socket | ||
- cb(socket) | ||
- } else { | ||
- debug('tunneling socket could not be established, statusCode=%d', res.statusCode) | ||
- var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode) | ||
- error.code = 'ECONNRESET' | ||
- options.request.emit('error', error) | ||
- self.removeSocket(placeholder) | ||
- } | ||
- } | ||
- | ||
- function onError(cause) { | ||
- connectReq.removeAllListeners() | ||
- | ||
- debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack) | ||
- var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message) | ||
- error.code = 'ECONNRESET' | ||
- options.request.emit('error', error) | ||
- self.removeSocket(placeholder) | ||
- } | ||
-} | ||
- | ||
-TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { | ||
- var pos = this.sockets.indexOf(socket) | ||
- if (pos === -1) return | ||
- | ||
- this.sockets.splice(pos, 1) | ||
- | ||
- var pending = this.requests.shift() | ||
- if (pending) { | ||
- // If we have pending requests and a socket gets closed a new one | ||
- // needs to be created to take over in the pool for the one that closed. | ||
- this.createSocket(pending, function(socket) { | ||
- pending.request.onSocket(socket) | ||
- }) | ||
- } | ||
-} | ||
- | ||
-function createSecureSocket(options, cb) { | ||
- var self = this | ||
- TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { | ||
- // 0 is dummy port for v0.6 | ||
- var secureSocket = tls.connect(0, mergeOptions({}, self.options, | ||
- { servername: options.host | ||
- , socket: socket | ||
- } | ||
- )) | ||
- cb(secureSocket) | ||
- }) | ||
-} | ||
- | ||
- | ||
-function mergeOptions(target) { | ||
- for (var i = 1, len = arguments.length; i < len; ++i) { | ||
- var overrides = arguments[i] | ||
- if (typeof overrides === 'object') { | ||
- var keys = Object.keys(overrides) | ||
- for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { | ||
- var k = keys[j] | ||
- if (overrides[k] !== undefined) { | ||
- target[k] = overrides[k] | ||
- } | ||
- } | ||
- } | ||
- } | ||
- return target | ||
-} | ||
- | ||
- | ||
-var debug | ||
-if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { | ||
- debug = function() { | ||
- var args = Array.prototype.slice.call(arguments) | ||
- if (typeof args[0] === 'string') { | ||
- args[0] = 'TUNNEL: ' + args[0] | ||
- } else { | ||
- args.unshift('TUNNEL:') | ||
- } | ||
- console.error.apply(console, args) | ||
- } | ||
-} else { | ||
- debug = function() {} | ||
-} | ||
-exports.debug = debug // for test |
0 comments on commit
ca1ed81