follow-redirects 1.14.5 → 1.14.9

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.

Files changed (2) hide show
  1. package/index.js +85 -79
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -336,96 +336,101 @@ RedirectableRequest.prototype._processResponse = function (response) {
336
336
  // the user agent MAY automatically redirect its request to the URI
337
337
  // referenced by the Location field value,
338
338
  // even if the specific status code is not understood.
339
+
340
+ // If the response is not a redirect; return it as-is
339
341
  var location = response.headers.location;
340
- if (location && this._options.followRedirects !== false &&
341
- statusCode >= 300 && statusCode < 400) {
342
- // Abort the current request
343
- abortRequest(this._currentRequest);
344
- // Discard the remainder of the response to avoid waiting for data
345
- response.destroy();
346
-
347
- // RFC7231§6.4: A client SHOULD detect and intervene
348
- // in cyclical redirections (i.e., "infinite" redirection loops).
349
- if (++this._redirectCount > this._options.maxRedirects) {
350
- this.emit("error", new TooManyRedirectsError());
351
- return;
352
- }
342
+ if (!location || this._options.followRedirects === false ||
343
+ statusCode < 300 || statusCode >= 400) {
344
+ response.responseUrl = this._currentUrl;
345
+ response.redirects = this._redirects;
346
+ this.emit("response", response);
353
347
 
354
- // RFC7231§6.4: Automatic redirection needs to done with
355
- // care for methods not known to be safe, []
356
- // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
357
- // the request method from POST to GET for the subsequent request.
358
- if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
359
- // RFC7231§6.4.4: The 303 (See Other) status code indicates that
360
- // the server is redirecting the user agent to a different resource […]
361
- // A user agent can perform a retrieval request targeting that URI
362
- // (a GET or HEAD request if using HTTP) […]
363
- (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
364
- this._options.method = "GET";
365
- // Drop a possible entity and headers related to it
366
- this._requestBodyBuffers = [];
367
- removeMatchingHeaders(/^content-/i, this._options.headers);
368
- }
348
+ // Clean up
349
+ this._requestBodyBuffers = [];
350
+ return;
351
+ }
369
352
 
370
- // Drop the Host header, as the redirect might lead to a different host
371
- var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
353
+ // The response is a redirect, so abort the current request
354
+ abortRequest(this._currentRequest);
355
+ // Discard the remainder of the response to avoid waiting for data
356
+ response.destroy();
372
357
 
373
- // If the redirect is relative, carry over the host of the last request
374
- var currentUrlParts = url.parse(this._currentUrl);
375
- var currentHost = currentHostHeader || currentUrlParts.host;
376
- var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
377
- url.format(Object.assign(currentUrlParts, { host: currentHost }));
358
+ // RFC7231§6.4: A client SHOULD detect and intervene
359
+ // in cyclical redirections (i.e., "infinite" redirection loops).
360
+ if (++this._redirectCount > this._options.maxRedirects) {
361
+ this.emit("error", new TooManyRedirectsError());
362
+ return;
363
+ }
378
364
 
379
- // Determine the URL of the redirection
380
- var redirectUrl;
381
- try {
382
- redirectUrl = url.resolve(currentUrl, location);
383
- }
384
- catch (cause) {
385
- this.emit("error", new RedirectionError(cause));
386
- return;
387
- }
365
+ // RFC7231§6.4: Automatic redirection needs to done with
366
+ // care for methods not known to be safe, […]
367
+ // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
368
+ // the request method from POST to GET for the subsequent request.
369
+ if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
370
+ // RFC7231§6.4.4: The 303 (See Other) status code indicates that
371
+ // the server is redirecting the user agent to a different resource […]
372
+ // A user agent can perform a retrieval request targeting that URI
373
+ // (a GET or HEAD request if using HTTP) […]
374
+ (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
375
+ this._options.method = "GET";
376
+ // Drop a possible entity and headers related to it
377
+ this._requestBodyBuffers = [];
378
+ removeMatchingHeaders(/^content-/i, this._options.headers);
379
+ }
388
380
 
389
- // Create the redirected request
390
- debug("redirecting to", redirectUrl);
391
- this._isRedirect = true;
392
- var redirectUrlParts = url.parse(redirectUrl);
393
- Object.assign(this._options, redirectUrlParts);
381
+ // Drop the Host header, as the redirect might lead to a different host
382
+ var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
394
383
 
395
- // Drop the Authorization header if redirecting to another domain
396
- if (!(redirectUrlParts.host === currentHost || isSubdomainOf(redirectUrlParts.host, currentHost))) {
397
- removeMatchingHeaders(/^authorization$/i, this._options.headers);
398
- }
384
+ // If the redirect is relative, carry over the host of the last request
385
+ var currentUrlParts = url.parse(this._currentUrl);
386
+ var currentHost = currentHostHeader || currentUrlParts.host;
387
+ var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
388
+ url.format(Object.assign(currentUrlParts, { host: currentHost }));
399
389
 
400
- // Evaluate the beforeRedirect callback
401
- if (typeof this._options.beforeRedirect === "function") {
402
- var responseDetails = { headers: response.headers };
403
- try {
404
- this._options.beforeRedirect.call(null, this._options, responseDetails);
405
- }
406
- catch (err) {
407
- this.emit("error", err);
408
- return;
409
- }
410
- this._sanitizeOptions(this._options);
411
- }
390
+ // Determine the URL of the redirection
391
+ var redirectUrl;
392
+ try {
393
+ redirectUrl = url.resolve(currentUrl, location);
394
+ }
395
+ catch (cause) {
396
+ this.emit("error", new RedirectionError(cause));
397
+ return;
398
+ }
399
+
400
+ // Create the redirected request
401
+ debug("redirecting to", redirectUrl);
402
+ this._isRedirect = true;
403
+ var redirectUrlParts = url.parse(redirectUrl);
404
+ Object.assign(this._options, redirectUrlParts);
405
+
406
+ // Drop confidential headers when redirecting to a less secure protocol
407
+ // or to a different domain that is not a superdomain
408
+ if (redirectUrlParts.protocol !== currentUrlParts.protocol &&
409
+ redirectUrlParts.protocol !== "https:" ||
410
+ redirectUrlParts.host !== currentHost &&
411
+ !isSubdomain(redirectUrlParts.host, currentHost)) {
412
+ removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
413
+ }
412
414
 
413
- // Perform the redirected request
415
+ // Evaluate the beforeRedirect callback
416
+ if (typeof this._options.beforeRedirect === "function") {
417
+ var responseDetails = { headers: response.headers };
414
418
  try {
415
- this._performRequest();
419
+ this._options.beforeRedirect.call(null, this._options, responseDetails);
416
420
  }
417
- catch (cause) {
418
- this.emit("error", new RedirectionError(cause));
421
+ catch (err) {
422
+ this.emit("error", err);
423
+ return;
419
424
  }
425
+ this._sanitizeOptions(this._options);
420
426
  }
421
- else {
422
- // The response is not a redirect; return it as-is
423
- response.responseUrl = this._currentUrl;
424
- response.redirects = this._redirects;
425
- this.emit("response", response);
426
427
 
427
- // Clean up
428
- this._requestBodyBuffers = [];
428
+ // Perform the redirected request
429
+ try {
430
+ this._performRequest();
431
+ }
432
+ catch (cause) {
433
+ this.emit("error", new RedirectionError(cause));
429
434
  }
430
435
  };
431
436
 
@@ -525,11 +530,12 @@ function removeMatchingHeaders(regex, headers) {
525
530
  var lastValue;
526
531
  for (var header in headers) {
527
532
  if (regex.test(header)) {
528
- lastValue = headers[header].toString().trim();
533
+ lastValue = headers[header];
529
534
  delete headers[header];
530
535
  }
531
536
  }
532
- return lastValue;
537
+ return (lastValue === null || typeof lastValue === "undefined") ?
538
+ undefined : String(lastValue).trim();
533
539
  }
534
540
 
535
541
  function createErrorType(code, defaultMessage) {
@@ -558,7 +564,7 @@ function abortRequest(request) {
558
564
  request.abort();
559
565
  }
560
566
 
561
- function isSubdomainOf(subdomain, domain) {
567
+ function isSubdomain(subdomain, domain) {
562
568
  const dot = subdomain.length - domain.length - 1;
563
569
  return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
564
570
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "follow-redirects",
3
- "version": "1.14.5",
3
+ "version": "1.14.9",
4
4
  "description": "HTTP and HTTPS modules that follow redirects.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",