redirect.js (4635B)
1 'use strict' 2 3 var url = require('url') 4 var isUrl = /^https?:/ 5 6 function Redirect (request) { 7 this.request = request 8 this.followRedirect = true 9 this.followRedirects = true 10 this.followAllRedirects = false 11 this.followOriginalHttpMethod = false 12 this.allowRedirect = function () { return true } 13 this.maxRedirects = 10 14 this.redirects = [] 15 this.redirectsFollowed = 0 16 this.removeRefererHeader = false 17 } 18 19 Redirect.prototype.onRequest = function (options) { 20 var self = this 21 22 if (options.maxRedirects !== undefined) { 23 self.maxRedirects = options.maxRedirects 24 } 25 if (typeof options.followRedirect === 'function') { 26 self.allowRedirect = options.followRedirect 27 } 28 if (options.followRedirect !== undefined) { 29 self.followRedirects = !!options.followRedirect 30 } 31 if (options.followAllRedirects !== undefined) { 32 self.followAllRedirects = options.followAllRedirects 33 } 34 if (self.followRedirects || self.followAllRedirects) { 35 self.redirects = self.redirects || [] 36 } 37 if (options.removeRefererHeader !== undefined) { 38 self.removeRefererHeader = options.removeRefererHeader 39 } 40 if (options.followOriginalHttpMethod !== undefined) { 41 self.followOriginalHttpMethod = options.followOriginalHttpMethod 42 } 43 } 44 45 Redirect.prototype.redirectTo = function (response) { 46 var self = this 47 var request = self.request 48 49 var redirectTo = null 50 if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) { 51 var location = response.caseless.get('location') 52 request.debug('redirect', location) 53 54 if (self.followAllRedirects) { 55 redirectTo = location 56 } else if (self.followRedirects) { 57 switch (request.method) { 58 case 'PATCH': 59 case 'PUT': 60 case 'POST': 61 case 'DELETE': 62 // Do not follow redirects 63 break 64 default: 65 redirectTo = location 66 break 67 } 68 } 69 } else if (response.statusCode === 401) { 70 var authHeader = request._auth.onResponse(response) 71 if (authHeader) { 72 request.setHeader('authorization', authHeader) 73 redirectTo = request.uri 74 } 75 } 76 return redirectTo 77 } 78 79 Redirect.prototype.onResponse = function (response) { 80 var self = this 81 var request = self.request 82 83 var redirectTo = self.redirectTo(response) 84 if (!redirectTo || !self.allowRedirect.call(request, response)) { 85 return false 86 } 87 88 request.debug('redirect to', redirectTo) 89 90 // ignore any potential response body. it cannot possibly be useful 91 // to us at this point. 92 // response.resume should be defined, but check anyway before calling. Workaround for browserify. 93 if (response.resume) { 94 response.resume() 95 } 96 97 if (self.redirectsFollowed >= self.maxRedirects) { 98 request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href)) 99 return false 100 } 101 self.redirectsFollowed += 1 102 103 if (!isUrl.test(redirectTo)) { 104 redirectTo = url.resolve(request.uri.href, redirectTo) 105 } 106 107 var uriPrev = request.uri 108 request.uri = url.parse(redirectTo) 109 110 // handle the case where we change protocol from https to http or vice versa 111 if (request.uri.protocol !== uriPrev.protocol) { 112 delete request.agent 113 } 114 115 self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo }) 116 117 if (self.followAllRedirects && request.method !== 'HEAD' && 118 response.statusCode !== 401 && response.statusCode !== 307) { 119 request.method = self.followOriginalHttpMethod ? request.method : 'GET' 120 } 121 // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215 122 delete request.src 123 delete request.req 124 delete request._started 125 if (response.statusCode !== 401 && response.statusCode !== 307) { 126 // Remove parameters from the previous response, unless this is the second request 127 // for a server that requires digest authentication. 128 delete request.body 129 delete request._form 130 if (request.headers) { 131 request.removeHeader('host') 132 request.removeHeader('content-type') 133 request.removeHeader('content-length') 134 if (request.uri.hostname !== request.originalHost.split(':')[0]) { 135 // Remove authorization if changing hostnames (but not if just 136 // changing ports or protocols). This matches the behavior of curl: 137 // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710 138 request.removeHeader('authorization') 139 } 140 } 141 } 142 143 if (!self.removeRefererHeader) { 144 request.setHeader('referer', uriPrev.href) 145 } 146 147 request.emit('redirect') 148 149 request.init() 150 151 return true 152 } 153 154 exports.Redirect = Redirect