follow-redirects 1.8.0 → 1.10.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 +2 -2
- package/index.js +71 -34
- package/package.json +4 -11
package/README.md
CHANGED
@@ -137,8 +137,8 @@ Pull Requests are always welcome. Please [file an issue](https://github.com/foll
|
|
137
137
|
## Authors
|
138
138
|
|
139
139
|
- [Ruben Verborgh](https://ruben.verborgh.org/)
|
140
|
-
- Olivier Lalonde
|
141
|
-
- James Talmage
|
140
|
+
- [Olivier Lalonde](mailto:olalonde@gmail.com)
|
141
|
+
- [James Talmage](mailto:james@talmage.io)
|
142
142
|
|
143
143
|
## License
|
144
144
|
|
package/index.js
CHANGED
@@ -6,18 +6,32 @@ var assert = require("assert");
|
|
6
6
|
var Writable = require("stream").Writable;
|
7
7
|
var debug = require("debug")("follow-redirects");
|
8
8
|
|
9
|
-
// RFC7231§4.2.1: Of the request methods defined by this specification,
|
10
|
-
// the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe.
|
11
|
-
var SAFE_METHODS = { GET: true, HEAD: true, OPTIONS: true, TRACE: true };
|
12
|
-
|
13
9
|
// Create handlers that pass events from native requests
|
14
10
|
var eventHandlers = Object.create(null);
|
15
|
-
["abort", "aborted", "error", "socket", "timeout"].forEach(function (event) {
|
16
|
-
eventHandlers[event] = function (
|
17
|
-
this._redirectable.emit(event,
|
11
|
+
["abort", "aborted", "connect", "error", "socket", "timeout"].forEach(function (event) {
|
12
|
+
eventHandlers[event] = function (arg1, arg2, arg3) {
|
13
|
+
this._redirectable.emit(event, arg1, arg2, arg3);
|
18
14
|
};
|
19
15
|
});
|
20
16
|
|
17
|
+
// Error types with codes
|
18
|
+
var RedirectionError = createErrorType(
|
19
|
+
"ERR_FR_REDIRECTION_FAILURE",
|
20
|
+
""
|
21
|
+
);
|
22
|
+
var TooManyRedirectsError = createErrorType(
|
23
|
+
"ERR_FR_TOO_MANY_REDIRECTS",
|
24
|
+
"Maximum number of redirects exceeded"
|
25
|
+
);
|
26
|
+
var MaxBodyLengthExceededError = createErrorType(
|
27
|
+
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
|
28
|
+
"Request body larger than maxBodyLength limit"
|
29
|
+
);
|
30
|
+
var WriteAfterEndError = createErrorType(
|
31
|
+
"ERR_STREAM_WRITE_AFTER_END",
|
32
|
+
"write after end"
|
33
|
+
);
|
34
|
+
|
21
35
|
// An HTTP(S) request that can be redirected
|
22
36
|
function RedirectableRequest(options, responseCallback) {
|
23
37
|
// Initialize the request
|
@@ -51,12 +65,12 @@ RedirectableRequest.prototype = Object.create(Writable.prototype);
|
|
51
65
|
RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
52
66
|
// Writing is not allowed if end has been called
|
53
67
|
if (this._ending) {
|
54
|
-
throw new
|
68
|
+
throw new WriteAfterEndError();
|
55
69
|
}
|
56
70
|
|
57
71
|
// Validate input and shift parameters if necessary
|
58
72
|
if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) {
|
59
|
-
throw new
|
73
|
+
throw new TypeError("data should be a string, Buffer or Uint8Array");
|
60
74
|
}
|
61
75
|
if (typeof encoding === "function") {
|
62
76
|
callback = encoding;
|
@@ -79,7 +93,7 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
|
79
93
|
}
|
80
94
|
// Error when we exceed the maximum body length
|
81
95
|
else {
|
82
|
-
this.emit("error", new
|
96
|
+
this.emit("error", new MaxBodyLengthExceededError());
|
83
97
|
this.abort();
|
84
98
|
}
|
85
99
|
};
|
@@ -211,7 +225,7 @@ RedirectableRequest.prototype._performRequest = function () {
|
|
211
225
|
var protocol = this._options.protocol;
|
212
226
|
var nativeProtocol = this._options.nativeProtocols[protocol];
|
213
227
|
if (!nativeProtocol) {
|
214
|
-
this.emit("error", new
|
228
|
+
this.emit("error", new TypeError("Unsupported protocol " + protocol));
|
215
229
|
return;
|
216
230
|
}
|
217
231
|
|
@@ -300,43 +314,38 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
300
314
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
301
315
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
302
316
|
if (++this._redirectCount > this._options.maxRedirects) {
|
303
|
-
this.emit("error", new
|
317
|
+
this.emit("error", new TooManyRedirectsError());
|
304
318
|
return;
|
305
319
|
}
|
306
320
|
|
307
321
|
// RFC7231§6.4: Automatic redirection needs to done with
|
308
|
-
// care for methods not known to be safe […]
|
309
|
-
//
|
310
|
-
//
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
322
|
+
// care for methods not known to be safe, […]
|
323
|
+
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
324
|
+
// the request method from POST to GET for the subsequent request.
|
325
|
+
if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
|
326
|
+
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
327
|
+
// the server is redirecting the user agent to a different resource […]
|
328
|
+
// A user agent can perform a retrieval request targeting that URI
|
329
|
+
// (a GET or HEAD request if using HTTP) […]
|
330
|
+
(statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
|
317
331
|
this._options.method = "GET";
|
318
332
|
// Drop a possible entity and headers related to it
|
319
333
|
this._requestBodyBuffers = [];
|
320
|
-
|
321
|
-
if (/^content-/i.test(header)) {
|
322
|
-
delete headers[header];
|
323
|
-
}
|
324
|
-
}
|
334
|
+
removeMatchingHeaders(/^content-/i, this._options.headers);
|
325
335
|
}
|
326
336
|
|
327
337
|
// Drop the Host header, as the redirect might lead to a different host
|
328
338
|
if (!this._isRedirect) {
|
329
|
-
|
330
|
-
if (/^host$/i.test(header)) {
|
331
|
-
delete headers[header];
|
332
|
-
}
|
333
|
-
}
|
339
|
+
removeMatchingHeaders(/^host$/i, this._options.headers);
|
334
340
|
}
|
335
341
|
|
336
|
-
//
|
342
|
+
// Create the redirected request
|
337
343
|
var redirectUrl = url.resolve(this._currentUrl, location);
|
338
344
|
debug("redirecting to", redirectUrl);
|
345
|
+
this._isRedirect = true;
|
339
346
|
Object.assign(this._options, url.parse(redirectUrl));
|
347
|
+
|
348
|
+
// Evaluate the beforeRedirect callback
|
340
349
|
if (typeof this._options.beforeRedirect === "function") {
|
341
350
|
try {
|
342
351
|
this._options.beforeRedirect.call(null, this._options);
|
@@ -347,8 +356,16 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
347
356
|
}
|
348
357
|
this._sanitizeOptions(this._options);
|
349
358
|
}
|
350
|
-
|
351
|
-
|
359
|
+
|
360
|
+
// Perform the redirected request
|
361
|
+
try {
|
362
|
+
this._performRequest();
|
363
|
+
}
|
364
|
+
catch (cause) {
|
365
|
+
var error = new RedirectionError("Redirected request failed: " + cause.message);
|
366
|
+
error.cause = cause;
|
367
|
+
this.emit("error", error);
|
368
|
+
}
|
352
369
|
}
|
353
370
|
else {
|
354
371
|
// The response is not a redirect; return it as-is
|
@@ -447,6 +464,26 @@ function urlToOptions(urlObject) {
|
|
447
464
|
return options;
|
448
465
|
}
|
449
466
|
|
467
|
+
function removeMatchingHeaders(regex, headers) {
|
468
|
+
for (var header in headers) {
|
469
|
+
if (regex.test(header)) {
|
470
|
+
delete headers[header];
|
471
|
+
}
|
472
|
+
}
|
473
|
+
}
|
474
|
+
|
475
|
+
function createErrorType(code, defaultMessage) {
|
476
|
+
function CustomError(message) {
|
477
|
+
Error.captureStackTrace(this, this.constructor);
|
478
|
+
this.message = message || defaultMessage;
|
479
|
+
}
|
480
|
+
CustomError.prototype = new Error();
|
481
|
+
CustomError.prototype.constructor = CustomError;
|
482
|
+
CustomError.prototype.name = "Error [" + code + "]";
|
483
|
+
CustomError.prototype.code = code;
|
484
|
+
return CustomError;
|
485
|
+
}
|
486
|
+
|
450
487
|
// Exports
|
451
488
|
module.exports = wrap({ http: http, https: https });
|
452
489
|
module.exports.wrap = wrap;
|
package/package.json
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
{
|
2
2
|
"name": "follow-redirects",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.10.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"
|
8
9
|
],
|
9
10
|
"engines": {
|
10
|
-
"node": ">=
|
11
|
+
"node": ">=4.0"
|
11
12
|
},
|
12
13
|
"scripts": {
|
13
14
|
"test": "npm run lint && npm run mocha",
|
@@ -37,22 +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
44
|
"concat-stream": "^2.0.0",
|
44
|
-
"coveralls": "^3.0.3",
|
45
45
|
"eslint": "^5.16.0",
|
46
46
|
"express": "^4.16.4",
|
47
47
|
"lolex": "^3.1.0",
|
48
48
|
"mocha": "^6.0.2",
|
49
49
|
"nyc": "^14.1.1"
|
50
|
-
},
|
51
|
-
"license": "MIT",
|
52
|
-
"nyc": {
|
53
|
-
"reporter": [
|
54
|
-
"lcov",
|
55
|
-
"text"
|
56
|
-
]
|
57
50
|
}
|
58
51
|
}
|