Comparing version 2.40.0 to 2.41.0
212
index.js
@@ -15,111 +15,43 @@ // Copyright 2010-2012 Mikeal Rogers | ||
var cookies = require('./lib/cookies') | ||
, copy = require('./lib/copy') | ||
, Request = require('./request') | ||
, util = require('util') | ||
var extend = require('util')._extend | ||
, cookies = require('./lib/cookies') | ||
, copy = require('./lib/copy') | ||
, helpers = require('./lib/helpers') | ||
, isFunction = helpers.isFunction | ||
, constructObject = helpers.constructObject | ||
, filterForCallback = helpers.filterForCallback | ||
, constructOptionsFrom = helpers.constructOptionsFrom | ||
, paramsHaveRequestBody = helpers.paramsHaveRequestBody | ||
; | ||
// organize params for patch, post, put, head, del | ||
function initParams(uri, options, callback) { | ||
var opts; | ||
if ((typeof options === 'function') && !callback) callback = options | ||
if (options && typeof options === 'object') { | ||
opts = util._extend({}, options); | ||
opts.uri = uri | ||
} else if (typeof uri === 'string') { | ||
opts = {uri:uri} | ||
} else { | ||
opts = util._extend({}, uri); | ||
uri = opts.uri | ||
} | ||
callback = filterForCallback([options, callback]) | ||
options = constructOptionsFrom(uri, options) | ||
return { uri: uri, options: opts, callback: callback } | ||
return constructObject() | ||
.extend({callback: callback}) | ||
.extend({options: options}) | ||
.extend({uri: options.uri}) | ||
.done() | ||
} | ||
function request (uri, options, callback) { | ||
var opts; | ||
if (typeof uri === 'undefined') throw new Error('undefined is not a valid uri or options object.') | ||
if ((typeof options === 'function') && !callback) callback = options | ||
if (options && typeof options === 'object') { | ||
opts = util._extend({}, options); | ||
opts.uri = uri | ||
} else if (typeof uri === 'string') { | ||
opts = {uri:uri} | ||
} else { | ||
opts = util._extend({}, uri); | ||
} | ||
if (typeof uri === 'undefined') | ||
throw new Error('undefined is not a valid uri or options object.') | ||
if (callback) opts.callback = callback | ||
var r = new Request(opts) | ||
return r | ||
} | ||
var params = initParams(uri, options, callback) | ||
options = params.options | ||
options.callback = params.callback | ||
options.uri = params.uri | ||
module.exports = request | ||
request.Request = Request; | ||
request.debug = process.env.NODE_DEBUG && /request/.test(process.env.NODE_DEBUG) | ||
request.initParams = initParams | ||
request.defaults = function (options, requester) { | ||
var def = function (method) { | ||
var d = function (uri, opts, callback) { | ||
var params = initParams(uri, opts, callback) | ||
Object.keys(options).forEach(function (key) { | ||
if (key !== 'headers' && params.options[key] === undefined) { | ||
params.options[key] = options[key] | ||
} | ||
}) | ||
if (options.headers) { | ||
var headers = {} | ||
util._extend(headers, options.headers) | ||
util._extend(headers, params.options.headers) | ||
params.options.headers = headers | ||
} | ||
if(typeof requester === 'function') { | ||
if(method === request) { | ||
method = requester | ||
} else { | ||
params.options._requester = requester | ||
} | ||
} | ||
return method(params.options, params.callback) | ||
} | ||
return d | ||
} | ||
var de = def(request) | ||
de.get = def(request.get) | ||
de.patch = def(request.patch) | ||
de.post = def(request.post) | ||
de.put = def(request.put) | ||
de.head = def(request.head) | ||
de.del = def(request.del) | ||
de.cookie = def(request.cookie) | ||
de.jar = request.jar | ||
return de | ||
return new request.Request(options) | ||
} | ||
function requester(params) { | ||
if(typeof params.options._requester === 'function') { | ||
if(typeof params.options._requester === 'function') | ||
return params.options._requester | ||
} else { | ||
return request | ||
} | ||
return request | ||
} | ||
request.forever = function (agentOptions, optionsArg) { | ||
var options = {} | ||
if (optionsArg) { | ||
for (var option in optionsArg) { | ||
options[option] = optionsArg[option] | ||
} | ||
} | ||
if (agentOptions) options.agentOptions = agentOptions | ||
options.forever = true | ||
return request.defaults(options) | ||
} | ||
request.get = function (uri, options, callback) { | ||
@@ -130,2 +62,13 @@ var params = initParams(uri, options, callback) | ||
} | ||
request.head = function (uri, options, callback) { | ||
var params = initParams(uri, options, callback) | ||
params.options.method = 'HEAD' | ||
if (paramsHaveRequestBody(params)) | ||
throw new Error("HTTP HEAD requests MUST NOT include a request body.") | ||
return requester(params)(params.uri || null, params.options, params.callback) | ||
} | ||
request.post = function (uri, options, callback) { | ||
@@ -136,2 +79,3 @@ var params = initParams(uri, options, callback) | ||
} | ||
request.put = function (uri, options, callback) { | ||
@@ -142,2 +86,3 @@ var params = initParams(uri, options, callback) | ||
} | ||
request.patch = function (uri, options, callback) { | ||
@@ -148,14 +93,3 @@ var params = initParams(uri, options, 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 requester(params)(params.uri || null, params.options, params.callback) | ||
} | ||
request.del = function (uri, options, callback) { | ||
@@ -166,7 +100,73 @@ var params = initParams(uri, options, callback) | ||
} | ||
request.jar = function () { | ||
return cookies.jar(); | ||
return cookies.jar() | ||
} | ||
request.cookie = function (str) { | ||
return cookies.parse(str); | ||
return cookies.parse(str) | ||
} | ||
request.defaults = function (options, requester) { | ||
var wrap = function (method) { | ||
var headerlessOptions = function (options) { | ||
options = extend({}, options) | ||
delete options.headers | ||
return options | ||
} | ||
var getHeaders = function (params, options) { | ||
return constructObject() | ||
.extend(options.headers) | ||
.extend(params.options.headers) | ||
.done() | ||
} | ||
return function (uri, opts, callback) { | ||
var params = initParams(uri, opts, callback) | ||
params.options = extend(params.options, headerlessOptions(options)) | ||
if (options.headers) | ||
params.options.headers = getHeaders(params, options) | ||
if (isFunction(requester)) { | ||
if (method === request) { | ||
method = requester | ||
} else { | ||
params.options._requester = requester | ||
} | ||
} | ||
return method(params.options, params.callback) | ||
} | ||
} | ||
defaults = wrap(this) | ||
defaults.get = wrap(this.get) | ||
defaults.patch = wrap(this.patch) | ||
defaults.post = wrap(this.post) | ||
defaults.put = wrap(this.put) | ||
defaults.head = wrap(this.head) | ||
defaults.del = wrap(this.del) | ||
defaults.cookie = wrap(this.cookie) | ||
defaults.jar = this.jar | ||
defaults.defaults = this.defaults | ||
return defaults | ||
} | ||
request.forever = function (agentOptions, optionsArg) { | ||
var options = constructObject() | ||
if (optionsArg) options.extend(optionsArg) | ||
if (agentOptions) options.agentOptions = agentOptions | ||
options.extend({forever: true}) | ||
return request.defaults(options.done()) | ||
} | ||
// Exports | ||
module.exports = request | ||
request.Request = require('./request') | ||
request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG) | ||
request.initParams = initParams |
var util = require('util') | ||
, request = require('../index') | ||
; | ||
module.exports = | ||
function debug () { | ||
if (/\brequest\b/.test(process.env.NODE_DEBUG)) | ||
module.exports = function debug() { | ||
if (request.debug) { | ||
console.error('REQUEST %s', util.format.apply(util, arguments)) | ||
} | ||
} |
module.exports = function(moduleName) { | ||
try { | ||
return module.parent.require(moduleName); | ||
} catch (e) {} | ||
} catch (e) { | ||
// This could mean that we are in a browser context. | ||
// Add another try catch like it used to be, for backwards compability | ||
// and browserify reasons. | ||
try { | ||
return require(moduleName); | ||
} | ||
catch (e) {} | ||
} | ||
}; |
@@ -10,3 +10,3 @@ { | ||
], | ||
"version": "2.40.0", | ||
"version": "2.41.0", | ||
"author": "Mikeal Rogers <mikeal.rogers@gmail.com>", | ||
@@ -26,7 +26,10 @@ "repository": { | ||
"dependencies": { | ||
"qs": "~1.0.0", | ||
"bl": "~0.9.0", | ||
"caseless": "~0.6.0", | ||
"forever-agent": "~0.5.0", | ||
"qs": "~1.2.0", | ||
"json-stringify-safe": "~5.0.0", | ||
"mime-types": "~1.0.1", | ||
"forever-agent": "~0.5.0", | ||
"node-uuid": "~1.4.0" | ||
"node-uuid": "~1.4.0", | ||
"tunnel-agent": "~0.4.0" | ||
}, | ||
@@ -36,5 +39,4 @@ "optionalDependencies": { | ||
"form-data": "~0.1.0", | ||
"tunnel-agent": "~0.4.0", | ||
"http-signature": "~0.10.0", | ||
"oauth-sign": "~0.3.0", | ||
"oauth-sign": "~0.4.0", | ||
"hawk": "1.1.1", | ||
@@ -46,3 +48,6 @@ "aws-sign2": "~0.5.0", | ||
"test": "node tests/run.js" | ||
}, | ||
"devDependencies": { | ||
"rimraf": "~2.2.8" | ||
} | ||
} |
131
README.md
# Request — Simplified HTTP client | ||
[![NPM](https://nodei.co/npm/request.png)](https://nodei.co/npm/request/) | ||
[![NPM](https://nodei.co/npm/request.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/request/) | ||
@@ -84,2 +84,90 @@ ## Super simple to use | ||
## Proxies | ||
If you specify a `proxy` option, then the request (and any subsequent | ||
redirects) will be sent via a connection to the proxy server. | ||
If your endpoint is an `https` url, and you are using a proxy, then | ||
request will send a `CONNECT` request to the proxy server *first*, and | ||
then use the supplied connection to connect to the endpoint. | ||
That is, first it will make a request like: | ||
``` | ||
HTTP/1.1 CONNECT endpoint-server.com:80 | ||
Host: proxy-server.com | ||
User-Agent: whatever user agent you specify | ||
``` | ||
and then the proxy server make a TCP connection to `endpoint-server` | ||
on port `80`, and return a response that looks like: | ||
``` | ||
HTTP/1.1 200 OK | ||
``` | ||
At this point, the connection is left open, and the client is | ||
communicating directly with the `endpoint-server.com` machine. | ||
See (the wikipedia page on HTTP | ||
Tunneling)[http://en.wikipedia.org/wiki/HTTP_tunnel] for more | ||
information. | ||
By default, when proxying `http` traffic, request will simply make a | ||
standard proxied `http` request. This is done by making the `url` | ||
section of the initial line of the request a fully qualified url to | ||
the endpoint. | ||
For example, it will make a single request that looks like: | ||
``` | ||
HTTP/1.1 GET http://endpoint-server.com/some-url | ||
Host: proxy-server.com | ||
Other-Headers: all go here | ||
request body or whatever | ||
``` | ||
Because a pure "http over http" tunnel offers no additional security | ||
or other features, it is generally simpler to go with a | ||
straightforward HTTP proxy in this case. However, if you would like | ||
to force a tunneling proxy, you may set the `tunnel` option to `true`. | ||
If you are using a tunneling proxy, you may set the | ||
`proxyHeaderWhiteList` to share certain headers with the proxy. | ||
By default, this set is: | ||
``` | ||
accept | ||
accept-charset | ||
accept-encoding | ||
accept-language | ||
accept-ranges | ||
cache-control | ||
content-encoding | ||
content-language | ||
content-length | ||
content-location | ||
content-md5 | ||
content-range | ||
content-type | ||
connection | ||
date | ||
expect | ||
max-forwards | ||
pragma | ||
proxy-authorization | ||
referer | ||
te | ||
transfer-encoding | ||
user-agent | ||
via | ||
``` | ||
Note that, when using a tunneling proxy, the `proxy-authorization` | ||
header is *never* sent to the endpoint server, but only to the proxy | ||
server. All other headers are sent as-is over the established | ||
connection. | ||
## UNIX Socket | ||
@@ -219,3 +307,3 @@ | ||
### Custom HTTP Headers | ||
## Custom HTTP Headers | ||
@@ -248,3 +336,3 @@ HTTP Headers, such as `User-Agent`, can be set in the `options` object. | ||
### request(options, callback) | ||
## request(options, callback) | ||
@@ -262,3 +350,3 @@ The first argument can be either a `url` or an `options` object. The only required option is `uri`; all others are optional. | ||
* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below. | ||
* `followRedirect` - follow HTTP 3xx responses as redirects (default: `true`) | ||
* `followRedirect` - follow HTTP 3xx responses as redirects (default: `true`). This property can also be implemented as function which gets `response` object as a single argument and should return `true` if redirects should continue or `false` otherwise. | ||
* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects (default: `false`) | ||
@@ -279,2 +367,8 @@ * `maxRedirects` - the maximum number of redirects to follow (default: `10`) | ||
* `gzip` - If `true`, add an `Accept-Encoding` header to request compressed content encodings from the server (if not already present) and decode supported content encodings in the response. | ||
* `tunnel` - If `true`, then *always* use a tunneling proxy. If | ||
`false` (default), then tunneling will only be used if the | ||
destination is `https`, or if a previous request in the redirect | ||
chain used a tunneling proxy. | ||
* `proxyHeaderWhiteList` - A whitelist of headers to send to a | ||
tunneling proxy. | ||
@@ -296,2 +390,18 @@ | ||
**Note:** You can call `.defaults()` on the wrapper that is returned from `request.defaults` to add/override defaults that were previously defaulted. | ||
For example: | ||
```javascript | ||
//requests using baseRequest() will set the 'x-token' header | ||
var baseRequest = request.defaults({ | ||
headers: {x-token: 'my-token'} | ||
}) | ||
//requests using specialRequest() will include the 'x-token' header set in | ||
//baseRequest and will also include the 'special' header | ||
var specialRequest = baseRequest.defaults({ | ||
headers: {special: 'special value'} | ||
}) | ||
``` | ||
### request.put | ||
@@ -428,1 +538,14 @@ | ||
``` | ||
## Debugging | ||
There are at least three ways to debug the operation of `request`: | ||
1. Launch the node process like `NODE_DEBUG=request node script.js` | ||
(`lib,request,otherlib` works too). | ||
2. Set `require('request').debug = true` at any time (this does the same thing | ||
as #1). | ||
3. Use the [request-debug module](https://github.com/nylen/request-debug) to | ||
view request and response headers and bodies. |
365
request.js
@@ -13,2 +13,3 @@ var optional = require('./lib/optional') | ||
, bl = require('bl') | ||
, oauth = optional('oauth-sign') | ||
@@ -20,5 +21,6 @@ , hawk = optional('hawk') | ||
, mime = require('mime-types') | ||
, tunnel = optional('tunnel-agent') | ||
, tunnel = require('tunnel-agent') | ||
, _safeStringify = require('json-stringify-safe') | ||
, stringstream = optional('stringstream') | ||
, caseless = require('caseless') | ||
@@ -33,3 +35,2 @@ , ForeverAgent = require('forever-agent') | ||
, debug = require('./lib/debug') | ||
, getSafe = require('./lib/getSafe') | ||
, net = require('net') | ||
@@ -48,18 +49,29 @@ ; | ||
var defaultProxyHeaderWhiteList = [ | ||
'accept', | ||
'accept-charset', | ||
'accept-encoding', | ||
'accept-language', | ||
'accept-ranges', | ||
'cache-control', | ||
'content-encoding', | ||
'content-language', | ||
'content-length', | ||
'content-location', | ||
'content-md5', | ||
'content-range', | ||
'content-type', | ||
'connection', | ||
'date', | ||
'expect', | ||
'max-forwards', | ||
'pragma', | ||
'proxy-authorization', | ||
'referer', | ||
'te', | ||
'transfer-encoding', | ||
'user-agent', | ||
'via' | ||
] | ||
// Hacky fix for pre-0.4.4 https | ||
if (https && !https.Agent) { | ||
https.Agent = function (options) { | ||
http.Agent.call(this, options) | ||
} | ||
util.inherits(https.Agent, http.Agent) | ||
https.Agent.prototype._getConnection = function (host, port, cb) { | ||
var s = tls.connect(port, host, this.options, function () { | ||
// do other checks here? | ||
if (cb) cb() | ||
}) | ||
return s | ||
} | ||
} | ||
function isReadStream (rs) { | ||
@@ -77,2 +89,21 @@ return rs.readable && rs.path && rs.mode; | ||
// Return a simpler request object to allow serialization | ||
function requestToJSON() { | ||
return { | ||
uri: this.uri, | ||
method: this.method, | ||
headers: this.headers | ||
} | ||
} | ||
// Return a simpler response object to allow serialization | ||
function responseToJSON() { | ||
return { | ||
statusCode: this.statusCode, | ||
body: this.body, | ||
headers: this.headers, | ||
request: requestToJSON.call(this.request) | ||
} | ||
} | ||
function Request (options) { | ||
@@ -102,3 +133,4 @@ stream.Stream.call(this) | ||
this.canTunnel = options.tunnel !== false && tunnel; | ||
// Assume that we're not going to tunnel unless we need to | ||
if (typeof options.tunnel === 'undefined') options.tunnel = false | ||
@@ -108,2 +140,75 @@ this.init(options) | ||
util.inherits(Request, stream.Stream) | ||
// Set up the tunneling agent if necessary | ||
Request.prototype.setupTunnel = function () { | ||
var self = this | ||
if (typeof self.proxy == 'string') self.proxy = url.parse(self.proxy) | ||
if (!self.proxy) return false | ||
// Don't need to use a tunneling proxy | ||
if (!self.tunnel && self.uri.protocol !== 'https:') | ||
return | ||
// do the HTTP CONNECT dance using koichik/node-tunnel | ||
// The host to tell the proxy to CONNECT to | ||
var proxyHost = self.uri.hostname + ':' | ||
if (self.uri.port) | ||
proxyHost += self.uri.port | ||
else if (self.uri.protocol === 'https:') | ||
proxyHost += '443' | ||
else | ||
proxyHost += '80' | ||
if (!self.proxyHeaderWhiteList) | ||
self.proxyHeaderWhiteList = defaultProxyHeaderWhiteList | ||
// Only send the proxy the whitelisted header names. | ||
var proxyHeaders = Object.keys(self.headers).filter(function (h) { | ||
return self.proxyHeaderWhiteList.indexOf(h.toLowerCase()) !== -1 | ||
}).reduce(function (set, h) { | ||
set[h] = self.headers[h] | ||
return set | ||
}, {}) | ||
proxyHeaders.host = proxyHost | ||
var tunnelFnName = | ||
(self.uri.protocol === 'https:' ? 'https' : 'http') + | ||
'Over' + | ||
(self.proxy.protocol === 'https:' ? 'Https' : 'Http') | ||
var tunnelFn = tunnel[tunnelFnName] | ||
var proxyAuth | ||
if (self.proxy.auth) | ||
proxyAuth = self.proxy.auth | ||
else if (self.proxyAuthorization) | ||
proxyHeaders['Proxy-Authorization'] = self.proxyAuthorization | ||
var tunnelOptions = { proxy: { host: self.proxy.hostname | ||
, port: +self.proxy.port | ||
, proxyAuth: proxyAuth | ||
, headers: proxyHeaders } | ||
, rejectUnauthorized: self.rejectUnauthorized | ||
, headers: self.headers | ||
, ca: self.ca | ||
, cert: self.cert | ||
, key: self.key} | ||
self.agent = tunnelFn(tunnelOptions) | ||
// At this point, we know that the proxy will support tunneling | ||
// (or fail miserably), so we're going to tunnel all proxied requests | ||
// from here on out. | ||
self.tunnel = true | ||
return true | ||
} | ||
Request.prototype.init = function (options) { | ||
@@ -116,2 +221,10 @@ // init() contains all the code to setup the request object. | ||
caseless.httpify(self, self.headers || {}) | ||
// Never send proxy-auth to the endpoint! | ||
if (self.hasHeader('proxy-authorization')) { | ||
self.proxyAuthorization = self.getHeader('proxy-authorization') | ||
self.removeHeader('proxy-authorization') | ||
} | ||
if (!self.method) self.method = options.method || 'GET' | ||
@@ -159,28 +272,11 @@ self.localAddress = options.localAddress | ||
} else if(self.uri.protocol == "https:") { | ||
self.proxy = process.env.HTTPS_PROXY || process.env.https_proxy || | ||
self.proxy = process.env.HTTPS_PROXY || process.env.https_proxy || | ||
process.env.HTTP_PROXY || process.env.http_proxy || null; | ||
} | ||
} | ||
// Pass in `tunnel:true` to *always* tunnel through proxies | ||
self.tunnel = !!options.tunnel | ||
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.canTunnel) { | ||
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 | ||
, headers: { Host: self.uri.hostname + ':' + | ||
(self.uri.port || self.uri.protocol === 'https:' ? 443 : 80) }} | ||
, rejectUnauthorized: self.rejectUnauthorized | ||
, ca: this.ca | ||
, cert:this.cert | ||
, key: this.key} | ||
self.agent = tunnelFn(tunnelOptions) | ||
self.tunnel = true | ||
} | ||
self.setupTunnel() | ||
} | ||
@@ -207,3 +303,6 @@ | ||
self.maxRedirects = (self.maxRedirects !== undefined) ? self.maxRedirects : 10 | ||
self.followRedirect = (self.followRedirect !== undefined) ? self.followRedirect : true | ||
self.allowRedirect = (typeof self.followRedirect === 'function') ? self.followRedirect : function(response) { | ||
return true; | ||
}; | ||
self.followRedirect = (self.followRedirect !== undefined) ? !!self.followRedirect : true | ||
self.followAllRedirects = (self.followAllRedirects !== undefined) ? self.followAllRedirects : false | ||
@@ -213,4 +312,2 @@ if (self.followRedirect || self.followAllRedirects) | ||
self.headers = self.headers ? copy(self.headers) : {} | ||
self.setHost = false | ||
@@ -325,7 +422,13 @@ if (!self.hasHeader('host')) { | ||
} | ||
if (self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization') && !self.tunnel) { | ||
self.setHeader('proxy-authorization', "Basic " + toBase64(self.proxy.auth.split(':').map(function(item){ return querystring.unescape(item)}).join(':'))) | ||
if (self.proxy && !self.tunnel) { | ||
if (self.proxy.auth && !self.proxyAuthorization) { | ||
var authPieces = self.uri.auth.split(':').map(function(item){ return querystring.unescape(item) }) | ||
var authHeader = 'Basic ' + toBase64(authPieces[0], authPieces.slice(1).join(':')) | ||
self.proxyAuthorization = authHeader | ||
} | ||
if (self.proxyAuthorization) | ||
self.setHeader('proxy-authorization', self.proxyAuthorization) | ||
} | ||
if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path) | ||
@@ -451,3 +554,3 @@ | ||
// Before ending the request, we had to compute the length of the whole form, asyncly | ||
self.setHeaders(self._form.getHeaders()) | ||
self.setHeader(self._form.getHeaders()) | ||
self._form.getLength(function (err, length) { | ||
@@ -507,3 +610,2 @@ if (!err) { | ||
for (r in lookup_table){ | ||
//console.log(r, lookup_table[r], lookup_table[r].error_connecting) | ||
if('undefined' == typeof lookup_table[r].error_connecting) | ||
@@ -559,16 +661,7 @@ trying = true; | ||
if (protocol === 'https:') { | ||
if (protocol === 'https:' || self.tunnel) { | ||
// previously was doing http, now doing https | ||
// if it's https, then we might need to tunnel now. | ||
if (self.proxy && self.canTunnel) { | ||
self.tunnel = true | ||
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 } | ||
, rejectUnauthorized: self.rejectUnauthorized | ||
, ca: self.ca } | ||
self.agent = tunnelFn(tunnelOptions) | ||
return | ||
if (self.proxy) { | ||
if (self.setupTunnel()) return | ||
} | ||
@@ -594,4 +687,2 @@ | ||
// previously was doing https, now doing http | ||
// stop any tunneling. | ||
if (self.tunnel) self.tunnel = false | ||
self.httpModule = http | ||
@@ -780,10 +871,10 @@ switch (self.agentClass) { | ||
response.request = self | ||
response.toJSON = toJSON | ||
response.toJSON = responseToJSON | ||
// XXX This is different on 0.10, because SSL is strict by default | ||
if (self.httpModule === https && | ||
self.strictSSL && | ||
!response.client.authorized) { | ||
self.strictSSL && (!response.hasOwnProperty('client') || | ||
!response.client.authorized)) { | ||
debug('strict ssl error', self.uri.href) | ||
var sslErr = response.client.authorizationError | ||
var sslErr = response.hasOwnProperty('client') ? response.client.authorizationError : self.uri.href + " does not support SSL"; | ||
self.emit('error', new Error('SSL Error: '+ sslErr)) | ||
@@ -793,3 +884,3 @@ return | ||
if (self.setHost && self.hasHeader('host')) delete self.headers[self.hasHeader('host')] | ||
if (self.setHost) self.removeHeader('host') | ||
if (self.timeout && self.timeoutTimer) { | ||
@@ -810,4 +901,6 @@ clearTimeout(self.timeoutTimer) | ||
if (hasHeader('set-cookie', response.headers) && (!self._disableCookies)) { | ||
var headerName = hasHeader('set-cookie', response.headers) | ||
response.caseless = caseless(response.headers) | ||
if (response.caseless.has('set-cookie') && (!self._disableCookies)) { | ||
var headerName = response.caseless.has('set-cookie') | ||
if (Array.isArray(response.headers[headerName])) response.headers[headerName].forEach(addCookie) | ||
@@ -818,4 +911,4 @@ else addCookie(response.headers[headerName]) | ||
var redirectTo = null | ||
if (response.statusCode >= 300 && response.statusCode < 400 && hasHeader('location', response.headers)) { | ||
var location = response.headers[hasHeader('location', response.headers)] | ||
if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) { | ||
var location = response.caseless.get('location') | ||
debug('redirect', location) | ||
@@ -839,3 +932,3 @@ | ||
} else if (response.statusCode == 401 && self._hasAuth && !self._sentAuth) { | ||
var authHeader = response.headers[hasHeader('www-authenticate', response.headers)] | ||
var authHeader = response.caseless.get('www-authenticate') | ||
var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase() | ||
@@ -914,3 +1007,3 @@ debug('reauth', authVerb) | ||
if (redirectTo) { | ||
if (redirectTo && self.allowRedirect.call(self, response)) { | ||
debug('redirect to', redirectTo) | ||
@@ -957,5 +1050,5 @@ | ||
if (self.headers) { | ||
if (self.hasHeader('host')) delete self.headers[self.hasHeader('host')] | ||
if (self.hasHeader('content-type')) delete self.headers[self.hasHeader('content-type')] | ||
if (self.hasHeader('content-length')) delete self.headers[self.hasHeader('content-length')] | ||
self.removeHeader('host') | ||
self.removeHeader('content-type') | ||
self.removeHeader('content-length') | ||
} | ||
@@ -976,2 +1069,6 @@ } | ||
response.on('end', function () { | ||
self._ended = true | ||
}) | ||
var dataStream | ||
@@ -1021,3 +1118,2 @@ if (self.gzip) { | ||
dataStream.on("end", function (chunk) { | ||
self._ended = true | ||
self.emit("end", chunk) | ||
@@ -1028,7 +1124,8 @@ }) | ||
if (self.callback) { | ||
var buffer = [] | ||
var bodyLen = 0 | ||
var buffer = bl() | ||
, strings = [] | ||
; | ||
self.on("data", function (chunk) { | ||
buffer.push(chunk) | ||
bodyLen += chunk.length | ||
if (Buffer.isBuffer(chunk)) buffer.append(chunk) | ||
else strings.push(chunk) | ||
}) | ||
@@ -1042,22 +1139,18 @@ self.on("end", function () { | ||
if (buffer.length && Buffer.isBuffer(buffer[0])) { | ||
debug('has body', self.uri.href, bodyLen) | ||
var body = new Buffer(bodyLen) | ||
var i = 0 | ||
buffer.forEach(function (chunk) { | ||
chunk.copy(body, i, 0, chunk.length) | ||
i += chunk.length | ||
}) | ||
if (buffer.length) { | ||
debug('has body', self.uri.href, buffer.length) | ||
if (self.encoding === null) { | ||
response.body = body | ||
// response.body = buffer | ||
// can't move to this until https://github.com/rvagg/bl/issues/13 | ||
response.body = buffer.slice() | ||
} else { | ||
response.body = body.toString(self.encoding) | ||
response.body = buffer.toString(self.encoding) | ||
} | ||
} else if (buffer.length) { | ||
} else if (strings.length) { | ||
// The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation. | ||
// Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse(). | ||
if (self.encoding === 'utf8' && buffer[0].length > 0 && buffer[0][0] === "\uFEFF") { | ||
buffer[0] = buffer[0].substring(1) | ||
if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === "\uFEFF") { | ||
strings[0] = strings[0].substring(1) | ||
} | ||
response.body = buffer.join('') | ||
response.body = strings.join('') | ||
} | ||
@@ -1108,4 +1201,4 @@ | ||
if (dest.headers && !dest.headersSent) { | ||
if (hasHeader('content-type', response.headers)) { | ||
var ctname = hasHeader('content-type', response.headers) | ||
if (response.caseless.has('content-type')) { | ||
var ctname = response.caseless.has('content-type') | ||
if (dest.setHeader) dest.setHeader(ctname, response.headers[ctname]) | ||
@@ -1115,4 +1208,4 @@ else dest.headers[ctname] = response.headers[ctname] | ||
if (hasHeader('content-length', response.headers)) { | ||
var clname = hasHeader('content-length', response.headers) | ||
if (response.caseless.has('content-length')) { | ||
var clname = response.caseless.has('content-length') | ||
if (dest.setHeader) dest.setHeader(clname, response.headers[clname]) | ||
@@ -1135,26 +1228,2 @@ else dest.headers[clname] = response.headers[clname] | ||
// Composable API | ||
Request.prototype.setHeader = function (name, value, clobber) { | ||
if (clobber === undefined) clobber = true | ||
if (clobber || !this.hasHeader(name)) this.headers[name] = value | ||
else this.headers[this.hasHeader(name)] += ',' + value | ||
return this | ||
} | ||
Request.prototype.setHeaders = function (headers) { | ||
for (var i in headers) {this.setHeader(i, headers[i])} | ||
return this | ||
} | ||
Request.prototype.hasHeader = function (header, headers) { | ||
var headers = Object.keys(headers || this.headers) | ||
, lheaders = headers.map(function (h) {return h.toLowerCase()}) | ||
; | ||
header = header.toLowerCase() | ||
for (var i=0;i<lheaders.length;i++) { | ||
if (lheaders[i] === header) return headers[i] | ||
} | ||
return false | ||
} | ||
var hasHeader = Request.prototype.hasHeader | ||
Request.prototype.qs = function (q, clobber) { | ||
@@ -1281,2 +1350,3 @@ var base | ||
} | ||
Request.prototype.aws = function (opts, now) { | ||
@@ -1298,9 +1368,10 @@ if (!now) { | ||
} | ||
if (opts.bucket && this.path) { | ||
auth.resource = '/' + opts.bucket + this.path | ||
} else if (opts.bucket && !this.path) { | ||
var path = this.uri.path; | ||
if (opts.bucket && path) { | ||
auth.resource = '/' + opts.bucket + path | ||
} else if (opts.bucket && !path) { | ||
auth.resource = '/' + opts.bucket | ||
} else if (!opts.bucket && this.path) { | ||
auth.resource = this.path | ||
} else if (!opts.bucket && !this.path) { | ||
} else if (!opts.bucket && path) { | ||
auth.resource = path | ||
} else if (!opts.bucket && !path) { | ||
auth.resource = '/' | ||
@@ -1335,3 +1406,3 @@ } | ||
Request.prototype.oauth = function (_oauth) { | ||
var form | ||
var form, query | ||
if (this.hasHeader('content-type') && | ||
@@ -1341,11 +1412,12 @@ this.getHeader('content-type').slice(0, 'application/x-www-form-urlencoded'.length) === | ||
) { | ||
form = qs.parse(this.body) | ||
form = this.body | ||
} | ||
if (this.uri.query) { | ||
form = qs.parse(this.uri.query) | ||
query = 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 ('oauth_realm' in oa) delete oa.oauth_realm | ||
if (!oa.oauth_version) oa.oauth_version = '1.0' | ||
@@ -1361,18 +1433,10 @@ if (!oa.oauth_timestamp) oa.oauth_timestamp = Math.floor( Date.now() / 1000 ).toString() | ||
delete oa.oauth_token_secret | ||
var timestamp = oa.oauth_timestamp | ||
var baseurl = this.uri.protocol + '//' + this.uri.host + this.uri.pathname | ||
var signature = oauth.hmacsign(this.method, baseurl, oa, consumer_secret, token_secret) | ||
var params = qs.parse([].concat(query, form, qs.stringify(oa)).join('&')) | ||
var signature = oauth.hmacsign(this.method, baseurl, params, 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] | ||
if (i !== 'x_auth_mode') delete oa[i] | ||
} | ||
} | ||
oa.oauth_timestamp = timestamp | ||
var authHeader = 'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',') | ||
var realm = _oauth.realm ? 'realm="' + _oauth.realm + '",' : ''; | ||
var authHeader = 'OAuth ' + realm + | ||
Object.keys(oa).sort().map(function (i) {return i+'="'+oauth.rfc3986(oa[i])+'"'}).join(',') | ||
authHeader += ',oauth_signature="' + oauth.rfc3986(signature) + '"' | ||
@@ -1456,9 +1520,8 @@ this.setHeader('Authorization', authHeader) | ||
function toJSON () { | ||
return getSafe(this, '__' + (((1+Math.random())*0x10000)|0).toString(16)) | ||
} | ||
Request.prototype.toJSON = requestToJSON | ||
Request.prototype.toJSON = toJSON | ||
Request.defaultProxyHeaderWhiteList = | ||
defaultProxyHeaderWhiteList.slice() | ||
module.exports = Request |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
210878
15
1528
545
15
1
+ Addedbl@~0.9.0
+ Addedcaseless@~0.6.0
+ Addedtunnel-agent@~0.4.0
+ Addedbl@0.9.5(transitive)
+ Addedcaseless@0.6.0(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@0.0.1(transitive)
+ Addedoauth-sign@0.4.0(transitive)
+ Addedqs@1.2.2(transitive)
+ Addedreadable-stream@1.0.34(transitive)
+ Addedstring_decoder@0.10.31(transitive)
- Removedoauth-sign@0.3.0(transitive)
- Removedqs@1.0.2(transitive)
Updatedqs@~1.2.0