follow-redirects 1.6.1 → 1.9.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 +23 -17
- package/index.js +85 -34
- package/package.json +9 -15
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
|
});
|
@@ -103,8 +109,8 @@ the `http` and `https` browser equivalents perform redirects by default.
|
|
103
109
|
|
104
110
|
By requiring `follow-redirects` this way:
|
105
111
|
```javascript
|
106
|
-
|
107
|
-
|
112
|
+
const http = require('follow-redirects/http');
|
113
|
+
const https = require('follow-redirects/https');
|
108
114
|
```
|
109
115
|
you can easily tell webpack and friends to replace
|
110
116
|
`follow-redirect` by the built-in versions:
|
@@ -130,9 +136,9 @@ Pull Requests are always welcome. Please [file an issue](https://github.com/foll
|
|
130
136
|
|
131
137
|
## Authors
|
132
138
|
|
139
|
+
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
133
140
|
- Olivier Lalonde (olalonde@gmail.com)
|
134
141
|
- James Talmage (james@talmage.io)
|
135
|
-
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
136
142
|
|
137
143
|
## License
|
138
144
|
|
package/index.js
CHANGED
@@ -12,9 +12,9 @@ var SAFE_METHODS = { GET: true, HEAD: true, OPTIONS: true, TRACE: true };
|
|
12
12
|
|
13
13
|
// Create handlers that pass events from native requests
|
14
14
|
var eventHandlers = Object.create(null);
|
15
|
-
["abort", "aborted", "error", "socket", "timeout"].forEach(function (event) {
|
16
|
-
eventHandlers[event] = function (
|
17
|
-
this._redirectable.emit(event,
|
15
|
+
["abort", "aborted", "connect", "error", "socket", "timeout"].forEach(function (event) {
|
16
|
+
eventHandlers[event] = function (arg1, arg2, arg3) {
|
17
|
+
this._redirectable.emit(event, arg1, arg2, arg3);
|
18
18
|
};
|
19
19
|
});
|
20
20
|
|
@@ -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
|
package/package.json
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
{
|
2
2
|
"name": "follow-redirects",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.9.0",
|
4
4
|
"description": "HTTP and HTTPS modules that follow redirects.",
|
5
|
+
"license": "MIT",
|
5
6
|
"main": "index.js",
|
6
7
|
"files": [
|
7
8
|
"*.js"
|
@@ -37,21 +38,14 @@
|
|
37
38
|
"James Talmage <james@talmage.io>"
|
38
39
|
],
|
39
40
|
"dependencies": {
|
40
|
-
"debug": "
|
41
|
+
"debug": "^3.0.0"
|
41
42
|
},
|
42
43
|
"devDependencies": {
|
43
|
-
"concat-stream": "^
|
44
|
-
"
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"mocha": "^
|
48
|
-
"nyc": "^
|
49
|
-
},
|
50
|
-
"license": "MIT",
|
51
|
-
"nyc": {
|
52
|
-
"reporter": [
|
53
|
-
"lcov",
|
54
|
-
"text"
|
55
|
-
]
|
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"
|
56
50
|
}
|
57
51
|
}
|