follow-redirects 1.6.0 → 1.8.1
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 +31 -41
- package/index.js +83 -32
- package/package.json +12 -21
package/README.md
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
## Follow Redirects
|
2
2
|
|
3
|
-
Drop-in replacement for
|
3
|
+
Drop-in replacement for Node's `http` and `https` modules that automatically follows redirects.
|
4
4
|
|
5
5
|
[](https://www.npmjs.com/package/follow-redirects)
|
6
6
|
[](https://travis-ci.org/follow-redirects/follow-redirects)
|
7
7
|
[](https://coveralls.io/r/follow-redirects/follow-redirects?branch=master)
|
8
|
-
[](https://david-dm.org/follow-redirects/follow-redirects)
|
9
8
|
[](https://www.npmjs.com/package/follow-redirects)
|
10
9
|
|
11
10
|
`follow-redirects` provides [request](https://nodejs.org/api/http.html#http_http_request_options_callback) and [get](https://nodejs.org/api/http.html#http_http_get_options_callback)
|
@@ -13,14 +12,13 @@ Drop-in replacement for Nodes `http` and `https` that automatically follows redi
|
|
13
12
|
modules, with the exception that they will seamlessly follow redirects.
|
14
13
|
|
15
14
|
```javascript
|
16
|
-
|
17
|
-
var https = require('follow-redirects').https;
|
15
|
+
const { http, https } = require('follow-redirects');
|
18
16
|
|
19
|
-
http.get('http://bit.ly/900913',
|
20
|
-
response.on('data',
|
17
|
+
http.get('http://bit.ly/900913', response => {
|
18
|
+
response.on('data', chunk => {
|
21
19
|
console.log(chunk);
|
22
20
|
});
|
23
|
-
}).on('error',
|
21
|
+
}).on('error', err => {
|
24
22
|
console.error(err);
|
25
23
|
});
|
26
24
|
```
|
@@ -32,7 +30,7 @@ If no redirection happened, `responseUrl` is the original request URL.
|
|
32
30
|
https.request({
|
33
31
|
host: 'bitly.com',
|
34
32
|
path: '/UHfDGO',
|
35
|
-
},
|
33
|
+
}, response => {
|
36
34
|
console.log(response.responseUrl);
|
37
35
|
// 'http://duckduckgo.com/robots.txt'
|
38
36
|
});
|
@@ -43,7 +41,7 @@ https.request({
|
|
43
41
|
Global options are set directly on the `follow-redirects` module:
|
44
42
|
|
45
43
|
```javascript
|
46
|
-
|
44
|
+
const followRedirects = require('follow-redirects');
|
47
45
|
followRedirects.maxRedirects = 10;
|
48
46
|
followRedirects.maxBodyLength = 20 * 1024 * 1024; // 20 MB
|
49
47
|
```
|
@@ -54,16 +52,22 @@ The following global options are supported:
|
|
54
52
|
|
55
53
|
- `maxBodyLength` (default: 10MB) – sets the maximum size of the request body; if exceeded, an error will be emitted.
|
56
54
|
|
57
|
-
|
58
55
|
### Per-request options
|
59
56
|
Per-request options are set by passing an `options` object:
|
60
57
|
|
61
58
|
```javascript
|
62
|
-
|
63
|
-
|
59
|
+
const url = require('url');
|
60
|
+
const { http, https } = require('follow-redirects');
|
64
61
|
|
65
|
-
|
62
|
+
const options = url.parse('http://bit.ly/900913');
|
66
63
|
options.maxRedirects = 10;
|
64
|
+
options.beforeRedirect = options => {
|
65
|
+
// Use this function to adjust the options upon redirecting,
|
66
|
+
// or to cancel the request by throwing an error
|
67
|
+
if (options.hostname === "example.com") {
|
68
|
+
options.auth = "user:password";
|
69
|
+
}
|
70
|
+
};
|
67
71
|
http.request(options);
|
68
72
|
```
|
69
73
|
|
@@ -75,6 +79,8 @@ the following per-request options are supported:
|
|
75
79
|
|
76
80
|
- `maxBodyLength` (default: 10MB) – sets the maximum size of the request body; if exceeded, an error will be emitted.
|
77
81
|
|
82
|
+
- `beforeRedirect` (default: `undefined`) – optionally change the request `options` on redirects, or abort the request by throwing an error.
|
83
|
+
|
78
84
|
- `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() }`
|
79
85
|
|
80
86
|
- `trackRedirects` (default: `false`) – whether to store the redirected response details into the `redirects` array on the response object.
|
@@ -88,7 +94,7 @@ To enable features such as caching and/or intermediate request tracking,
|
|
88
94
|
you might instead want to wrap `follow-redirects` around custom protocol implementations:
|
89
95
|
|
90
96
|
```javascript
|
91
|
-
|
97
|
+
const { http, https } = require('follow-redirects').wrap({
|
92
98
|
http: require('your-custom-http'),
|
93
99
|
https: require('your-custom-https'),
|
94
100
|
});
|
@@ -96,42 +102,26 @@ var followRedirects = require('follow-redirects').wrap({
|
|
96
102
|
|
97
103
|
Such custom protocols only need an implementation of the `request` method.
|
98
104
|
|
99
|
-
##
|
105
|
+
## Browser Usage
|
100
106
|
|
101
|
-
Due to the way
|
102
|
-
|
103
|
-
platform code for node and the browser, `follow-redirects` provides a great solution for making the native node
|
104
|
-
modules behave the same as they do in browserified builds in the browser. To avoid bundling unnecessary code
|
105
|
-
you should tell browserify to swap out `follow-redirects` with the standard modules when bundling.
|
106
|
-
To make this easier, you need to change how you require the modules:
|
107
|
+
Due to the way the browser works,
|
108
|
+
the `http` and `https` browser equivalents perform redirects by default.
|
107
109
|
|
110
|
+
By requiring `follow-redirects` this way:
|
108
111
|
```javascript
|
109
|
-
|
110
|
-
|
112
|
+
const http = require('follow-redirects/http');
|
113
|
+
const https = require('follow-redirects/https');
|
111
114
|
```
|
115
|
+
you can easily tell webpack and friends to replace
|
116
|
+
`follow-redirect` by the built-in versions:
|
112
117
|
|
113
|
-
|
114
|
-
|
115
|
-
```javascript
|
116
|
-
"browser": {
|
118
|
+
```json
|
119
|
+
{
|
117
120
|
"follow-redirects/http" : "http",
|
118
121
|
"follow-redirects/https" : "https"
|
119
122
|
}
|
120
123
|
```
|
121
124
|
|
122
|
-
The `browserify-http` module has not kept pace with node development, and no long behaves identically to the native
|
123
|
-
module when running in the browser. If you are experiencing problems, you may want to check out
|
124
|
-
[browserify-http-2](https://www.npmjs.com/package/http-browserify-2). It is more actively maintained and
|
125
|
-
attempts to address a few of the shortcomings of `browserify-http`. In that case, your browserify config should
|
126
|
-
look something like this:
|
127
|
-
|
128
|
-
```javascript
|
129
|
-
"browser": {
|
130
|
-
"follow-redirects/http" : "browserify-http-2/http",
|
131
|
-
"follow-redirects/https" : "browserify-http-2/https"
|
132
|
-
}
|
133
|
-
```
|
134
|
-
|
135
125
|
## Contributing
|
136
126
|
|
137
127
|
Pull Requests are always welcome. Please [file an issue](https://github.com/follow-redirects/follow-redirects/issues)
|
@@ -146,9 +136,9 @@ Pull Requests are always welcome. Please [file an issue](https://github.com/foll
|
|
146
136
|
|
147
137
|
## Authors
|
148
138
|
|
139
|
+
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
149
140
|
- Olivier Lalonde (olalonde@gmail.com)
|
150
141
|
- James Talmage (james@talmage.io)
|
151
|
-
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
152
142
|
|
153
143
|
## License
|
154
144
|
|
package/index.js
CHANGED
@@ -22,7 +22,7 @@ var eventHandlers = Object.create(null);
|
|
22
22
|
function RedirectableRequest(options, responseCallback) {
|
23
23
|
// Initialize the request
|
24
24
|
Writable.call(this);
|
25
|
-
|
25
|
+
this._sanitizeOptions(options);
|
26
26
|
this._options = options;
|
27
27
|
this._ended = false;
|
28
28
|
this._ending = false;
|
@@ -31,17 +31,6 @@ function RedirectableRequest(options, responseCallback) {
|
|
31
31
|
this._requestBodyLength = 0;
|
32
32
|
this._requestBodyBuffers = [];
|
33
33
|
|
34
|
-
// Since http.request treats host as an alias of hostname,
|
35
|
-
// but the url module interprets host as hostname plus port,
|
36
|
-
// eliminate the host property to avoid confusion.
|
37
|
-
if (options.host) {
|
38
|
-
// Use hostname if set, because it has precedence
|
39
|
-
if (!options.hostname) {
|
40
|
-
options.hostname = options.host;
|
41
|
-
}
|
42
|
-
delete options.host;
|
43
|
-
}
|
44
|
-
|
45
34
|
// Attach a callback if passed
|
46
35
|
if (responseCallback) {
|
47
36
|
this.on("response", responseCallback);
|
@@ -53,18 +42,6 @@ function RedirectableRequest(options, responseCallback) {
|
|
53
42
|
self._processResponse(response);
|
54
43
|
};
|
55
44
|
|
56
|
-
// Complete the URL object when necessary
|
57
|
-
if (!options.pathname && options.path) {
|
58
|
-
var searchPos = options.path.indexOf("?");
|
59
|
-
if (searchPos < 0) {
|
60
|
-
options.pathname = options.path;
|
61
|
-
}
|
62
|
-
else {
|
63
|
-
options.pathname = options.path.substring(0, searchPos);
|
64
|
-
options.search = options.path.substring(searchPos);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
45
|
// Perform the first request
|
69
46
|
this._performRequest();
|
70
47
|
}
|
@@ -147,10 +124,43 @@ RedirectableRequest.prototype.removeHeader = function (name) {
|
|
147
124
|
this._currentRequest.removeHeader(name);
|
148
125
|
};
|
149
126
|
|
127
|
+
// Global timeout for all underlying requests
|
128
|
+
RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
129
|
+
if (callback) {
|
130
|
+
this.once("timeout", callback);
|
131
|
+
}
|
132
|
+
|
133
|
+
if (this.socket) {
|
134
|
+
startTimer(this, msecs);
|
135
|
+
}
|
136
|
+
else {
|
137
|
+
var self = this;
|
138
|
+
this._currentRequest.once("socket", function () {
|
139
|
+
startTimer(self, msecs);
|
140
|
+
});
|
141
|
+
}
|
142
|
+
|
143
|
+
this.once("response", clearTimer);
|
144
|
+
this.once("error", clearTimer);
|
145
|
+
|
146
|
+
return this;
|
147
|
+
};
|
148
|
+
|
149
|
+
function startTimer(request, msecs) {
|
150
|
+
clearTimeout(request._timeout);
|
151
|
+
request._timeout = setTimeout(function () {
|
152
|
+
request.emit("timeout");
|
153
|
+
}, msecs);
|
154
|
+
}
|
155
|
+
|
156
|
+
function clearTimer() {
|
157
|
+
clearTimeout(this._timeout);
|
158
|
+
}
|
159
|
+
|
150
160
|
// Proxy all other public ClientRequest methods
|
151
161
|
[
|
152
162
|
"abort", "flushHeaders", "getHeader",
|
153
|
-
"setNoDelay", "setSocketKeepAlive",
|
163
|
+
"setNoDelay", "setSocketKeepAlive",
|
154
164
|
].forEach(function (method) {
|
155
165
|
RedirectableRequest.prototype[method] = function (a, b) {
|
156
166
|
return this._currentRequest[method](a, b);
|
@@ -164,6 +174,37 @@ RedirectableRequest.prototype.removeHeader = function (name) {
|
|
164
174
|
});
|
165
175
|
});
|
166
176
|
|
177
|
+
RedirectableRequest.prototype._sanitizeOptions = function (options) {
|
178
|
+
// Ensure headers are always present
|
179
|
+
if (!options.headers) {
|
180
|
+
options.headers = {};
|
181
|
+
}
|
182
|
+
|
183
|
+
// Since http.request treats host as an alias of hostname,
|
184
|
+
// but the url module interprets host as hostname plus port,
|
185
|
+
// eliminate the host property to avoid confusion.
|
186
|
+
if (options.host) {
|
187
|
+
// Use hostname if set, because it has precedence
|
188
|
+
if (!options.hostname) {
|
189
|
+
options.hostname = options.host;
|
190
|
+
}
|
191
|
+
delete options.host;
|
192
|
+
}
|
193
|
+
|
194
|
+
// Complete the URL object when necessary
|
195
|
+
if (!options.pathname && options.path) {
|
196
|
+
var searchPos = options.path.indexOf("?");
|
197
|
+
if (searchPos < 0) {
|
198
|
+
options.pathname = options.path;
|
199
|
+
}
|
200
|
+
else {
|
201
|
+
options.pathname = options.path.substring(0, searchPos);
|
202
|
+
options.search = options.path.substring(searchPos);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
};
|
206
|
+
|
207
|
+
|
167
208
|
// Executes the next native request (initial or redirect)
|
168
209
|
RedirectableRequest.prototype._performRequest = function () {
|
169
210
|
// Load the native protocol
|
@@ -231,11 +272,12 @@ RedirectableRequest.prototype._performRequest = function () {
|
|
231
272
|
// Processes a response from the current native request
|
232
273
|
RedirectableRequest.prototype._processResponse = function (response) {
|
233
274
|
// Store the redirected response
|
275
|
+
var statusCode = response.statusCode;
|
234
276
|
if (this._options.trackRedirects) {
|
235
277
|
this._redirects.push({
|
236
278
|
url: this._currentUrl,
|
237
279
|
headers: response.headers,
|
238
|
-
statusCode:
|
280
|
+
statusCode: statusCode,
|
239
281
|
});
|
240
282
|
}
|
241
283
|
|
@@ -247,11 +289,13 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
247
289
|
// even if the specific status code is not understood.
|
248
290
|
var location = response.headers.location;
|
249
291
|
if (location && this._options.followRedirects !== false &&
|
250
|
-
|
292
|
+
statusCode >= 300 && statusCode < 400) {
|
251
293
|
// Abort the current request
|
252
294
|
this._currentRequest.removeAllListeners();
|
253
295
|
this._currentRequest.on("error", noop);
|
254
296
|
this._currentRequest.abort();
|
297
|
+
// Discard the remainder of the response to avoid waiting for data
|
298
|
+
response.destroy();
|
255
299
|
|
256
300
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
257
301
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
@@ -269,7 +313,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
269
313
|
// if it performs an automatic redirection to that URI.
|
270
314
|
var header;
|
271
315
|
var headers = this._options.headers;
|
272
|
-
if (
|
316
|
+
if (statusCode !== 307 && !(this._options.method in SAFE_METHODS)) {
|
273
317
|
this._options.method = "GET";
|
274
318
|
// Drop a possible entity and headers related to it
|
275
319
|
this._requestBodyBuffers = [];
|
@@ -293,11 +337,18 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
293
337
|
var redirectUrl = url.resolve(this._currentUrl, location);
|
294
338
|
debug("redirecting to", redirectUrl);
|
295
339
|
Object.assign(this._options, url.parse(redirectUrl));
|
340
|
+
if (typeof this._options.beforeRedirect === "function") {
|
341
|
+
try {
|
342
|
+
this._options.beforeRedirect.call(null, this._options);
|
343
|
+
}
|
344
|
+
catch (err) {
|
345
|
+
this.emit("error", err);
|
346
|
+
return;
|
347
|
+
}
|
348
|
+
this._sanitizeOptions(this._options);
|
349
|
+
}
|
296
350
|
this._isRedirect = true;
|
297
351
|
this._performRequest();
|
298
|
-
|
299
|
-
// Discard the remainder of the response to avoid waiting for data
|
300
|
-
response.destroy();
|
301
352
|
}
|
302
353
|
else {
|
303
354
|
// The response is not a redirect; return it as-is
|
@@ -387,7 +438,7 @@ function urlToOptions(urlObject) {
|
|
387
438
|
hash: urlObject.hash,
|
388
439
|
search: urlObject.search,
|
389
440
|
pathname: urlObject.pathname,
|
390
|
-
path:
|
441
|
+
path: urlObject.pathname + urlObject.search,
|
391
442
|
href: urlObject.href,
|
392
443
|
};
|
393
444
|
if (urlObject.port !== "") {
|
package/package.json
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "follow-redirects",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.8.1",
|
4
4
|
"description": "HTTP and HTTPS modules that follow redirects.",
|
5
|
+
"license": "MIT",
|
5
6
|
"main": "index.js",
|
7
|
+
"files": [
|
8
|
+
"*.js"
|
9
|
+
],
|
6
10
|
"engines": {
|
7
11
|
"node": ">=4.0"
|
8
12
|
},
|
@@ -33,28 +37,15 @@
|
|
33
37
|
"Olivier Lalonde <olalonde@gmail.com> (http://www.syskall.com)",
|
34
38
|
"James Talmage <james@talmage.io>"
|
35
39
|
],
|
36
|
-
"files": [
|
37
|
-
"index.js",
|
38
|
-
"create.js",
|
39
|
-
"http.js",
|
40
|
-
"https.js"
|
41
|
-
],
|
42
40
|
"dependencies": {
|
43
|
-
"debug": "
|
41
|
+
"debug": "^3.0.0"
|
44
42
|
},
|
45
43
|
"devDependencies": {
|
46
|
-
"concat-stream": "^
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"mocha": "^
|
51
|
-
"nyc": "^
|
52
|
-
},
|
53
|
-
"license": "MIT",
|
54
|
-
"nyc": {
|
55
|
-
"reporter": [
|
56
|
-
"lcov",
|
57
|
-
"text"
|
58
|
-
]
|
44
|
+
"concat-stream": "^2.0.0",
|
45
|
+
"eslint": "^5.16.0",
|
46
|
+
"express": "^4.16.4",
|
47
|
+
"lolex": "^3.1.0",
|
48
|
+
"mocha": "^6.0.2",
|
49
|
+
"nyc": "^14.1.1"
|
59
50
|
}
|
60
51
|
}
|