| 'use strict' |
| |
| 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 |
| } |
| |
| 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 |
| } |
| } |
| |
| 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) { |
| var self = this |
| var request = self.request |
| |
| var redirectTo = self.redirectTo(response) |
| if (!redirectTo || !self.allowRedirect.call(request, response)) { |
| return false |
| } |
| |
| 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) { |
| request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href)) |
| return false |
| } |
| 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) { |
| 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() |
| |
| return true |
| } |
| |
| exports.Redirect = Redirect |