Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2012-03-02 03:48:29 +0400
committerisaacs <i@izs.me>2012-03-02 03:48:29 +0400
commit02094835d138b48c0733ebdb40dd0888c4a630ab (patch)
tree43986fb248405cd0a4a382a07761eed520bcd740
parentabbda96de7725f89c497e727518c1e7c4dc26eeb (diff)
Update request
-rw-r--r--node_modules/request/forever.js21
-rw-r--r--node_modules/request/main.js158
-rw-r--r--node_modules/request/package.json58
-rw-r--r--node_modules/request/tunnel.js229
4 files changed, 420 insertions, 46 deletions
diff --git a/node_modules/request/forever.js b/node_modules/request/forever.js
index e6531a21b..ac853c0d2 100644
--- a/node_modules/request/forever.js
+++ b/node_modules/request/forever.js
@@ -1,8 +1,11 @@
module.exports = ForeverAgent
+ForeverAgent.SSL = ForeverAgentSSL
var util = require('util')
, Agent = require('http').Agent
, net = require('net')
+ , tls = require('tls')
+ , AgentSSL = require('https').Agent
function ForeverAgent(options) {
var self = this
@@ -34,12 +37,14 @@ function ForeverAgent(options) {
socket.destroy();
}
})
- self.createConnection = net.createConnection
+
}
util.inherits(ForeverAgent, Agent)
ForeverAgent.defaultMinSockets = 5
+
+ForeverAgent.prototype.createConnection = net.createConnection
ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest
ForeverAgent.prototype.addRequest = function(req, host, port) {
var name = host + ':' + port
@@ -82,3 +87,17 @@ ForeverAgent.prototype.removeSocket = function(s, name, host, port) {
this.createSocket(name, host, port).emit('free');
}
}
+
+function ForeverAgentSSL (options) {
+ ForeverAgent.call(this, options)
+}
+util.inherits(ForeverAgentSSL, ForeverAgent)
+
+ForeverAgentSSL.prototype.createConnection = createConnectionSSL
+ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest
+
+function createConnectionSSL (port, host, options) {
+ options.port = port
+ options.host = host
+ return tls.connect(options)
+}
diff --git a/node_modules/request/main.js b/node_modules/request/main.js
index 5c1426416..f65120274 100644
--- a/node_modules/request/main.js
+++ b/node_modules/request/main.js
@@ -26,6 +26,7 @@ var http = require('http')
, Cookie = require('./vendor/cookie')
, CookieJar = require('./vendor/cookie/jar')
, cookieJar = new CookieJar
+ , tunnel = require('./tunnel')
;
if (process.logging) {
@@ -133,6 +134,20 @@ Request.prototype.init = function (options) {
}
if (self.proxy) {
if (typeof self.proxy == 'string') self.proxy = url.parse(self.proxy)
+
+ // do the HTTP CONNECT dance using koichik/node-tunnel
+ if (http.globalAgent && self.uri.protocol === "https:") {
+ self.tunnel = true
+ var tunnelFn = self.proxy.protocol === "http:"
+ ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
+
+ var tunnelOptions = { proxy: { host: self.proxy.hostname
+ , port: +self.proxy.port }
+ , ca: this.ca }
+
+ self.agent = tunnelFn(tunnelOptions)
+ self.tunnel = true
+ }
}
self._redirectsFollowed = self._redirectsFollowed || 0
@@ -163,7 +178,7 @@ Request.prototype.init = function (options) {
else if (self.uri.protocol == 'https:') {self.uri.port = 443}
}
- if (self.proxy) {
+ if (self.proxy && !self.tunnel) {
self.port = self.proxy.port
self.host = self.proxy.hostname
} else {
@@ -177,9 +192,12 @@ Request.prototype.init = function (options) {
}
self.clientErrorHandler = function (error) {
+ if (self._aborted) return
+
if (self.setHost) delete self.headers.host
- if (self.req._reusedSocket && error.code === 'ECONNRESET') {
- self.agent = {addRequest: ForeverAgent.prototype.addRequestNoreuse.bind(self.agent)}
+ if (self.req._reusedSocket && error.code === 'ECONNRESET'
+ && self.agent.addRequestNoreuse) {
+ self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
self.start()
self.req.end()
return
@@ -204,7 +222,7 @@ Request.prototype.init = function (options) {
if (self.uri.auth && !self.headers.authorization) {
self.headers.authorization = "Basic " + toBase64(self.uri.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
}
- if (self.proxy && self.proxy.auth && !self.headers['proxy-authorization']) {
+ if (self.proxy && self.proxy.auth && !self.headers['proxy-authorization'] && !self.tunnel) {
self.headers['proxy-authorization'] = "Basic " + toBase64(self.proxy.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
}
@@ -218,7 +236,7 @@ Request.prototype.init = function (options) {
if (self.path.length === 0) self.path = '/'
- if (self.proxy) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
+ if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
if (options.json) {
self.json(options.json)
@@ -247,7 +265,7 @@ Request.prototype.init = function (options) {
}
}
- var protocol = self.proxy ? self.proxy.protocol : self.uri.protocol
+ var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
, defaultModules = {'http:':http, 'https:':https}
, httpModules = self.httpModules || {}
;
@@ -255,17 +273,30 @@ Request.prototype.init = function (options) {
if (!self.httpModule) throw new Error("Invalid protocol")
+ if (options.ca) self.ca = options.ca
+
+ if (!self.agent) {
+ if (options.agentOptions) self.agentOptions = options.agentOptions
+
+ if (options.agentClass) {
+ self.agentClass = options.agentClass
+ } else if (options.forever) {
+ self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
+ } else {
+ self.agentClass = self.httpModule.Agent
+ }
+ }
+
if (self.pool === false) {
self.agent = false
} else {
+ self.agent = self.agent || self.getAgent()
if (self.maxSockets) {
// Don't use our pooling if node has the refactored client
- self.agent = self.agent || self.httpModule.globalAgent || self.getAgent(self.host, self.port)
self.agent.maxSockets = self.maxSockets
}
if (self.pool.maxSockets) {
// Don't use our pooling if node has the refactored client
- self.agent = self.agent || self.httpModule.globalAgent || self.getAgent(self.host, self.port)
self.agent.maxSockets = self.pool.maxSockets
}
}
@@ -295,6 +326,8 @@ Request.prototype.init = function (options) {
})
process.nextTick(function () {
+ if (self._aborted) return
+
if (self.body) {
if (Array.isArray(self.body)) {
self.body.forEach(function(part) {
@@ -314,19 +347,61 @@ Request.prototype.init = function (options) {
self.ntick = true
})
}
-Request.prototype.getAgent = function (host, port) {
- if (!this.pool[host+':'+port]) {
- this.pool[host+':'+port] = new this.httpModule.Agent({host:host, port:port})
+
+Request.prototype.getAgent = function () {
+ var Agent = this.agentClass
+ var options = {}
+ if (this.agentOptions) {
+ for (var i in this.agentOptions) {
+ options[i] = this.agentOptions[i]
+ }
+ }
+ if (this.ca) options.ca = this.ca
+
+ var poolKey = ''
+
+ // different types of agents are in different pools
+ if (Agent !== this.httpModule.Agent) {
+ poolKey += Agent.name
+ }
+
+ if (!this.httpModule.globalAgent) {
+ // node 0.4.x
+ options.host = this.host
+ options.port = this.port
+ if (poolKey) poolKey += ':'
+ poolKey += this.host + ':' + this.port
}
- return this.pool[host+':'+port]
+
+ if (options.ca) {
+ if (poolKey) poolKey += ':'
+ poolKey += options.ca
+ }
+
+ if (!poolKey && Agent === this.httpModule.Agent && this.httpModule.globalAgent) {
+ // not doing anything special. Use the globalAgent
+ return this.httpModule.globalAgent
+ }
+
+ // already generated an agent for this setting
+ if (this.pool[poolKey]) return this.pool[poolKey]
+
+ return this.pool[poolKey] = new Agent(options)
}
+
Request.prototype.start = function () {
var self = this
+
+ if (self._aborted) return
+
self._started = true
self.method = self.method || 'GET'
self.href = self.uri.href
if (log) log('%method %href', self)
self.req = self.httpModule.request(self, function (response) {
+ if (self._aborted) return
+ if (self._paused) response.pause()
+
self.response = response
response.request = self
@@ -353,7 +428,7 @@ Request.prototype.start = function () {
if (response.statusCode >= 300 && response.statusCode < 400 &&
(self.followAllRedirects ||
- (self.followRedirect && (self.method !== 'PUT' && self.method !== 'POST'))) &&
+ (self.followRedirect && (self.method !== 'PUT' && self.method !== 'POST' && self.method !== 'DELETE'))) &&
response.headers.location) {
if (self._redirectsFollowed >= self.maxRedirects) {
self.emit('error', new Error("Exceeded maxRedirects. Probably stuck in a redirect loop."))
@@ -423,6 +498,8 @@ Request.prototype.start = function () {
bodyLen += chunk.length
})
self.on("end", function () {
+ if (self._aborted) return
+
if (buffer.length && Buffer.isBuffer(buffer[0])) {
var body = new Buffer(bodyLen)
var i = 0
@@ -439,7 +516,7 @@ Request.prototype.start = function () {
response.body = buffer.join('')
}
- if (self.json) {
+ if (self._json) {
try {
response.body = JSON.parse(response.body)
} catch (e) {}
@@ -461,20 +538,36 @@ Request.prototype.start = function () {
// Set additional timeout on socket - in case if remote
// server freeze after sending headers
- self.req.setTimeout(self.timeout, function(){
- if (self.req) {
- self.req.abort()
- var e = new Error("ESOCKETTIMEDOUT")
- e.code = "ESOCKETTIMEDOUT"
- self.emit("error", e)
- }
- });
+ if (self.req.setTimeout) { // only works on node 0.6+
+ self.req.setTimeout(self.timeout, function(){
+ if (self.req) {
+ self.req.abort()
+ var e = new Error("ESOCKETTIMEDOUT")
+ e.code = "ESOCKETTIMEDOUT"
+ self.emit("error", e)
+ }
+ })
+ }
}
self.req.on('error', self.clientErrorHandler)
self.emit('request', self.req)
}
+
+Request.prototype.abort = function() {
+ this._aborted = true;
+
+ if (this.req) {
+ this.req.abort()
+ }
+ else if (this.response) {
+ this.response.abort()
+ }
+
+ this.emit("abort")
+}
+
Request.prototype.pipeDest = function (dest) {
var response = this.response
// Called after the response is received
@@ -554,6 +647,7 @@ Request.prototype.multipart = function (multipart) {
Request.prototype.json = function (val) {
this.setHeader('content-type', 'application/json')
this.setHeader('accept', 'application/json')
+ this._json = true
if (typeof val === 'boolean') {
if (typeof this.body === 'object') this.body = JSON.stringify(this.body)
} else {
@@ -659,22 +753,20 @@ Request.prototype.pipe = function (dest, opts) {
}
Request.prototype.write = function () {
if (!this._started) this.start()
- if (!this.req) throw new Error("This request has been piped before http.request() was called.")
this.req.write.apply(this.req, arguments)
}
Request.prototype.end = function (chunk) {
if (chunk) this.write(chunk)
if (!this._started) this.start()
- if (!this.req) throw new Error("This request has been piped before http.request() was called.")
this.req.end()
}
Request.prototype.pause = function () {
- if (!this.response) throw new Error("This request has been piped before http.request() was called.")
- this.response.pause.apply(this.response, arguments)
+ if (!this.response) this._paused = true
+ else this.response.pause.apply(this.response, arguments)
}
Request.prototype.resume = function () {
- if (!this.response) throw new Error("This request has been piped before http.request() was called.")
- this.response.resume.apply(this.response, arguments)
+ if (!this.response) this._paused = false
+ else this.response.resume.apply(this.response, arguments)
}
Request.prototype.destroy = function () {
if (!this._ended) this.end()
@@ -735,12 +827,13 @@ request.defaults = function (options) {
request.forever = function (agentOptions, optionsArg) {
var options = {}
- if (agentOptions) {
+ if (optionsArg) {
for (option in optionsArg) {
options[option] = optionsArg[option]
}
}
- options.agent = new ForeverAgent(agentOptions)
+ if (agentOptions) options.agentOptions = agentOptions
+ options.forever = true
return request.defaults(options)
}
@@ -758,7 +851,10 @@ request.put = function (uri, options, callback) {
request.head = function (uri, options, callback) {
var params = initParams(uri, options, callback);
params.options.method = 'HEAD'
- if (options.body || options.requestBodyStream || options.json || options.multipart) {
+ if (params.options.body ||
+ params.options.requestBodyStream ||
+ (params.options.json && typeof params.options.json !== 'boolean') ||
+ params.options.multipart) {
throw new Error("HTTP HEAD requests MUST NOT include a request body.")
}
return request(params.uri, params.options, params.callback)
diff --git a/node_modules/request/package.json b/node_modules/request/package.json
index 305cf167b..4a62c5a46 100644
--- a/node_modules/request/package.json
+++ b/node_modules/request/package.json
@@ -1,15 +1,45 @@
-{ "name" : "request"
-, "description" : "Simplified HTTP request client."
-, "tags" : ["http", "simple", "util", "utility"]
-, "version" : "2.9.151"
-, "author" : "Mikeal Rogers <mikeal.rogers@gmail.com>"
-, "repository" :
- { "type" : "git"
- , "url" : "http://github.com/mikeal/request.git"
- }
-, "bugs" :
- { "url" : "http://github.com/mikeal/request/issues" }
-, "engines" : ["node >= 0.3.6"]
-, "main" : "./main"
-, "scripts": { "test": "node tests/run.js" }
+{
+ "name": "request",
+ "description": "Simplified HTTP request client.",
+ "tags": [
+ "http",
+ "simple",
+ "util",
+ "utility"
+ ],
+ "version": "2.9.153",
+ "author": {
+ "name": "Mikeal Rogers",
+ "email": "mikeal.rogers@gmail.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mikeal/request.git"
+ },
+ "bugs": {
+ "url": "http://github.com/mikeal/request/issues"
+ },
+ "engines": [
+ "node >= 0.3.6"
+ ],
+ "main": "./main",
+ "scripts": {
+ "test": "node tests/run.js"
+ },
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "_id": "request@2.9.153",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "_engineSupported": true,
+ "_npmVersion": "1.1.2",
+ "_nodeVersion": "v0.7.6-pre",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "d1f4855a90a9e69bf6cd2a68503e253cc4a27a71"
+ },
+ "_from": "request@~2.9"
}
diff --git a/node_modules/request/tunnel.js b/node_modules/request/tunnel.js
new file mode 100644
index 000000000..453786c5e
--- /dev/null
+++ b/node_modules/request/tunnel.js
@@ -0,0 +1,229 @@
+'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) {
+ 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, ' +
+ 'sutatusCode=' + 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, {
+ 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