UNPKG

4.46 kBJavaScriptView Raw
1'use strict'
2
3var url = require('url')
4var isUrl = /^https?:/
5
6function Redirect (request) {
7 this.request = request
8 this.followRedirect = true
9 this.followRedirects = true
10 this.followAllRedirects = false
11 this.allowRedirect = function () {return true}
12 this.maxRedirects = 10
13 this.redirects = []
14 this.redirectsFollowed = 0
15 this.removeRefererHeader = false
16}
17
18Redirect.prototype.onRequest = function (options) {
19 var self = this
20
21 if (options.maxRedirects !== undefined) {
22 self.maxRedirects = options.maxRedirects
23 }
24 if (typeof options.followRedirect === 'function') {
25 self.allowRedirect = options.followRedirect
26 }
27 if (options.followRedirect !== undefined) {
28 self.followRedirects = !!options.followRedirect
29 }
30 if (options.followAllRedirects !== undefined) {
31 self.followAllRedirects = options.followAllRedirects
32 }
33 if (self.followRedirects || self.followAllRedirects) {
34 self.redirects = self.redirects || []
35 }
36 if (options.removeRefererHeader !== undefined) {
37 self.removeRefererHeader = options.removeRefererHeader
38 }
39}
40
41Redirect.prototype.redirectTo = function (response) {
42 var self = this
43 , request = self.request
44
45 var redirectTo = null
46 if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) {
47 var location = response.caseless.get('location')
48 request.debug('redirect', location)
49
50 if (self.followAllRedirects) {
51 redirectTo = location
52 } else if (self.followRedirects) {
53 switch (request.method) {
54 case 'PATCH':
55 case 'PUT':
56 case 'POST':
57 case 'DELETE':
58 // Do not follow redirects
59 break
60 default:
61 redirectTo = location
62 break
63 }
64 }
65 } else if (response.statusCode === 401) {
66 var authHeader = request._auth.onResponse(response)
67 if (authHeader) {
68 request.setHeader('authorization', authHeader)
69 redirectTo = request.uri
70 }
71 }
72 return redirectTo
73}
74
75Redirect.prototype.onResponse = function (response) {
76 var self = this
77 , request = self.request
78
79 var redirectTo = self.redirectTo(response)
80 if (!redirectTo || !self.allowRedirect.call(request, response)) {
81 return false
82 }
83
84 request.debug('redirect to', redirectTo)
85
86 // ignore any potential response body. it cannot possibly be useful
87 // to us at this point.
88 // response.resume should be defined, but check anyway before calling. Workaround for browserify.
89 if (response.resume) {
90 response.resume()
91 }
92
93 if (self.redirectsFollowed >= self.maxRedirects) {
94 request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href))
95 return false
96 }
97 self.redirectsFollowed += 1
98
99 if (!isUrl.test(redirectTo)) {
100 redirectTo = url.resolve(request.uri.href, redirectTo)
101 }
102
103 var uriPrev = request.uri
104 request.uri = url.parse(redirectTo)
105
106 // handle the case where we change protocol from https to http or vice versa
107 if (request.uri.protocol !== uriPrev.protocol) {
108 request._updateProtocol()
109 }
110
111 self.redirects.push(
112 { statusCode : response.statusCode
113 , redirectUri: redirectTo
114 }
115 )
116 if (self.followAllRedirects && request.method !== 'HEAD'
117 && response.statusCode !== 401 && response.statusCode !== 307) {
118 request.method = 'GET'
119 }
120 // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215
121 delete request.src
122 delete request.req
123 delete request.agent
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', request.uri.href)
145 }
146
147 request.emit('redirect')
148
149 request.init()
150
151 return true
152}
153
154exports.Redirect = Redirect