follow-redirects 1.14.9 → 1.15.2
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 +8 -1
- package/index.js +83 -36
- package/package.json +1 -1
package/README.md
CHANGED
@@ -63,10 +63,17 @@ const { http, https } = require('follow-redirects');
|
|
63
63
|
|
64
64
|
const options = url.parse('http://bit.ly/900913');
|
65
65
|
options.maxRedirects = 10;
|
66
|
-
options.beforeRedirect = (options,
|
66
|
+
options.beforeRedirect = (options, response, request) => {
|
67
67
|
// Use this to adjust the request options upon redirecting,
|
68
68
|
// to inspect the latest response headers,
|
69
69
|
// or to cancel the request by throwing an error
|
70
|
+
|
71
|
+
// response.headers = the redirect response headers
|
72
|
+
// response.statusCode = the redirect response code (eg. 301, 307, etc.)
|
73
|
+
|
74
|
+
// request.url = the requested URL that resulted in a redirect
|
75
|
+
// request.headers = the headers in the request that resulted in a redirect
|
76
|
+
// request.method = the method of the request that resulted in a redirect
|
70
77
|
if (options.hostname === "example.com") {
|
71
78
|
options.auth = "user:password";
|
72
79
|
}
|
package/index.js
CHANGED
@@ -15,6 +15,11 @@ events.forEach(function (event) {
|
|
15
15
|
};
|
16
16
|
});
|
17
17
|
|
18
|
+
var InvalidUrlError = createErrorType(
|
19
|
+
"ERR_INVALID_URL",
|
20
|
+
"Invalid URL",
|
21
|
+
TypeError
|
22
|
+
);
|
18
23
|
// Error types with codes
|
19
24
|
var RedirectionError = createErrorType(
|
20
25
|
"ERR_FR_REDIRECTION_FAILURE",
|
@@ -75,10 +80,10 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
|
75
80
|
}
|
76
81
|
|
77
82
|
// Validate input and shift parameters if necessary
|
78
|
-
if (!(
|
83
|
+
if (!isString(data) && !isBuffer(data)) {
|
79
84
|
throw new TypeError("data should be a string, Buffer or Uint8Array");
|
80
85
|
}
|
81
|
-
if (
|
86
|
+
if (isFunction(encoding)) {
|
82
87
|
callback = encoding;
|
83
88
|
encoding = null;
|
84
89
|
}
|
@@ -107,11 +112,11 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
|
107
112
|
// Ends the current native request
|
108
113
|
RedirectableRequest.prototype.end = function (data, encoding, callback) {
|
109
114
|
// Shift parameters if necessary
|
110
|
-
if (
|
115
|
+
if (isFunction(data)) {
|
111
116
|
callback = data;
|
112
117
|
data = encoding = null;
|
113
118
|
}
|
114
|
-
else if (
|
119
|
+
else if (isFunction(encoding)) {
|
115
120
|
callback = encoding;
|
116
121
|
encoding = null;
|
117
122
|
}
|
@@ -270,25 +275,30 @@ RedirectableRequest.prototype._performRequest = function () {
|
|
270
275
|
// If specified, use the agent corresponding to the protocol
|
271
276
|
// (HTTP and HTTPS use different types of agents)
|
272
277
|
if (this._options.agents) {
|
273
|
-
var scheme = protocol.
|
278
|
+
var scheme = protocol.slice(0, -1);
|
274
279
|
this._options.agent = this._options.agents[scheme];
|
275
280
|
}
|
276
281
|
|
277
|
-
// Create the native request
|
282
|
+
// Create the native request and set up its event handlers
|
278
283
|
var request = this._currentRequest =
|
279
284
|
nativeProtocol.request(this._options, this._onNativeResponse);
|
280
|
-
this._currentUrl = url.format(this._options);
|
281
|
-
|
282
|
-
// Set up event handlers
|
283
285
|
request._redirectable = this;
|
284
|
-
for (var
|
285
|
-
request.on(
|
286
|
+
for (var event of events) {
|
287
|
+
request.on(event, eventHandlers[event]);
|
286
288
|
}
|
287
289
|
|
290
|
+
// RFC7230§5.3.1: When making a request directly to an origin server, […]
|
291
|
+
// a client MUST send only the absolute path […] as the request-target.
|
292
|
+
this._currentUrl = /^\//.test(this._options.path) ?
|
293
|
+
url.format(this._options) :
|
294
|
+
// When making a request to a proxy, […]
|
295
|
+
// a client MUST send the target URI in absolute-form […].
|
296
|
+
this._options.path;
|
297
|
+
|
288
298
|
// End a redirected request
|
289
299
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
290
300
|
if (this._isRedirect) {
|
291
|
-
// Write the request entity and end
|
301
|
+
// Write the request entity and end
|
292
302
|
var i = 0;
|
293
303
|
var self = this;
|
294
304
|
var buffers = this._requestBodyBuffers;
|
@@ -362,10 +372,21 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
362
372
|
return;
|
363
373
|
}
|
364
374
|
|
375
|
+
// Store the request headers if applicable
|
376
|
+
var requestHeaders;
|
377
|
+
var beforeRedirect = this._options.beforeRedirect;
|
378
|
+
if (beforeRedirect) {
|
379
|
+
requestHeaders = Object.assign({
|
380
|
+
// The Host header was set by nativeProtocol.request
|
381
|
+
Host: response.req.getHeader("host"),
|
382
|
+
}, this._options.headers);
|
383
|
+
}
|
384
|
+
|
365
385
|
// RFC7231§6.4: Automatic redirection needs to done with
|
366
386
|
// care for methods not known to be safe, […]
|
367
387
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
368
388
|
// the request method from POST to GET for the subsequent request.
|
389
|
+
var method = this._options.method;
|
369
390
|
if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
|
370
391
|
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
371
392
|
// the server is redirecting the user agent to a different resource […]
|
@@ -393,7 +414,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
393
414
|
redirectUrl = url.resolve(currentUrl, location);
|
394
415
|
}
|
395
416
|
catch (cause) {
|
396
|
-
this.emit("error", new RedirectionError(cause));
|
417
|
+
this.emit("error", new RedirectionError({ cause: cause }));
|
397
418
|
return;
|
398
419
|
}
|
399
420
|
|
@@ -413,10 +434,18 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
413
434
|
}
|
414
435
|
|
415
436
|
// Evaluate the beforeRedirect callback
|
416
|
-
if (
|
417
|
-
var responseDetails = {
|
437
|
+
if (isFunction(beforeRedirect)) {
|
438
|
+
var responseDetails = {
|
439
|
+
headers: response.headers,
|
440
|
+
statusCode: statusCode,
|
441
|
+
};
|
442
|
+
var requestDetails = {
|
443
|
+
url: currentUrl,
|
444
|
+
method: method,
|
445
|
+
headers: requestHeaders,
|
446
|
+
};
|
418
447
|
try {
|
419
|
-
|
448
|
+
beforeRedirect(this._options, responseDetails, requestDetails);
|
420
449
|
}
|
421
450
|
catch (err) {
|
422
451
|
this.emit("error", err);
|
@@ -430,7 +459,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
430
459
|
this._performRequest();
|
431
460
|
}
|
432
461
|
catch (cause) {
|
433
|
-
this.emit("error", new RedirectionError(cause));
|
462
|
+
this.emit("error", new RedirectionError({ cause: cause }));
|
434
463
|
}
|
435
464
|
};
|
436
465
|
|
@@ -452,15 +481,19 @@ function wrap(protocols) {
|
|
452
481
|
// Executes a request, following redirects
|
453
482
|
function request(input, options, callback) {
|
454
483
|
// Parse parameters
|
455
|
-
if (
|
456
|
-
var
|
484
|
+
if (isString(input)) {
|
485
|
+
var parsed;
|
457
486
|
try {
|
458
|
-
|
487
|
+
parsed = urlToOptions(new URL(input));
|
459
488
|
}
|
460
489
|
catch (err) {
|
461
490
|
/* istanbul ignore next */
|
462
|
-
|
491
|
+
parsed = url.parse(input);
|
492
|
+
}
|
493
|
+
if (!isString(parsed.protocol)) {
|
494
|
+
throw new InvalidUrlError({ input });
|
463
495
|
}
|
496
|
+
input = parsed;
|
464
497
|
}
|
465
498
|
else if (URL && (input instanceof URL)) {
|
466
499
|
input = urlToOptions(input);
|
@@ -470,7 +503,7 @@ function wrap(protocols) {
|
|
470
503
|
options = input;
|
471
504
|
input = { protocol: protocol };
|
472
505
|
}
|
473
|
-
if (
|
506
|
+
if (isFunction(options)) {
|
474
507
|
callback = options;
|
475
508
|
options = null;
|
476
509
|
}
|
@@ -481,6 +514,9 @@ function wrap(protocols) {
|
|
481
514
|
maxBodyLength: exports.maxBodyLength,
|
482
515
|
}, input, options);
|
483
516
|
options.nativeProtocols = nativeProtocols;
|
517
|
+
if (!isString(options.host) && !isString(options.hostname)) {
|
518
|
+
options.hostname = "::1";
|
519
|
+
}
|
484
520
|
|
485
521
|
assert.equal(options.protocol, protocol, "protocol mismatch");
|
486
522
|
debug("options", options);
|
@@ -538,37 +574,48 @@ function removeMatchingHeaders(regex, headers) {
|
|
538
574
|
undefined : String(lastValue).trim();
|
539
575
|
}
|
540
576
|
|
541
|
-
function createErrorType(code,
|
542
|
-
|
577
|
+
function createErrorType(code, message, baseClass) {
|
578
|
+
// Create constructor
|
579
|
+
function CustomError(properties) {
|
543
580
|
Error.captureStackTrace(this, this.constructor);
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
else {
|
548
|
-
this.message = defaultMessage + ": " + cause.message;
|
549
|
-
this.cause = cause;
|
550
|
-
}
|
581
|
+
Object.assign(this, properties || {});
|
582
|
+
this.code = code;
|
583
|
+
this.message = this.cause ? message + ": " + this.cause.message : message;
|
551
584
|
}
|
552
|
-
|
585
|
+
|
586
|
+
// Attach constructor and set default properties
|
587
|
+
CustomError.prototype = new (baseClass || Error)();
|
553
588
|
CustomError.prototype.constructor = CustomError;
|
554
589
|
CustomError.prototype.name = "Error [" + code + "]";
|
555
|
-
CustomError.prototype.code = code;
|
556
590
|
return CustomError;
|
557
591
|
}
|
558
592
|
|
559
593
|
function abortRequest(request) {
|
560
|
-
for (var
|
561
|
-
request.removeListener(
|
594
|
+
for (var event of events) {
|
595
|
+
request.removeListener(event, eventHandlers[event]);
|
562
596
|
}
|
563
597
|
request.on("error", noop);
|
564
598
|
request.abort();
|
565
599
|
}
|
566
600
|
|
567
601
|
function isSubdomain(subdomain, domain) {
|
568
|
-
|
602
|
+
assert(isString(subdomain) && isString(domain));
|
603
|
+
var dot = subdomain.length - domain.length - 1;
|
569
604
|
return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
|
570
605
|
}
|
571
606
|
|
607
|
+
function isString(value) {
|
608
|
+
return typeof value === "string" || value instanceof String;
|
609
|
+
}
|
610
|
+
|
611
|
+
function isFunction(value) {
|
612
|
+
return typeof value === "function";
|
613
|
+
}
|
614
|
+
|
615
|
+
function isBuffer(value) {
|
616
|
+
return typeof value === "object" && ("length" in value);
|
617
|
+
}
|
618
|
+
|
572
619
|
// Exports
|
573
620
|
module.exports = wrap({ http: http, https: https });
|
574
621
|
module.exports.wrap = wrap;
|