Spaces:
Configuration error
Configuration error
var url = require('url') | |
var isUrl = /^https?:/ | |
function Redirect (request) { | |
this.request = request | |
this.followRedirect = true | |
this.followRedirects = true | |
this.followAllRedirects = false | |
this.followOriginalHttpMethod = false | |
this.allowRedirect = function () { return true } | |
this.maxRedirects = 10 | |
this.redirects = [] | |
this.redirectsFollowed = 0 | |
this.removeRefererHeader = false | |
this.allowInsecureRedirect = false | |
} | |
Redirect.prototype.onRequest = function (options) { | |
var self = this | |
if (options.maxRedirects !== undefined) { | |
self.maxRedirects = options.maxRedirects | |
} | |
if (typeof options.followRedirect === 'function') { | |
self.allowRedirect = options.followRedirect | |
} | |
if (options.followRedirect !== undefined) { | |
self.followRedirects = !!options.followRedirect | |
} | |
if (options.followAllRedirects !== undefined) { | |
self.followAllRedirects = options.followAllRedirects | |
} | |
if (self.followRedirects || self.followAllRedirects) { | |
self.redirects = self.redirects || [] | |
} | |
if (options.removeRefererHeader !== undefined) { | |
self.removeRefererHeader = options.removeRefererHeader | |
} | |
if (options.followOriginalHttpMethod !== undefined) { | |
self.followOriginalHttpMethod = options.followOriginalHttpMethod | |
} | |
if (options.allowInsecureRedirect !== undefined) { | |
self.allowInsecureRedirect = options.allowInsecureRedirect | |
} | |
} | |
Redirect.prototype.redirectTo = function (response) { | |
var self = this | |
var request = self.request | |
var redirectTo = null | |
if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) { | |
var location = response.caseless.get('location') | |
request.debug('redirect', location) | |
if (self.followAllRedirects) { | |
redirectTo = location | |
} else if (self.followRedirects) { | |
switch (request.method) { | |
case 'PATCH': | |
case 'PUT': | |
case 'POST': | |
case 'DELETE': | |
// Do not follow redirects | |
break | |
default: | |
redirectTo = location | |
break | |
} | |
} | |
} else if (response.statusCode === 401) { | |
var authHeader = request._auth.onResponse(response) | |
if (authHeader) { | |
request.setHeader('authorization', authHeader) | |
redirectTo = request.uri | |
} | |
} | |
return redirectTo | |
} | |
Redirect.prototype.onResponse = function (response, callback) { | |
var self = this | |
var request = self.request | |
var redirectTo = self.redirectTo(response) | |
if (!redirectTo) return callback(null, false) | |
function processRedirect (shouldRedirect) { | |
if (!shouldRedirect) return callback(null, false) | |
if (typeof shouldRedirect === 'string') { | |
// overridden redirect url | |
request.debug('redirect overridden', redirectTo) | |
redirectTo = shouldRedirect | |
} | |
request.debug('redirect to', redirectTo) | |
// ignore any potential response body. it cannot possibly be useful | |
// to us at this point. | |
// response.resume should be defined, but check anyway before calling. Workaround for browserify. | |
if (response.resume) { | |
response.resume() | |
} | |
if (self.redirectsFollowed >= self.maxRedirects) { | |
return callback(new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href)) | |
} | |
self.redirectsFollowed += 1 | |
if (!isUrl.test(redirectTo)) { | |
redirectTo = url.resolve(request.uri.href, redirectTo) | |
} | |
var uriPrev = request.uri | |
request.uri = url.parse(redirectTo) | |
// handle the case where we change protocol from https to http or vice versa | |
if (request.uri.protocol !== uriPrev.protocol && self.allowInsecureRedirect) { | |
delete request.agent | |
} | |
self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo }) | |
if (self.followAllRedirects && request.method !== 'HEAD' && | |
response.statusCode !== 401 && response.statusCode !== 307) { | |
request.method = self.followOriginalHttpMethod ? request.method : 'GET' | |
} | |
// request.method = 'GET' // Force all redirects to use GET || commented out fixes #215 | |
delete request.src | |
delete request.req | |
delete request._started | |
if (response.statusCode !== 401 && response.statusCode !== 307) { | |
// Remove parameters from the previous response, unless this is the second request | |
// for a server that requires digest authentication. | |
delete request.body | |
delete request._form | |
if (request.headers) { | |
request.removeHeader('host') | |
request.removeHeader('content-type') | |
request.removeHeader('content-length') | |
if (request.uri.hostname !== request.originalHost.split(':')[0]) { | |
// Remove authorization if changing hostnames (but not if just | |
// changing ports or protocols). This matches the behavior of curl: | |
// https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710 | |
request.removeHeader('authorization') | |
} | |
} | |
} | |
if (!self.removeRefererHeader) { | |
request.setHeader('referer', uriPrev.href) | |
} | |
request.emit('redirect') | |
request.init() | |
callback(null, true) | |
} | |
// test allowRedirect arity; if has more than one argument, | |
// assume it's asynchronous via a callback | |
if (self.allowRedirect.length > 1) { | |
return self.allowRedirect.call(request, response, function (err, result) { | |
if (err) return callback(err) | |
processRedirect(result) | |
}) | |
} | |
var allowsRedirect = self.allowRedirect.call(request, response) | |
if (allowsRedirect && allowsRedirect.then) { | |
return allowsRedirect.then(processRedirect, callback) | |
} | |
// treat as a regular boolean | |
processRedirect(allowsRedirect) | |
} | |
exports.Redirect = Redirect | |