mirror of
				https://github.com/actions/setup-node.git
				synced 2025-10-26 07:16:44 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			248 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var net = require('net');
 | |
| var tls = require('tls');
 | |
| var http = require('http');
 | |
| var https = require('https');
 | |
| var events = require('events');
 | |
| var assert = require('assert');
 | |
| var 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, localAddress) {
 | |
|     var options = toOptions(host, port, localAddress);
 | |
|     for (var i = 0, len = self.requests.length; i < len; ++i) {
 | |
|       var pending = self.requests[i];
 | |
|       if (pending.host === options.host && pending.port === options.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, localAddress) {
 | |
|   var self = this;
 | |
|   var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress));
 | |
| 
 | |
|   if (self.sockets.length >= this.maxSockets) {
 | |
|     // We are over limit so we'll add it to the queue.
 | |
|     self.requests.push(options);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If we are under maxSockets create a new one.
 | |
|   self.createSocket(options, function(socket) {
 | |
|     socket.on('free', onFree);
 | |
|     socket.on('close', onCloseOrRemove);
 | |
|     socket.on('agentRemove', onCloseOrRemove);
 | |
|     req.onSocket(socket);
 | |
| 
 | |
|     function onFree() {
 | |
|       self.emit('free', socket, options);
 | |
|     }
 | |
| 
 | |
|     function onCloseOrRemove(err) {
 | |
|       self.removeSocket(socket);
 | |
|       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) {
 | |
|     var hostHeader = options.request.getHeader('host');
 | |
|     var tlsOptions = mergeOptions({}, self.options, {
 | |
|       socket: socket,
 | |
|       servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host
 | |
|     });
 | |
| 
 | |
|     // 0 is dummy port for v0.6
 | |
|     var secureSocket = tls.connect(0, tlsOptions);
 | |
|     self.sockets[self.sockets.indexOf(socket)] = secureSocket;
 | |
|     cb(secureSocket);
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| function toOptions(host, port, localAddress) {
 | |
|   if (typeof host === 'string') { // since v0.10
 | |
|     return {
 | |
|       host: host,
 | |
|       port: port,
 | |
|       localAddress: localAddress
 | |
|     };
 | |
|   }
 | |
|   return host; // for v0.11 or later
 | |
| }
 | |
| 
 | |
| 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
 |