follow-redirects 0.0.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of follow-redirects might be problematic. Click here for more details.
- package/README.md +45 -22
- package/index.js +184 -3
- package/package.json +21 -16
- package/create.js +0 -162
package/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
## Follow Redirects
|
2
2
|
|
3
|
-
Drop
|
3
|
+
Drop-in replacement for Nodes `http` and `https` that automatically follows redirects.
|
4
4
|
|
5
|
+
[](https://www.npmjs.com/package/follow-redirects)
|
5
6
|
[](https://travis-ci.org/olalonde/follow-redirects)
|
6
7
|
[](https://coveralls.io/r/olalonde/follow-redirects?branch=master)
|
7
8
|
[](https://codeclimate.com/github/olalonde/follow-redirects)
|
@@ -18,8 +19,8 @@ Drop in replacement for Nodes `http` and `https` that automatically follows redi
|
|
18
19
|
var http = require('follow-redirects').http;
|
19
20
|
var https = require('follow-redirects').https;
|
20
21
|
|
21
|
-
http.get('http://bit.ly/900913', function (
|
22
|
-
|
22
|
+
http.get('http://bit.ly/900913', function (response) {
|
23
|
+
response.on('data', function (chunk) {
|
23
24
|
console.log(chunk);
|
24
25
|
});
|
25
26
|
}).on('error', function (err) {
|
@@ -27,36 +28,58 @@ http.get('http://bit.ly/900913', function (res) {
|
|
27
28
|
});
|
28
29
|
```
|
29
30
|
|
30
|
-
|
31
|
+
You can inspect the final redirected URL through the `responseUrl` property on the `response`.
|
32
|
+
If no redirection happened, `responseUrl` is the original request URL.
|
31
33
|
|
32
34
|
```javascript
|
33
|
-
require('follow-redirects').maxRedirects = 10; // Has global affect (be careful!)
|
34
|
-
|
35
35
|
https.request({
|
36
36
|
host: 'bitly.com',
|
37
37
|
path: '/UHfDGO',
|
38
|
-
|
39
|
-
|
38
|
+
}, function (response) {
|
39
|
+
console.log(response.responseUrl);
|
40
|
+
// 'http://duckduckgo.com/robots.txt'
|
41
|
+
});
|
40
42
|
```
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
## Options
|
45
|
+
### Global options
|
46
|
+
Global options are set directly on the `follow-redirects` module:
|
45
47
|
|
46
48
|
```javascript
|
47
|
-
|
48
|
-
|
49
|
-
path: '/UHfDGO',
|
50
|
-
}, function (res) {
|
51
|
-
console.log(res.fetchedUrls);
|
52
|
-
// [ 'http://duckduckgo.com/robots.txt', 'http://bitly.com/UHfDGO' ]
|
53
|
-
});
|
49
|
+
var followRedirects = require('follow-redirects');
|
50
|
+
followRedirects.maxRedirects = 10;
|
54
51
|
```
|
55
52
|
|
53
|
+
The following global options are supported:
|
54
|
+
|
55
|
+
- `maxRedirects` (default: `21`) – sets the maximum number of allowed redirects; if exceeded, an error will be emitted.
|
56
|
+
|
57
|
+
|
58
|
+
### Per-request options
|
59
|
+
Per-request options are set by passing an `options` object:
|
60
|
+
|
61
|
+
```javascript
|
62
|
+
var url = require('url');
|
63
|
+
var followRedirects = require('follow-redirects');
|
64
|
+
|
65
|
+
var options = url.parse('http://bit.ly/900913');
|
66
|
+
options.maxRedirects = 10;
|
67
|
+
http.request(options);
|
68
|
+
```
|
69
|
+
|
70
|
+
In addition to the [standard HTTP](https://nodejs.org/api/http.html#http_http_request_options_callback) and [HTTPS options](https://nodejs.org/api/https.html#https_https_request_options_callback),
|
71
|
+
the following per-request options are supported:
|
72
|
+
- `followRedirects` (default: `true`) – whether redirects should be followed.
|
73
|
+
|
74
|
+
- `maxRedirects` (default: `21`) – sets the maximum number of allowed redirects; if exceeded, an error will be emitted.
|
75
|
+
|
76
|
+
- `agents` (default: `undefined`) – sets the `agent` option per protocol, since HTTP and HTTPS use different agents. Example value: `{ http: new http.Agent(), https: new https.Agent() }`
|
77
|
+
|
78
|
+
|
56
79
|
## Browserify Usage
|
57
80
|
|
58
81
|
Due to the way `XMLHttpRequest` works, the `browserify` versions of `http` and `https` already follow redirects.
|
59
|
-
If you are *only*
|
82
|
+
If you are *only* targeting the browser, then this library has little value for you. If you want to write cross
|
60
83
|
platform code for node and the browser, `follow-redirects` provides a great solution for making the native node
|
61
84
|
modules behave the same as they do in browserified builds in the browser. To avoid bundling unnecessary code
|
62
85
|
you should tell browserify to swap out `follow-redirects` with the standard modules when bundling.
|
@@ -103,9 +126,9 @@ Pull Requests are always welcome. Please [file an issue](https://github.com/olal
|
|
103
126
|
|
104
127
|
## Authors
|
105
128
|
|
106
|
-
Olivier Lalonde (olalonde@gmail.com)
|
107
|
-
|
108
|
-
|
129
|
+
- Olivier Lalonde (olalonde@gmail.com)
|
130
|
+
- James Talmage (james@talmage.io)
|
131
|
+
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
109
132
|
|
110
133
|
## License
|
111
134
|
|
package/index.js
CHANGED
@@ -1,4 +1,185 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
'use strict';
|
2
|
+
var url = require('url');
|
3
|
+
var assert = require('assert');
|
4
|
+
var http = require('http');
|
5
|
+
var https = require('https');
|
6
|
+
var Writable = require('stream').Writable;
|
7
|
+
var debug = require('debug')('follow-redirects');
|
8
|
+
|
9
|
+
var nativeProtocols = {'http:': http, 'https:': https};
|
10
|
+
var schemes = {};
|
11
|
+
var exports = module.exports = {
|
12
|
+
maxRedirects: 21
|
13
|
+
};
|
14
|
+
// RFC7231§4.2.1: Of the request methods defined by this specification,
|
15
|
+
// the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe.
|
16
|
+
var safeMethods = {GET: true, HEAD: true, OPTIONS: true, TRACE: true};
|
17
|
+
|
18
|
+
// Create handlers that pass events from native requests
|
19
|
+
var eventHandlers = Object.create(null);
|
20
|
+
['abort', 'aborted', 'error'].forEach(function (event) {
|
21
|
+
eventHandlers[event] = function (arg) {
|
22
|
+
this._redirectable.emit(event, arg);
|
23
|
+
};
|
24
|
+
});
|
25
|
+
|
26
|
+
// An HTTP(S) request that can be redirected
|
27
|
+
function RedirectableRequest(options, responseCallback) {
|
28
|
+
// Initialize the request
|
29
|
+
Writable.call(this);
|
30
|
+
this._options = options;
|
31
|
+
this._redirectCount = 0;
|
32
|
+
|
33
|
+
// Attach a callback if passed
|
34
|
+
if (responseCallback) {
|
35
|
+
this.on('response', responseCallback);
|
36
|
+
}
|
37
|
+
|
38
|
+
// React to responses of native requests
|
39
|
+
var self = this;
|
40
|
+
this._onNativeResponse = function (response) {
|
41
|
+
self._processResponse(response);
|
42
|
+
};
|
43
|
+
|
44
|
+
// Perform the first request
|
45
|
+
this._performRequest();
|
46
|
+
}
|
47
|
+
RedirectableRequest.prototype = Object.create(Writable.prototype);
|
48
|
+
|
49
|
+
// Executes the next native request (initial or redirect)
|
50
|
+
RedirectableRequest.prototype._performRequest = function () {
|
51
|
+
// If specified, use the agent corresponding to the protocol
|
52
|
+
// (HTTP and HTTPS use different types of agents)
|
53
|
+
var protocol = this._options.protocol;
|
54
|
+
if (this._options.agents) {
|
55
|
+
this._options.agent = this._options.agents[schemes[protocol]];
|
56
|
+
}
|
57
|
+
|
58
|
+
// Create the native request
|
59
|
+
var nativeProtocol = nativeProtocols[this._options.protocol];
|
60
|
+
var request = this._currentRequest =
|
61
|
+
nativeProtocol.request(this._options, this._onNativeResponse);
|
62
|
+
this._currentUrl = url.format(this._options);
|
63
|
+
|
64
|
+
// Set up event handlers
|
65
|
+
request._redirectable = this;
|
66
|
+
for (var event in eventHandlers) {
|
67
|
+
if (event) {
|
68
|
+
request.on(event, eventHandlers[event]);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
// The first request is explicitly ended in RedirectableRequest#end
|
73
|
+
if (this._currentResponse) {
|
74
|
+
request.end();
|
75
|
+
}
|
76
|
+
};
|
77
|
+
|
78
|
+
// Processes a response from the current native request
|
79
|
+
RedirectableRequest.prototype._processResponse = function (response) {
|
80
|
+
// RFC7231§6.4: The 3xx (Redirection) class of status code indicates
|
81
|
+
// that further action needs to be taken by the user agent in order to
|
82
|
+
// fulfill the request. If a Location header field is provided,
|
83
|
+
// the user agent MAY automatically redirect its request to the URI
|
84
|
+
// referenced by the Location field value,
|
85
|
+
// even if the specific status code is not understood.
|
86
|
+
var location = response.headers.location;
|
87
|
+
if (location && this._options.followRedirects !== false &&
|
88
|
+
response.statusCode >= 300 && response.statusCode < 400) {
|
89
|
+
// RFC7231§6.4: A client SHOULD detect and intervene
|
90
|
+
// in cyclical redirections (i.e., "infinite" redirection loops).
|
91
|
+
if (++this._redirectCount > this._options.maxRedirects) {
|
92
|
+
return this.emit('error', new Error('Max redirects exceeded.'));
|
93
|
+
}
|
94
|
+
|
95
|
+
// RFC7231§6.4.7: The 307 (Temporary Redirect) status code indicates
|
96
|
+
// that the target resource resides temporarily under a different URI
|
97
|
+
// and the user agent MUST NOT change the request method
|
98
|
+
// if it performs an automatic redirection to that URI.
|
99
|
+
if (response.statusCode !== 307) {
|
100
|
+
// RFC7231§6.4: Automatic redirection needs to done with
|
101
|
+
// care for methods not known to be safe […],
|
102
|
+
// since the user might not wish to redirect an unsafe request.
|
103
|
+
if (!(this._options.method in safeMethods)) {
|
104
|
+
this._options.method = 'GET';
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
// Perform the redirected request
|
109
|
+
var redirectUrl = url.resolve(this._currentUrl, location);
|
110
|
+
debug('redirecting to', redirectUrl);
|
111
|
+
Object.assign(this._options, url.parse(redirectUrl));
|
112
|
+
this._currentResponse = response;
|
113
|
+
this._performRequest();
|
114
|
+
} else {
|
115
|
+
// The response is not a redirect; return it as-is
|
116
|
+
response.responseUrl = this._currentUrl;
|
117
|
+
return this.emit('response', response);
|
118
|
+
}
|
119
|
+
};
|
120
|
+
|
121
|
+
// Aborts the current native request
|
122
|
+
RedirectableRequest.prototype.abort = function () {
|
123
|
+
this._currentRequest.abort();
|
124
|
+
};
|
125
|
+
|
126
|
+
// Ends the current native request
|
127
|
+
RedirectableRequest.prototype.end = function (data, encoding, callback) {
|
128
|
+
this._currentRequest.end(data, encoding, callback);
|
129
|
+
};
|
130
|
+
|
131
|
+
// Flushes the headers of the current native request
|
132
|
+
RedirectableRequest.prototype.flushHeaders = function () {
|
133
|
+
this._currentRequest.flushHeaders();
|
134
|
+
};
|
135
|
+
|
136
|
+
// Sets the noDelay option of the current native request
|
137
|
+
RedirectableRequest.prototype.setNoDelay = function (noDelay) {
|
138
|
+
this._currentRequest.setNoDelay(noDelay);
|
139
|
+
};
|
140
|
+
|
141
|
+
// Sets the socketKeepAlive option of the current native request
|
142
|
+
RedirectableRequest.prototype.setSocketKeepAlive = function (enable, initialDelay) {
|
143
|
+
this._currentRequest.setSocketKeepAlive(enable, initialDelay);
|
144
|
+
};
|
145
|
+
|
146
|
+
// Sets the timeout option of the current native request
|
147
|
+
RedirectableRequest.prototype.setTimeout = function (timeout, callback) {
|
148
|
+
this._currentRequest.setTimeout(timeout, callback);
|
149
|
+
};
|
150
|
+
|
151
|
+
// Writes buffered data to the current native request
|
152
|
+
RedirectableRequest.prototype._write = function (chunk, encoding, callback) {
|
153
|
+
this._currentRequest.write(chunk, encoding, callback);
|
154
|
+
};
|
155
|
+
|
156
|
+
// Export a redirecting wrapper for each native protocol
|
157
|
+
Object.keys(nativeProtocols).forEach(function (protocol) {
|
158
|
+
var scheme = schemes[protocol] = protocol.substr(0, protocol.length - 1);
|
159
|
+
var nativeProtocol = nativeProtocols[protocol];
|
160
|
+
var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
|
161
|
+
|
162
|
+
// Executes an HTTP request, following redirects
|
163
|
+
wrappedProtocol.request = function (options, callback) {
|
164
|
+
if (typeof options === 'string') {
|
165
|
+
options = url.parse(options);
|
166
|
+
options.maxRedirects = exports.maxRedirects;
|
167
|
+
} else {
|
168
|
+
options = Object.assign({
|
169
|
+
maxRedirects: exports.maxRedirects,
|
170
|
+
protocol: protocol
|
171
|
+
}, options);
|
172
|
+
}
|
173
|
+
assert.equal(options.protocol, protocol, 'protocol mismatch');
|
174
|
+
debug('options', options);
|
175
|
+
|
176
|
+
return new RedirectableRequest(options, callback);
|
177
|
+
};
|
178
|
+
|
179
|
+
// Executes a GET request, following redirects
|
180
|
+
wrappedProtocol.get = function (options, callback) {
|
181
|
+
var request = wrappedProtocol.request(options, callback);
|
182
|
+
request.end();
|
183
|
+
return request;
|
184
|
+
};
|
4
185
|
});
|
package/package.json
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"name": "follow-redirects",
|
3
|
-
"version": "0.0
|
3
|
+
"version": "1.0.0",
|
4
4
|
"description": "HTTP and HTTPS modules that follow redirects.",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
7
|
-
"test": "
|
8
|
-
"lint": "jshint *.js test/*.js test/**/*.js",
|
9
|
-
"style": "jscs *.js && jscs test/*.js test/**/*.js --config=test/.jscsrc",
|
10
|
-
"cover": "BLUEBIRD_DEBUG=1 istanbul cover ./node_modules/.bin/_mocha",
|
11
|
-
"debug": "BLUEBIRD_DEBUG=1 mocha"
|
7
|
+
"test": "xo && BLUEBIRD_DEBUG=1 nyc mocha"
|
12
8
|
},
|
13
9
|
"repository": {
|
14
10
|
"type": "git",
|
@@ -33,7 +29,8 @@
|
|
33
29
|
"url": "http://www.syskall.com"
|
34
30
|
},
|
35
31
|
"contributors": [
|
36
|
-
"James Talmage <james@talmage.io>"
|
32
|
+
"James Talmage <james@talmage.io>",
|
33
|
+
"Ruben Verborgh <ruben@verborgh.org> (https://ruben.verborgh.org/)"
|
37
34
|
],
|
38
35
|
"files": [
|
39
36
|
"index.js",
|
@@ -42,19 +39,27 @@
|
|
42
39
|
"https.js"
|
43
40
|
],
|
44
41
|
"dependencies": {
|
45
|
-
"debug": "^2.2.0"
|
46
|
-
"stream-consume": "^0.1.0"
|
42
|
+
"debug": "^2.2.0"
|
47
43
|
},
|
48
44
|
"devDependencies": {
|
49
|
-
"bluebird": "^
|
45
|
+
"bluebird": "^3.4.0",
|
50
46
|
"concat-stream": "^1.5.0",
|
51
47
|
"coveralls": "^2.11.2",
|
52
48
|
"express": "^4.13.0",
|
53
|
-
"
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"mocha": "^2.2.5",
|
57
|
-
"semver": "~4.3.6"
|
49
|
+
"mocha": "^3.1.2",
|
50
|
+
"nyc": "^8.3.1",
|
51
|
+
"xo": "^0.17.0"
|
58
52
|
},
|
59
|
-
"license": "MIT"
|
53
|
+
"license": "MIT",
|
54
|
+
"nyc": {
|
55
|
+
"reporter": [
|
56
|
+
"lcov",
|
57
|
+
"text"
|
58
|
+
]
|
59
|
+
},
|
60
|
+
"xo": {
|
61
|
+
"envs": [
|
62
|
+
"mocha"
|
63
|
+
]
|
64
|
+
}
|
60
65
|
}
|
package/create.js
DELETED
@@ -1,162 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
var url = require('url');
|
3
|
-
var debug = require('debug')('follow-redirects');
|
4
|
-
var assert = require('assert');
|
5
|
-
var consume = require('stream-consume');
|
6
|
-
|
7
|
-
module.exports = function(_nativeProtocols) {
|
8
|
-
var nativeProtocols = {};
|
9
|
-
|
10
|
-
var publicApi = {
|
11
|
-
maxRedirects: 5
|
12
|
-
};
|
13
|
-
|
14
|
-
for (var p in _nativeProtocols) {
|
15
|
-
/* istanbul ignore else */
|
16
|
-
if (_nativeProtocols.hasOwnProperty(p)) {
|
17
|
-
// http://www.ietf.org/rfc/rfc2396.txt - Section 3.1
|
18
|
-
assert(/^[A-Z][A-Z\+\-\.]*$/i.test(p), JSON.stringify(p) + ' is not a valid scheme name');
|
19
|
-
generateWrapper(p, _nativeProtocols[p]);
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
return publicApi;
|
24
|
-
|
25
|
-
function execute(options) {
|
26
|
-
var clientRequest;
|
27
|
-
var fetchedUrls = [];
|
28
|
-
|
29
|
-
return (clientRequest = cb());
|
30
|
-
|
31
|
-
function cb(res) {
|
32
|
-
// skip the redirection logic on the first call.
|
33
|
-
if (res) {
|
34
|
-
var fetchedUrl = url.format(options);
|
35
|
-
fetchedUrls.unshift(fetchedUrl);
|
36
|
-
|
37
|
-
if (!isRedirect(res)) {
|
38
|
-
res.fetchedUrls = fetchedUrls;
|
39
|
-
return options.userCallback(res);
|
40
|
-
}
|
41
|
-
|
42
|
-
// we are going to follow the redirect, but in node 0.10 we must first attach a data listener
|
43
|
-
// to consume the stream and send the 'end' event
|
44
|
-
consume(res);
|
45
|
-
|
46
|
-
// need to use url.resolve() in case location is a relative URL
|
47
|
-
var redirectUrl = url.resolve(fetchedUrl, res.headers.location);
|
48
|
-
debug('redirecting to', redirectUrl);
|
49
|
-
|
50
|
-
// clean all the properties related to the old url away, and copy from the redirect url
|
51
|
-
wipeUrlProps(options);
|
52
|
-
extend(options, url.parse(redirectUrl));
|
53
|
-
}
|
54
|
-
|
55
|
-
if (fetchedUrls.length > options.maxRedirects) {
|
56
|
-
var err = new Error('Max redirects exceeded.');
|
57
|
-
return forwardError(err);
|
58
|
-
}
|
59
|
-
|
60
|
-
options.nativeProtocol = nativeProtocols[options.protocol];
|
61
|
-
options.defaultRequest = defaultMakeRequest;
|
62
|
-
|
63
|
-
var req = (options.makeRequest || defaultMakeRequest)(options, cb, res);
|
64
|
-
|
65
|
-
if (res) {
|
66
|
-
req.on('error', forwardError);
|
67
|
-
}
|
68
|
-
return req;
|
69
|
-
}
|
70
|
-
|
71
|
-
function defaultMakeRequest(options, cb, res) {
|
72
|
-
if (res) {
|
73
|
-
// This is a redirect, so use only GET methods
|
74
|
-
options.method = 'GET';
|
75
|
-
}
|
76
|
-
|
77
|
-
var req = options.nativeProtocol.request(options, cb);
|
78
|
-
|
79
|
-
if (res) {
|
80
|
-
// We leave the user to call `end` on the first request
|
81
|
-
req.end();
|
82
|
-
}
|
83
|
-
|
84
|
-
return req;
|
85
|
-
}
|
86
|
-
|
87
|
-
// bubble errors that occur on the redirect back up to the initiating client request
|
88
|
-
// object, otherwise they wind up killing the process.
|
89
|
-
function forwardError (err) {
|
90
|
-
clientRequest.emit('error', err);
|
91
|
-
}
|
92
|
-
}
|
93
|
-
|
94
|
-
function generateWrapper (scheme, nativeProtocol) {
|
95
|
-
var wrappedProtocol = scheme + ':';
|
96
|
-
var H = function() {};
|
97
|
-
H.prototype = nativeProtocols[wrappedProtocol] = nativeProtocol;
|
98
|
-
H = new H();
|
99
|
-
publicApi[scheme] = H;
|
100
|
-
|
101
|
-
H.request = function(options, callback) {
|
102
|
-
return execute(parseOptions(options, callback, wrappedProtocol));
|
103
|
-
};
|
104
|
-
|
105
|
-
// see https://github.com/joyent/node/blob/master/lib/http.js#L1623
|
106
|
-
H.get = function(options, callback) {
|
107
|
-
options = parseOptions(options, callback, wrappedProtocol);
|
108
|
-
var req = execute(options);
|
109
|
-
req.end();
|
110
|
-
return req;
|
111
|
-
};
|
112
|
-
}
|
113
|
-
|
114
|
-
// returns a safe copy of options (or a parsed url object if options was a string).
|
115
|
-
// validates that the supplied callback is a function
|
116
|
-
function parseOptions (options, callback, wrappedProtocol) {
|
117
|
-
assert.equal(typeof callback, 'function', 'callback must be a function');
|
118
|
-
if ('string' === typeof options) {
|
119
|
-
options = url.parse(options);
|
120
|
-
options.maxRedirects = publicApi.maxRedirects;
|
121
|
-
} else {
|
122
|
-
options = extend({
|
123
|
-
maxRedirects: publicApi.maxRedirects,
|
124
|
-
protocol: wrappedProtocol
|
125
|
-
}, options);
|
126
|
-
}
|
127
|
-
assert.equal(options.protocol, wrappedProtocol, 'protocol mismatch');
|
128
|
-
options.protocol = wrappedProtocol;
|
129
|
-
options.userCallback = callback;
|
130
|
-
|
131
|
-
debug('options', options);
|
132
|
-
return options;
|
133
|
-
}
|
134
|
-
};
|
135
|
-
|
136
|
-
// copies source's own properties onto destination and returns destination
|
137
|
-
function extend(destination, source) {
|
138
|
-
for (var i in source) {
|
139
|
-
if (source.hasOwnProperty(i)) {
|
140
|
-
destination[i] = source[i];
|
141
|
-
}
|
142
|
-
}
|
143
|
-
return destination;
|
144
|
-
}
|
145
|
-
|
146
|
-
// to redirect the result must have
|
147
|
-
// a statusCode between 300-399
|
148
|
-
// and a `Location` header
|
149
|
-
function isRedirect (res) {
|
150
|
-
return (res.statusCode >= 300 && res.statusCode <= 399 &&
|
151
|
-
'location' in res.headers);
|
152
|
-
}
|
153
|
-
|
154
|
-
// nulls all url related properties on the object.
|
155
|
-
// required on node <10
|
156
|
-
function wipeUrlProps(options) {
|
157
|
-
for (var i = 0, l = urlProps.length; i < l; ++i) {
|
158
|
-
options[urlProps[i]] = null;
|
159
|
-
}
|
160
|
-
}
|
161
|
-
var urlProps = ['protocol', 'slashes', 'auth', 'host', 'port', 'hostname',
|
162
|
-
'hash', 'search', 'query', 'pathname', 'path', 'href'];
|