Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

230 linhas
6.4 KiB

  1. 'use strict';
  2. var net = require('net');
  3. var tls = require('tls');
  4. var http = require('http');
  5. var https = require('https');
  6. var events = require('events');
  7. var assert = require('assert');
  8. var util = require('util');
  9. exports.httpOverHttp = httpOverHttp;
  10. exports.httpsOverHttp = httpsOverHttp;
  11. exports.httpOverHttps = httpOverHttps;
  12. exports.httpsOverHttps = httpsOverHttps;
  13. function httpOverHttp(options) {
  14. var agent = new TunnelingAgent(options);
  15. agent.request = http.request;
  16. return agent;
  17. }
  18. function httpsOverHttp(options) {
  19. var agent = new TunnelingAgent(options);
  20. agent.request = http.request;
  21. agent.createSocket = createSecureSocket;
  22. return agent;
  23. }
  24. function httpOverHttps(options) {
  25. var agent = new TunnelingAgent(options);
  26. agent.request = https.request;
  27. return agent;
  28. }
  29. function httpsOverHttps(options) {
  30. var agent = new TunnelingAgent(options);
  31. agent.request = https.request;
  32. agent.createSocket = createSecureSocket;
  33. return agent;
  34. }
  35. function TunnelingAgent(options) {
  36. var self = this;
  37. self.options = options || {};
  38. self.proxyOptions = self.options.proxy || {};
  39. self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets;
  40. self.requests = [];
  41. self.sockets = [];
  42. self.on('free', function onFree(socket, host, port) {
  43. for (var i = 0, len = self.requests.length; i < len; ++i) {
  44. var pending = self.requests[i];
  45. if (pending.host === host && pending.port === port) {
  46. // Detect the request to connect same origin server,
  47. // reuse the connection.
  48. self.requests.splice(i, 1);
  49. pending.request.onSocket(socket);
  50. return;
  51. }
  52. }
  53. socket.destroy();
  54. self.removeSocket(socket);
  55. });
  56. }
  57. util.inherits(TunnelingAgent, events.EventEmitter);
  58. TunnelingAgent.prototype.addRequest = function addRequest(req, host, port) {
  59. var self = this;
  60. if (self.sockets.length >= this.maxSockets) {
  61. // We are over limit so we'll add it to the queue.
  62. self.requests.push({host: host, port: port, request: req});
  63. return;
  64. }
  65. // If we are under maxSockets create a new one.
  66. self.createSocket({host: host, port: port, request: req}, function(socket) {
  67. socket.on('free', onFree);
  68. socket.on('close', onCloseOrRemove);
  69. socket.on('agentRemove', onCloseOrRemove);
  70. req.onSocket(socket);
  71. function onFree() {
  72. self.emit('free', socket, host, port);
  73. }
  74. function onCloseOrRemove(err) {
  75. self.removeSocket();
  76. socket.removeListener('free', onFree);
  77. socket.removeListener('close', onCloseOrRemove);
  78. socket.removeListener('agentRemove', onCloseOrRemove);
  79. }
  80. });
  81. };
  82. TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
  83. var self = this;
  84. var placeholder = {};
  85. self.sockets.push(placeholder);
  86. var connectOptions = mergeOptions({}, self.proxyOptions, {
  87. method: 'CONNECT',
  88. path: options.host + ':' + options.port,
  89. agent: false
  90. });
  91. if (connectOptions.proxyAuth) {
  92. connectOptions.headers = connectOptions.headers || {};
  93. connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
  94. new Buffer(connectOptions.proxyAuth).toString('base64');
  95. }
  96. debug('making CONNECT request');
  97. var connectReq = self.request(connectOptions);
  98. connectReq.useChunkedEncodingByDefault = false; // for v0.6
  99. connectReq.once('response', onResponse); // for v0.6
  100. connectReq.once('upgrade', onUpgrade); // for v0.6
  101. connectReq.once('connect', onConnect); // for v0.7 or later
  102. connectReq.once('error', onError);
  103. connectReq.end();
  104. function onResponse(res) {
  105. // Very hacky. This is necessary to avoid http-parser leaks.
  106. res.upgrade = true;
  107. }
  108. function onUpgrade(res, socket, head) {
  109. // Hacky.
  110. process.nextTick(function() {
  111. onConnect(res, socket, head);
  112. });
  113. }
  114. function onConnect(res, socket, head) {
  115. connectReq.removeAllListeners();
  116. socket.removeAllListeners();
  117. if (res.statusCode === 200) {
  118. assert.equal(head.length, 0);
  119. debug('tunneling connection has established');
  120. self.sockets[self.sockets.indexOf(placeholder)] = socket;
  121. cb(socket);
  122. } else {
  123. debug('tunneling socket could not be established, statusCode=%d',
  124. res.statusCode);
  125. var error = new Error('tunneling socket could not be established, ' +
  126. 'sutatusCode=' + res.statusCode);
  127. error.code = 'ECONNRESET';
  128. options.request.emit('error', error);
  129. self.removeSocket(placeholder);
  130. }
  131. }
  132. function onError(cause) {
  133. connectReq.removeAllListeners();
  134. debug('tunneling socket could not be established, cause=%s\n',
  135. cause.message, cause.stack);
  136. var error = new Error('tunneling socket could not be established, ' +
  137. 'cause=' + cause.message);
  138. error.code = 'ECONNRESET';
  139. options.request.emit('error', error);
  140. self.removeSocket(placeholder);
  141. }
  142. };
  143. TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
  144. var pos = this.sockets.indexOf(socket)
  145. if (pos === -1) {
  146. return;
  147. }
  148. this.sockets.splice(pos, 1);
  149. var pending = this.requests.shift();
  150. if (pending) {
  151. // If we have pending requests and a socket gets closed a new one
  152. // needs to be created to take over in the pool for the one that closed.
  153. this.createSocket(pending, function(socket) {
  154. pending.request.onSocket(socket);
  155. });
  156. }
  157. };
  158. function createSecureSocket(options, cb) {
  159. var self = this;
  160. TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
  161. // 0 is dummy port for v0.6
  162. var secureSocket = tls.connect(0, mergeOptions({}, self.options, {
  163. socket: socket
  164. }));
  165. cb(secureSocket);
  166. });
  167. }
  168. function mergeOptions(target) {
  169. for (var i = 1, len = arguments.length; i < len; ++i) {
  170. var overrides = arguments[i];
  171. if (typeof overrides === 'object') {
  172. var keys = Object.keys(overrides);
  173. for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
  174. var k = keys[j];
  175. if (overrides[k] !== undefined) {
  176. target[k] = overrides[k];
  177. }
  178. }
  179. }
  180. }
  181. return target;
  182. }
  183. var debug;
  184. if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
  185. debug = function() {
  186. var args = Array.prototype.slice.call(arguments);
  187. if (typeof args[0] === 'string') {
  188. args[0] = 'TUNNEL: ' + args[0];
  189. } else {
  190. args.unshift('TUNNEL:');
  191. }
  192. console.error.apply(console, args);
  193. }
  194. } else {
  195. debug = function() {};
  196. }
  197. exports.debug = debug; // for test