summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/request/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/request/main.js')
-rw-r--r--deps/npm/node_modules/request/main.js947
1 files changed, 625 insertions, 322 deletions
diff --git a/deps/npm/node_modules/request/main.js b/deps/npm/node_modules/request/main.js
index a25393ec3e..5a6bd9eb04 100644
--- a/deps/npm/node_modules/request/main.js
+++ b/deps/npm/node_modules/request/main.js
@@ -1,4 +1,4 @@
-// Copyright 2010-2011 Mikeal Rogers
+// Copyright 2010-2012 Mikeal Rogers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -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) {
@@ -67,7 +68,9 @@ function isReadStream (rs) {
function copy (obj) {
var o = {}
- for (var i in obj) o[i] = obj[i]
+ Object.keys(obj).forEach(function (i) {
+ o[i] = obj[i]
+ })
return o
}
@@ -83,24 +86,31 @@ function Request (options) {
if (typeof options === 'string') {
options = {uri:options}
}
-
+
+ var reserved = Object.keys(Request.prototype)
for (var i in options) {
- this[i] = options[i]
+ if (reserved.indexOf(i) === -1) {
+ this[i] = options[i]
+ } else {
+ if (typeof options[i] === 'function') {
+ delete options[i]
+ }
+ }
}
- if (!this.pool) this.pool = globalPool
- this.dests = []
- this.__isRequestRequest = true
+ options = copy(options)
+
+ this.init(options)
}
util.inherits(Request, stream.Stream)
-Request.prototype.getAgent = function (host, port) {
- if (!this.pool[host+':'+port]) {
- this.pool[host+':'+port] = new this.httpModule.Agent({host:host, port:port})
- }
- return this.pool[host+':'+port]
-}
-Request.prototype.request = function () {
+Request.prototype.init = function (options) {
var self = this
-
+
+ if (!options) options = {}
+
+ if (!self.pool) self.pool = globalPool
+ self.dests = []
+ self.__isRequestRequest = true
+
// Protect against double callback
if (!self._callback && self.callback) {
self._callback = self.callback
@@ -124,17 +134,32 @@ Request.prototype.request = function () {
}
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:") {
+ var tunnelFn = self.proxy.protocol === "http:"
+ ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
+
+ var tunnelOptions = { proxy: { host: self.proxy.hostname
+ , port: +self.proxy.port
+ , proxyAuth: self.proxy.auth }
+ , ca: this.ca }
+
+ self.agent = tunnelFn(tunnelOptions)
+ self.tunnel = true
+ }
}
self._redirectsFollowed = self._redirectsFollowed || 0
self.maxRedirects = (self.maxRedirects !== undefined) ? self.maxRedirects : 10
self.followRedirect = (self.followRedirect !== undefined) ? self.followRedirect : true
- if (self.followRedirect)
+ self.followAllRedirects = (self.followAllRedirects !== undefined) ? self.followAllRedirects : false;
+ if (self.followRedirect || self.followAllRedirects)
self.redirects = self.redirects || []
self.headers = self.headers ? copy(self.headers) : {}
- var setHost = false
+ self.setHost = false
if (!self.headers.host) {
self.headers.host = self.uri.hostname
if (self.uri.port) {
@@ -142,27 +167,10 @@ Request.prototype.request = function () {
!(self.uri.port === 443 && self.uri.protocol === 'https:') )
self.headers.host += (':'+self.uri.port)
}
- setHost = true
- }
-
- if (self.jar === false) {
- // disable cookies
- var cookies = false;
- self._disableCookies = true;
- } else if (self.jar) {
- // fetch cookie from the user defined cookie jar
- var cookies = self.jar.get({ url: self.uri.href })
- } else {
- // fetch cookie from the global cookie jar
- var cookies = cookieJar.get({ url: self.uri.href })
- }
- if (cookies) {
- var cookieString = cookies.map(function (c) {
- return c.name + "=" + c.value;
- }).join("; ");
-
- self.headers.Cookie = cookieString;
+ self.setHost = true
}
+
+ self.jar(self._jar || options.jar)
if (!self.uri.pathname) {self.uri.pathname = '/'}
if (!self.uri.port) {
@@ -170,7 +178,7 @@ Request.prototype.request = function () {
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 {
@@ -183,74 +191,43 @@ Request.prototype.request = function () {
delete self.callback
}
- var clientErrorHandler = function (error) {
- if (setHost) delete self.headers.host
- if (self.req._reusedSocket && error.code === 'ECONNRESET') {
- self.agent = {addRequest: ForeverAgent.prototype.addRequestNoreuse.bind(self.agent)}
+ self.clientErrorHandler = function (error) {
+ if (self._aborted) return
+
+ if (self.setHost) delete self.headers.host
+ 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
}
- if (self.timeout && self.timeoutTimer) clearTimeout(self.timeoutTimer)
+ if (self.timeout && self.timeoutTimer) {
+ clearTimeout(self.timeoutTimer);
+ self.timeoutTimer = null;
+ }
self.emit('error', error)
}
if (self.onResponse) self.on('error', function (e) {self.onResponse(e)})
if (self.callback) self.on('error', function (e) {self.callback(e)})
- if (self.form) {
- self.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
- self.body = qs.stringify(self.form).toString('utf8')
- }
-
- if (self.oauth) {
- var form
- if (self.headers['content-type'] &&
- self.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
- 'application/x-www-form-urlencoded'
- ) {
- form = qs.parse(self.body)
- }
- if (self.uri.query) {
- form = qs.parse(self.uri.query)
- }
- if (!form) form = {}
- var oa = {}
- for (var i in form) oa[i] = form[i]
- for (var i in self.oauth) oa['oauth_'+i] = self.oauth[i]
- if (!oa.oauth_version) oa.oauth_version = '1.0'
- if (!oa.oauth_timestamp) oa.oauth_timestamp = Math.floor( (new Date()).getTime() / 1000 ).toString()
- if (!oa.oauth_nonce) oa.oauth_nonce = uuid().replace(/-/g, '')
-
- oa.oauth_signature_method = 'HMAC-SHA1'
-
- var consumer_secret = oa.oauth_consumer_secret
- delete oa.oauth_consumer_secret
- var token_secret = oa.oauth_token_secret
- delete oa.oauth_token_secret
-
- var baseurl = self.uri.protocol + '//' + self.uri.host + self.uri.pathname
- var signature = oauth.hmacsign(self.method, baseurl, oa, consumer_secret, token_secret)
-
- // oa.oauth_signature = signature
- for (var i in form) {
- if ( i.slice(0, 'oauth_') in self.oauth) {
- // skip
- } else {
- delete oa['oauth_'+i]
- }
- }
- self.headers.authorization =
- 'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',')
- self.headers.authorization += ',oauth_signature="'+oauth.rfc3986(signature)+'"'
+ if (options.form) {
+ self.form(options.form)
+ }
+
+ if (options.oauth) {
+ self.oauth(options.oauth)
}
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(':'))
}
+ if (options.qs) self.qs(options.qs)
+
if (self.uri.path) {
self.path = self.uri.path
} else {
@@ -259,41 +236,12 @@ Request.prototype.request = function () {
if (self.path.length === 0) self.path = '/'
- if (self.proxy) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
-
- if (self.json) {
- self.headers['content-type'] = 'application/json'
- if (typeof self.json === 'boolean') {
- if (typeof self.body === 'object') self.body = JSON.stringify(self.body)
- } else {
- self.body = JSON.stringify(self.json)
- }
-
- } else if (self.multipart) {
- self.body = []
-
- if (!self.headers['content-type']) {
- self.headers['content-type'] = 'multipart/related;boundary="frontier"';
- } else {
- self.headers['content-type'] = self.headers['content-type'].split(';')[0] + ';boundary="frontier"';
- }
-
- if (!self.multipart.forEach) throw new Error('Argument error, options.multipart.')
+ if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
- self.multipart.forEach(function (part) {
- var body = part.body
- if(!body) throw Error('Body attribute missing in multipart.')
- delete part.body
- var preamble = '--frontier\r\n'
- Object.keys(part).forEach(function(key){
- preamble += key + ': ' + part[key] + '\r\n'
- })
- preamble += '\r\n'
- self.body.push(new Buffer(preamble))
- self.body.push(new Buffer(body))
- self.body.push(new Buffer('\r\n'))
- })
- self.body.push(new Buffer('--frontier--'))
+ if (options.json) {
+ self.json(options.json)
+ } else if (options.multipart) {
+ self.multipart(options.multipart)
}
if (self.body) {
@@ -317,7 +265,7 @@ Request.prototype.request = function () {
}
}
- 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 || {}
;
@@ -325,177 +273,34 @@ Request.prototype.request = function () {
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
}
}
- self.start = function () {
- 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) {
- self.response = response
- response.request = self
-
- if (self.httpModule === https &&
- self.strictSSL &&
- !response.client.authorized) {
- var sslErr = response.client.authorizationError
- self.emit('error', new Error('SSL Error: '+ sslErr))
- return
- }
-
- if (setHost) delete self.headers.host
- if (self.timeout && self.timeoutTimer) clearTimeout(self.timeoutTimer)
-
- if (response.headers['set-cookie'] && (!self._disableCookies)) {
- response.headers['set-cookie'].forEach(function(cookie) {
- if (self.jar) self.jar.add(new Cookie(cookie))
- else cookieJar.add(new Cookie(cookie))
- })
- }
-
- if (response.statusCode >= 300 &&
- response.statusCode < 400 &&
- self.followRedirect &&
- self.method !== 'PUT' &&
- self.method !== 'POST' &&
- response.headers.location) {
- if (self._redirectsFollowed >= self.maxRedirects) {
- self.emit('error', new Error("Exceeded maxRedirects. Probably stuck in a redirect loop."))
- return
- }
- self._redirectsFollowed += 1
-
- if (!isUrl.test(response.headers.location)) {
- response.headers.location = url.resolve(self.uri.href, response.headers.location)
- }
- self.uri = response.headers.location
- self.redirects.push(
- { statusCode : response.statusCode
- , redirectUri: response.headers.location
- }
- )
- delete self.req
- delete self.agent
- delete self._started
- if (self.headers) {
- delete self.headers.host
- }
- if (log) log('Redirect to %uri', self)
- request(self, self.callback)
- return // Ignore the rest of the response
- } else {
- self._redirectsFollowed = self._redirectsFollowed || 0
- // Be a good stream and emit end when the response is finished.
- // Hack to emit end on close because of a core bug that never fires end
- response.on('close', function () {
- if (!self._ended) self.response.emit('end')
- })
-
- if (self.encoding) {
- if (self.dests.length !== 0) {
- console.error("Ingoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.")
- } else {
- response.setEncoding(self.encoding)
- }
- }
-
- self.pipeDest = function (dest) {
- if (dest.headers) {
- dest.headers['content-type'] = response.headers['content-type']
- if (response.headers['content-length']) {
- dest.headers['content-length'] = response.headers['content-length']
- }
- }
- if (dest.setHeader) {
- for (var i in response.headers) {
- dest.setHeader(i, response.headers[i])
- }
- dest.statusCode = response.statusCode
- }
- if (self.pipefilter) self.pipefilter(response, dest)
- }
-
- self.dests.forEach(function (dest) {
- self.pipeDest(dest)
- })
-
- response.on("data", function (chunk) {
- self._destdata = true
- self.emit("data", chunk)
- })
- response.on("end", function (chunk) {
- self._ended = true
- self.emit("end", chunk)
- })
- response.on("close", function () {self.emit("close")})
-
- self.emit('response', response)
-
- if (self.onResponse) {
- self.onResponse(null, response)
- }
- if (self.callback) {
- var buffer = []
- var bodyLen = 0
- self.on("data", function (chunk) {
- buffer.push(chunk)
- bodyLen += chunk.length
- })
- self.on("end", function () {
- if (buffer.length && Buffer.isBuffer(buffer[0])) {
- var body = new Buffer(bodyLen)
- var i = 0
- buffer.forEach(function (chunk) {
- chunk.copy(body, i, 0, chunk.length)
- i += chunk.length
- })
- if (self.encoding === null) {
- response.body = body
- } else {
- response.body = body.toString()
- }
- } else if (buffer.length) {
- response.body = buffer.join('')
- }
-
- if (self.json) {
- try {
- response.body = JSON.parse(response.body)
- } catch (e) {}
- }
-
- self.callback(null, response, response.body)
- })
- }
- }
- })
-
- if (self.timeout && !self.timeoutTimer) {
- self.timeoutTimer = setTimeout(function() {
- self.req.abort()
- var e = new Error("ETIMEDOUT")
- e.code = "ETIMEDOUT"
- self.emit("error", e)
- }, self.timeout)
- }
-
- self.req.on('error', clientErrorHandler)
- }
-
self.once('pipe', function (src) {
if (self.ntick) throw new Error("You cannot pipe to this stream after the first nextTick() after creation of the request stream.")
self.src = src
@@ -521,6 +326,8 @@ Request.prototype.request = function () {
})
process.nextTick(function () {
+ if (self._aborted) return
+
if (self.body) {
if (Array.isArray(self.body)) {
self.body.forEach(function(part) {
@@ -540,47 +347,538 @@ Request.prototype.request = function () {
self.ntick = true
})
}
-Request.prototype.pipe = function (dest) {
+
+// Must call this when following a redirect from https to http or vice versa
+// Attempts to keep everything as identical as possible, but update the
+// httpModule, Tunneling agent, and/or Forever Agent in use.
+Request.prototype._updateProtocol = function () {
+ var self = this
+ var protocol = self.uri.protocol
+
+ if (protocol === 'https:') {
+ // previously was doing http, now doing https
+ // if it's https, then we might need to tunnel now.
+ if (self.proxy) {
+ self.tunnel = true
+ var tunnelFn = self.proxy.protocol === 'http:'
+ ? tunnel.httpsOverHttp : tunnel.httpsOverHttps
+ var tunnelOptions = { proxy: { host: self.proxy.hostname
+ , post: +self.proxy.port
+ , proxyAuth: self.proxy.auth }
+ , ca: self.ca }
+ self.agent = tunnelFn(tunnelOptions)
+ return
+ }
+
+ self.httpModule = https
+ switch (self.agentClass) {
+ case ForeverAgent:
+ self.agentClass = ForeverAgent.SSL
+ break
+ case http.Agent:
+ self.agentClass = https.Agent
+ break
+ default:
+ // nothing we can do. Just hope for the best.
+ return
+ }
+
+ // if there's an agent, we need to get a new one.
+ if (self.agent) self.agent = self.getAgent()
+
+ } else {
+ if (log) log('previously https, now http')
+ // previously was doing https, now doing http
+ // stop any tunneling.
+ if (self.tunnel) self.tunnel = false
+ self.httpModule = http
+ switch (self.agentClass) {
+ case ForeverAgent.SSL:
+ self.agentClass = ForeverAgent
+ break
+ case https.Agent:
+ self.agentClass = http.Agent
+ break
+ default:
+ // nothing we can do. just hope for the best
+ return
+ }
+
+ // if there's an agent, then get a new one.
+ if (self.agent) {
+ self.agent = null
+ self.agent = self.getAgent()
+ }
+ }
+}
+
+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
+ }
+
+ // ca option is only relevant if proxy or destination are https
+ var proxy = this.proxy
+ if (typeof proxy === 'string') proxy = url.parse(proxy)
+ var caRelevant = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
+ if (options.ca && caRelevant) {
+ 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
+ }
+
+ // we're using a stored agent. Make sure it's protocol-specific
+ poolKey = this.uri.protocol + poolKey
+
+ // 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
+
+ if (self.httpModule === https &&
+ self.strictSSL &&
+ !response.client.authorized) {
+ var sslErr = response.client.authorizationError
+ self.emit('error', new Error('SSL Error: '+ sslErr))
+ return
+ }
+
+ if (self.setHost) delete self.headers.host
+ if (self.timeout && self.timeoutTimer) {
+ clearTimeout(self.timeoutTimer);
+ self.timeoutTimer = null;
+ }
+
+ if (response.headers['set-cookie'] && (!self._disableCookies)) {
+ response.headers['set-cookie'].forEach(function(cookie) {
+ if (self._jar) self._jar.add(new Cookie(cookie))
+ else cookieJar.add(new Cookie(cookie))
+ })
+ }
+
+ if (response.statusCode >= 300 && response.statusCode < 400 &&
+ (self.followAllRedirects ||
+ (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."))
+ return
+ }
+ self._redirectsFollowed += 1
+
+ if (!isUrl.test(response.headers.location)) {
+ response.headers.location = url.resolve(self.uri.href, response.headers.location)
+ }
+
+ var uriPrev = self.uri
+ self.uri = url.parse(response.headers.location)
+
+ // handle the case where we change protocol from https to http or vice versa
+ if (self.uri.protocol !== uriPrev.protocol) {
+ self._updateProtocol()
+ }
+
+ self.redirects.push(
+ { statusCode : response.statusCode
+ , redirectUri: response.headers.location
+ }
+ )
+ self.method = 'GET'; // Force all redirects to use GET
+ delete self.req
+ delete self.agent
+ delete self._started
+ delete self.body
+ if (self.headers) {
+ delete self.headers.host
+ }
+ if (log) log('Redirect to %uri', self)
+ self.init()
+ return // Ignore the rest of the response
+ } else {
+ self._redirectsFollowed = self._redirectsFollowed || 0
+ // Be a good stream and emit end when the response is finished.
+ // Hack to emit end on close because of a core bug that never fires end
+ response.on('close', function () {
+ if (!self._ended) self.response.emit('end')
+ })
+
+ if (self.encoding) {
+ if (self.dests.length !== 0) {
+ console.error("Ingoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.")
+ } else {
+ response.setEncoding(self.encoding)
+ }
+ }
+
+ self.dests.forEach(function (dest) {
+ self.pipeDest(dest)
+ })
+
+ response.on("data", function (chunk) {
+ self._destdata = true
+ self.emit("data", chunk)
+ })
+ response.on("end", function (chunk) {
+ self._ended = true
+ self.emit("end", chunk)
+ })
+ response.on("close", function () {self.emit("close")})
+
+ self.emit('response', response)
+
+ if (self.onResponse) {
+ self.onResponse(null, response)
+ }
+ if (self.callback) {
+ var buffer = []
+ var bodyLen = 0
+ self.on("data", function (chunk) {
+ buffer.push(chunk)
+ 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
+ buffer.forEach(function (chunk) {
+ chunk.copy(body, i, 0, chunk.length)
+ i += chunk.length
+ })
+ if (self.encoding === null) {
+ response.body = body
+ } else {
+ response.body = body.toString()
+ }
+ } else if (buffer.length) {
+ response.body = buffer.join('')
+ }
+
+ if (self._json) {
+ try {
+ response.body = JSON.parse(response.body)
+ } catch (e) {}
+ }
+
+ self.callback(null, response, response.body)
+ })
+ }
+ }
+ })
+
+ if (self.timeout && !self.timeoutTimer) {
+ self.timeoutTimer = setTimeout(function() {
+ self.req.abort()
+ var e = new Error("ETIMEDOUT")
+ e.code = "ETIMEDOUT"
+ self.emit("error", e)
+ }, self.timeout)
+
+ // Set additional timeout on socket - in case if remote
+ // server freeze after sending headers
+ 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
+ if (dest.headers) {
+ dest.headers['content-type'] = response.headers['content-type']
+ if (response.headers['content-length']) {
+ dest.headers['content-length'] = response.headers['content-length']
+ }
+ }
+ if (dest.setHeader) {
+ for (var i in response.headers) {
+ dest.setHeader(i, response.headers[i])
+ }
+ dest.statusCode = response.statusCode
+ }
+ if (this.pipefilter) this.pipefilter(response, dest)
+}
+
+// Composable API
+Request.prototype.setHeader = function (name, value, clobber) {
+ if (clobber === undefined) clobber = true
+ if (clobber || !this.headers.hasOwnProperty(name)) this.headers[name] = value
+ else this.headers[name] += ',' + value
+ return this
+}
+Request.prototype.setHeaders = function (headers) {
+ for (i in headers) {this.setHeader(i, headers[i])}
+ return this
+}
+Request.prototype.qs = function (q, clobber) {
+ var base
+ if (!clobber && this.uri.query) base = qs.parse(this.uri.query)
+ else base = {}
+
+ for (var i in q) {
+ base[i] = q[i]
+ }
+
+ this.uri = url.parse(this.uri.href.split('?')[0] + '?' + qs.stringify(base))
+ this.url = this.uri
+
+ return this
+}
+Request.prototype.form = function (form) {
+ this.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ this.body = qs.stringify(form).toString('utf8')
+ return this
+}
+Request.prototype.multipart = function (multipart) {
+ var self = this
+ self.body = []
+
+ if (!self.headers['content-type']) {
+ self.headers['content-type'] = 'multipart/related;boundary="frontier"';
+ } else {
+ self.headers['content-type'] = self.headers['content-type'].split(';')[0] + ';boundary="frontier"';
+ }
+
+ if (!multipart.forEach) throw new Error('Argument error, options.multipart.')
+
+ multipart.forEach(function (part) {
+ var body = part.body
+ if(!body) throw Error('Body attribute missing in multipart.')
+ delete part.body
+ var preamble = '--frontier\r\n'
+ Object.keys(part).forEach(function(key){
+ preamble += key + ': ' + part[key] + '\r\n'
+ })
+ preamble += '\r\n'
+ self.body.push(new Buffer(preamble))
+ self.body.push(new Buffer(body))
+ self.body.push(new Buffer('\r\n'))
+ })
+ self.body.push(new Buffer('--frontier--'))
+ return self
+}
+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 {
+ this.body = JSON.stringify(val)
+ }
+ return this
+}
+Request.prototype.oauth = function (_oauth) {
+ var form
+ if (this.headers['content-type'] &&
+ this.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
+ 'application/x-www-form-urlencoded'
+ ) {
+ form = qs.parse(this.body)
+ }
+ if (this.uri.query) {
+ form = qs.parse(this.uri.query)
+ }
+ if (!form) form = {}
+ var oa = {}
+ for (var i in form) oa[i] = form[i]
+ for (var i in _oauth) oa['oauth_'+i] = _oauth[i]
+ if (!oa.oauth_version) oa.oauth_version = '1.0'
+ if (!oa.oauth_timestamp) oa.oauth_timestamp = Math.floor( (new Date()).getTime() / 1000 ).toString()
+ if (!oa.oauth_nonce) oa.oauth_nonce = uuid().replace(/-/g, '')
+
+ oa.oauth_signature_method = 'HMAC-SHA1'
+
+ var consumer_secret = oa.oauth_consumer_secret
+ delete oa.oauth_consumer_secret
+ var token_secret = oa.oauth_token_secret
+ delete oa.oauth_token_secret
+
+ var baseurl = this.uri.protocol + '//' + this.uri.host + this.uri.pathname
+ var signature = oauth.hmacsign(this.method, baseurl, oa, consumer_secret, token_secret)
+
+ // oa.oauth_signature = signature
+ for (var i in form) {
+ if ( i.slice(0, 'oauth_') in _oauth) {
+ // skip
+ } else {
+ delete oa['oauth_'+i]
+ }
+ }
+ this.headers.authorization =
+ 'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',')
+ this.headers.authorization += ',oauth_signature="'+oauth.rfc3986(signature)+'"'
+ return this
+}
+Request.prototype.jar = function (jar) {
+ var cookies
+
+ if (this._redirectsFollowed === 0) {
+ this.originalCookieHeader = this.headers.cookie
+ }
+
+ if (jar === false) {
+ // disable cookies
+ cookies = false;
+ this._disableCookies = true;
+ } else if (jar) {
+ // fetch cookie from the user defined cookie jar
+ cookies = jar.get({ url: this.uri.href })
+ } else {
+ // fetch cookie from the global cookie jar
+ cookies = cookieJar.get({ url: this.uri.href })
+ }
+
+ if (cookies && cookies.length) {
+ var cookieString = cookies.map(function (c) {
+ return c.name + "=" + c.value
+ }).join("; ")
+
+ if (this.originalCookieHeader) {
+ // Don't overwrite existing Cookie header
+ this.headers.cookie = this.originalCookieHeader + '; ' + cookieString
+ } else {
+ this.headers.cookie = cookieString
+ }
+ }
+ this._jar = jar
+ return this
+}
+
+
+// Stream API
+Request.prototype.pipe = function (dest, opts) {
if (this.response) {
if (this._destdata) {
throw new Error("You cannot pipe after data has been emitted from the response.")
} else if (this._ended) {
throw new Error("You cannot pipe after the response has been ended.")
} else {
- stream.Stream.prototype.pipe.call(this, dest)
+ stream.Stream.prototype.pipe.call(this, dest, opts)
this.pipeDest(dest)
return dest
}
} else {
this.dests.push(dest)
- stream.Stream.prototype.pipe.call(this, dest)
+ stream.Stream.prototype.pipe.call(this, dest, opts)
return dest
}
}
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 () {
+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.apply(this.req, arguments)
+ 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()
}
-function request (options, callback) {
- if (typeof options === 'string') options = {uri:options}
- if (callback) options.callback = callback
+// organize params for post, put, head, del
+function initParams(uri, options, callback) {
+ if ((typeof options === 'function') && !callback) callback = options;
+ if (typeof options === 'object') {
+ options.uri = uri;
+ } else if (typeof uri === 'string') {
+ options = {uri:uri};
+ } else {
+ options = uri;
+ uri = options.uri;
+ }
+ return { uri: uri, options: options, callback: callback };
+}
+
+function request (uri, options, callback) {
+ if (typeof uri === 'undefined') throw new Error('undefined is not a valid uri or options object.')
+ if ((typeof options === 'function') && !callback) callback = options;
+ if (typeof options === 'object') {
+ options.uri = uri;
+ } else if (typeof uri === 'string') {
+ options = {uri:uri};
+ } else {
+ options = uri;
+ }
+
+ if (callback) options.callback = callback;
var r = new Request(options)
- r.request()
return r
}
@@ -588,12 +886,12 @@ module.exports = request
request.defaults = function (options) {
var def = function (method) {
- var d = function (opts, callback) {
- if (typeof opts === 'string') opts = {uri:opts}
+ var d = function (uri, opts, callback) {
+ var params = initParams(uri, opts, callback);
for (var i in options) {
- if (opts[i] === undefined) opts[i] = options[i]
+ if (params.options[i] === undefined) params.options[i] = options[i]
}
- return method(opts, callback)
+ return method(params.uri, params.options, params.callback)
}
return d
}
@@ -610,43 +908,48 @@ 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)
}
request.get = request
-request.post = function (options, callback) {
- if (typeof options === 'string') options = {uri:options}
- options.method = 'POST'
- return request(options, callback)
-}
-request.put = function (options, callback) {
- if (typeof options === 'string') options = {uri:options}
- options.method = 'PUT'
- return request(options, callback)
-}
-request.head = function (options, callback) {
- if (typeof options === 'string') options = {uri:options}
- options.method = 'HEAD'
- if (options.body || options.requestBodyStream || options.json || options.multipart) {
+request.post = function (uri, options, callback) {
+ var params = initParams(uri, options, callback);
+ params.options.method = 'POST';
+ return request(params.uri || null, params.options, params.callback)
+}
+request.put = function (uri, options, callback) {
+ var params = initParams(uri, options, callback);
+ params.options.method = 'PUT'
+ return request(params.uri || null, params.options, params.callback)
+}
+request.head = function (uri, options, callback) {
+ var params = initParams(uri, options, callback);
+ params.options.method = 'HEAD'
+ 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(options, callback)
+ return request(params.uri || null, params.options, params.callback)
}
-request.del = function (options, callback) {
- if (typeof options === 'string') options = {uri:options}
- options.method = 'DELETE'
- return request(options, callback)
+request.del = function (uri, options, callback) {
+ var params = initParams(uri, options, callback);
+ params.options.method = 'DELETE'
+ return request(params.uri || null, params.options, params.callback)
}
request.jar = function () {
return new CookieJar
}
request.cookie = function (str) {
+ if (str && str.uri) str = str.uri
if (typeof str !== 'string') throw new Error("The cookie function only accepts STRING as param")
return new Cookie(str)
}