follow-redirects
Advanced tools
Comparing version 0.1.0 to 0.2.0
242
create.js
'use strict'; | ||
var url = require('url'); | ||
var assert = require('assert'); | ||
var debug = require('debug')('follow-redirects'); | ||
var assert = require('assert'); | ||
var consume = require('stream-consume'); | ||
module.exports = function(_nativeProtocols) { | ||
var nativeProtocols = {}; | ||
module.exports = function (_nativeProtocols) { | ||
var nativeProtocols = {}; | ||
var publicApi = { | ||
maxRedirects: 5 | ||
}; | ||
var publicApi = { | ||
maxRedirects: 5 | ||
}; | ||
for (var p in _nativeProtocols) { | ||
/* istanbul ignore else */ | ||
if (_nativeProtocols.hasOwnProperty(p)) { | ||
// http://www.ietf.org/rfc/rfc2396.txt - Section 3.1 | ||
assert(/^[A-Z][A-Z\+\-\.]*$/i.test(p), JSON.stringify(p) + ' is not a valid scheme name'); | ||
generateWrapper(p, _nativeProtocols[p]); | ||
} | ||
} | ||
for (var p in _nativeProtocols) { | ||
/* istanbul ignore else */ | ||
if (_nativeProtocols.hasOwnProperty(p)) { | ||
// http://www.ietf.org/rfc/rfc2396.txt - Section 3.1 | ||
assert(/^[A-Z][A-Z\+\-\.]*$/i.test(p), JSON.stringify(p) + ' is not a valid scheme name'); | ||
generateWrapper(p, _nativeProtocols[p]); | ||
} | ||
} | ||
return publicApi; | ||
return publicApi; | ||
function execute(options, callback) { | ||
var fetchedUrls = []; | ||
var clientRequest = cb(); | ||
function execute(options, callback) { | ||
var fetchedUrls = []; | ||
var clientRequest = cb(); | ||
// return a proxy to the request with separate event handling | ||
var requestProxy = Object.create(clientRequest); | ||
requestProxy._events = {}; | ||
requestProxy._eventsCount = 0; | ||
if (callback) { | ||
requestProxy.on('response', callback); | ||
} | ||
return requestProxy; | ||
// return a proxy to the request with separate event handling | ||
var requestProxy = Object.create(clientRequest); | ||
requestProxy._events = {}; | ||
requestProxy._eventsCount = 0; | ||
if (callback) { | ||
requestProxy.on('response', callback); | ||
} | ||
return requestProxy; | ||
function cb(res) { | ||
// skip the redirection logic on the first call. | ||
if (res) { | ||
var fetchedUrl = url.format(options); | ||
fetchedUrls.unshift(fetchedUrl); | ||
function cb(res) { | ||
// skip the redirection logic on the first call. | ||
if (res) { | ||
var fetchedUrl = url.format(options); | ||
fetchedUrls.unshift(fetchedUrl); | ||
if (!isRedirect(res)) { | ||
res.fetchedUrls = fetchedUrls; | ||
requestProxy.emit('response', res); | ||
return; | ||
} | ||
if (!isRedirect(res)) { | ||
res.fetchedUrls = fetchedUrls; | ||
requestProxy.emit('response', res); | ||
return; | ||
} | ||
// we are going to follow the redirect, but in node 0.10 we must first attach a data listener | ||
// to consume the stream and send the 'end' event | ||
consume(res); | ||
// we are going to follow the redirect, but in node 0.10 we must first attach a data listener | ||
// to consume the stream and send the 'end' event | ||
consume(res); | ||
// need to use url.resolve() in case location is a relative URL | ||
var redirectUrl = url.resolve(fetchedUrl, res.headers.location); | ||
debug('redirecting to', redirectUrl); | ||
// need to use url.resolve() in case location is a relative URL | ||
var redirectUrl = url.resolve(fetchedUrl, res.headers.location); | ||
debug('redirecting to', redirectUrl); | ||
// clean all the properties related to the old url away, and copy from the redirect url | ||
wipeUrlProps(options); | ||
extend(options, url.parse(redirectUrl)); | ||
} | ||
// clean all the properties related to the old url away, and copy from the redirect url | ||
wipeUrlProps(options); | ||
extend(options, url.parse(redirectUrl)); | ||
} | ||
if (fetchedUrls.length > options.maxRedirects) { | ||
var err = new Error('Max redirects exceeded.'); | ||
return forwardError(err); | ||
} | ||
if (fetchedUrls.length > options.maxRedirects) { | ||
var err = new Error('Max redirects exceeded.'); | ||
return forwardError(err); | ||
} | ||
options.nativeProtocol = nativeProtocols[options.protocol]; | ||
options.defaultRequest = defaultMakeRequest; | ||
options.nativeProtocol = nativeProtocols[options.protocol]; | ||
options.defaultRequest = defaultMakeRequest; | ||
var req = (options.makeRequest || defaultMakeRequest)(options, cb, res); | ||
req.on('error', forwardError); | ||
return req; | ||
} | ||
var req = (options.makeRequest || defaultMakeRequest)(options, cb, res); | ||
req.on('error', forwardError); | ||
return req; | ||
} | ||
function defaultMakeRequest(options, cb, res) { | ||
if (res) { | ||
// This is a redirect, so use only GET methods | ||
options.method = 'GET'; | ||
} | ||
function defaultMakeRequest(options, cb, res) { | ||
if (res && res.statusCode !== 307) { | ||
// This is a redirect, so use only GET methods, except for status 307, | ||
// which must honor the previous request method. | ||
options.method = 'GET'; | ||
} | ||
var req = options.nativeProtocol.request(options, cb); | ||
var req = options.nativeProtocol.request(options, cb); | ||
if (res) { | ||
// We leave the user to call `end` on the first request | ||
req.end(); | ||
} | ||
if (res) { | ||
// We leave the user to call `end` on the first request | ||
req.end(); | ||
} | ||
return req; | ||
} | ||
return req; | ||
} | ||
// bubble errors that occur on the redirect back up to the initiating client request | ||
// object, otherwise they wind up killing the process. | ||
function forwardError (err) { | ||
requestProxy.emit('error', err); | ||
} | ||
} | ||
// bubble errors that occur on the redirect back up to the initiating client request | ||
// object, otherwise they wind up killing the process. | ||
function forwardError(err) { | ||
requestProxy.emit('error', err); | ||
} | ||
} | ||
function generateWrapper (scheme, nativeProtocol) { | ||
var wrappedProtocol = scheme + ':'; | ||
var H = function() {}; | ||
H.prototype = nativeProtocols[wrappedProtocol] = nativeProtocol; | ||
H = new H(); | ||
publicApi[scheme] = H; | ||
function generateWrapper(scheme, nativeProtocol) { | ||
var wrappedProtocol = scheme + ':'; | ||
var H = function () {}; | ||
H.prototype = nativeProtocols[wrappedProtocol] = nativeProtocol; | ||
H = new H(); | ||
publicApi[scheme] = H; | ||
H.request = function(options, callback) { | ||
return execute(parseOptions(options, wrappedProtocol), callback); | ||
}; | ||
H.request = function (options, callback) { | ||
return execute(parseOptions(options, wrappedProtocol), callback); | ||
}; | ||
// see https://github.com/joyent/node/blob/master/lib/http.js#L1623 | ||
H.get = function(options, callback) { | ||
var req = execute(parseOptions(options, wrappedProtocol), callback); | ||
req.end(); | ||
return req; | ||
}; | ||
} | ||
// see https://github.com/joyent/node/blob/master/lib/http.js#L1623 | ||
H.get = function (options, callback) { | ||
var req = execute(parseOptions(options, wrappedProtocol), callback); | ||
req.end(); | ||
return req; | ||
}; | ||
} | ||
// returns a safe copy of options (or a parsed url object if options was a string). | ||
// validates that the supplied callback is a function | ||
function parseOptions (options, wrappedProtocol) { | ||
if ('string' === typeof options) { | ||
options = url.parse(options); | ||
options.maxRedirects = publicApi.maxRedirects; | ||
} else { | ||
options = extend({ | ||
maxRedirects: publicApi.maxRedirects, | ||
protocol: wrappedProtocol | ||
}, options); | ||
} | ||
assert.equal(options.protocol, wrappedProtocol, 'protocol mismatch'); | ||
// returns a safe copy of options (or a parsed url object if options was a string). | ||
// validates that the supplied callback is a function | ||
function parseOptions(options, wrappedProtocol) { | ||
if (typeof options === 'string') { | ||
options = url.parse(options); | ||
options.maxRedirects = publicApi.maxRedirects; | ||
} else { | ||
options = extend({ | ||
maxRedirects: publicApi.maxRedirects, | ||
protocol: wrappedProtocol | ||
}, options); | ||
} | ||
assert.equal(options.protocol, wrappedProtocol, 'protocol mismatch'); | ||
debug('options', options); | ||
return options; | ||
} | ||
debug('options', options); | ||
return options; | ||
} | ||
}; | ||
@@ -139,8 +140,8 @@ | ||
function extend(destination, source) { | ||
for (var i in source) { | ||
if (source.hasOwnProperty(i)) { | ||
destination[i] = source[i]; | ||
} | ||
} | ||
return destination; | ||
for (var i in source) { | ||
if (source.hasOwnProperty(i)) { | ||
destination[i] = source[i]; | ||
} | ||
} | ||
return destination; | ||
} | ||
@@ -151,15 +152,16 @@ | ||
// and a `Location` header | ||
function isRedirect (res) { | ||
return (res.statusCode >= 300 && res.statusCode <= 399 && | ||
'location' in res.headers); | ||
function isRedirect(res) { | ||
return (res.statusCode >= 300 && res.statusCode <= 399 && | ||
'location' in res.headers); | ||
} | ||
var urlProps = ['protocol', 'slashes', 'auth', 'host', 'port', 'hostname', | ||
'hash', 'search', 'query', 'pathname', 'path', 'href']; | ||
// nulls all url related properties on the object. | ||
// required on node <10 | ||
function wipeUrlProps(options) { | ||
for (var i = 0, l = urlProps.length; i < l; ++i) { | ||
options[urlProps[i]] = null; | ||
} | ||
for (var i = 0, l = urlProps.length; i < l; ++i) { | ||
options[urlProps[i]] = null; | ||
} | ||
} | ||
var urlProps = ['protocol', 'slashes', 'auth', 'host', 'port', 'hostname', | ||
'hash', 'search', 'query', 'pathname', 'path', 'href']; |
module.exports = require('./create')({ | ||
'http': require('http'), | ||
'https': require('https') | ||
http: require('http'), | ||
https: require('https') | ||
}); |
{ | ||
"name": "follow-redirects", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "HTTP and HTTPS modules that follow redirects.", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "npm run cover && npm run lint && npm run style", | ||
"lint": "jshint *.js test/*.js test/**/*.js", | ||
"style": "jscs *.js && jscs test/*.js test/**/*.js --config=test/.jscsrc", | ||
"cover": "BLUEBIRD_DEBUG=1 istanbul cover ./node_modules/.bin/_mocha", | ||
"debug": "BLUEBIRD_DEBUG=1 mocha" | ||
"test": "xo && BLUEBIRD_DEBUG=1 nyc mocha" | ||
}, | ||
@@ -49,13 +45,23 @@ "repository": { | ||
"devDependencies": { | ||
"bluebird": "^2.9.30", | ||
"bluebird": "^3.4.0", | ||
"concat-stream": "^1.5.0", | ||
"coveralls": "^2.11.2", | ||
"express": "^4.13.0", | ||
"istanbul": "^0.3.17", | ||
"jscs": "^1.13.1", | ||
"jshint": "^2.8.0", | ||
"mocha": "^2.2.5", | ||
"semver": "~4.3.6" | ||
"nyc": "^6.4.4", | ||
"semver": "~5.1.0", | ||
"xo": "^0.15.1" | ||
}, | ||
"license": "MIT" | ||
"license": "MIT", | ||
"nyc": { | ||
"reporter": [ | ||
"lcov", | ||
"text" | ||
] | ||
}, | ||
"xo": { | ||
"envs": [ | ||
"mocha" | ||
] | ||
} | ||
} |
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
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
8
143
2
11044