follow-redirects 1.14.4 → 1.14.5

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 +44 -14
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -18,7 +18,7 @@ events.forEach(function (event) {
18
18
  // Error types with codes
19
19
  var RedirectionError = createErrorType(
20
20
  "ERR_FR_REDIRECTION_FAILURE",
21
- ""
21
+ "Redirected request failed"
22
22
  );
23
23
  var TooManyRedirectsError = createErrorType(
24
24
  "ERR_FR_TOO_MANY_REDIRECTS",
@@ -169,10 +169,16 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
169
169
 
170
170
  // Stops a timeout from triggering
171
171
  function clearTimer() {
172
+ // Clear the timeout
172
173
  if (self._timeout) {
173
174
  clearTimeout(self._timeout);
174
175
  self._timeout = null;
175
176
  }
177
+
178
+ // Clean up all attached listeners
179
+ self.removeListener("abort", clearTimer);
180
+ self.removeListener("error", clearTimer);
181
+ self.removeListener("response", clearTimer);
176
182
  if (callback) {
177
183
  self.removeListener("timeout", callback);
178
184
  }
@@ -196,8 +202,9 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
196
202
 
197
203
  // Clean up on events
198
204
  this.on("socket", destroyOnTimeout);
199
- this.once("response", clearTimer);
200
- this.once("error", clearTimer);
205
+ this.on("abort", clearTimer);
206
+ this.on("error", clearTimer);
207
+ this.on("response", clearTimer);
201
208
 
202
209
  return this;
203
210
  };
@@ -361,18 +368,32 @@ RedirectableRequest.prototype._processResponse = function (response) {
361
368
  }
362
369
 
363
370
  // Drop the Host header, as the redirect might lead to a different host
364
- var previousHostName = removeMatchingHeaders(/^host$/i, this._options.headers) ||
365
- url.parse(this._currentUrl).hostname;
371
+ var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
372
+
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 }));
378
+
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
+ }
366
388
 
367
389
  // Create the redirected request
368
- var redirectUrl = url.resolve(this._currentUrl, location);
369
390
  debug("redirecting to", redirectUrl);
370
391
  this._isRedirect = true;
371
392
  var redirectUrlParts = url.parse(redirectUrl);
372
393
  Object.assign(this._options, redirectUrlParts);
373
394
 
374
- // Drop the Authorization header if redirecting to another host
375
- if (redirectUrlParts.hostname !== previousHostName) {
395
+ // Drop the Authorization header if redirecting to another domain
396
+ if (!(redirectUrlParts.host === currentHost || isSubdomainOf(redirectUrlParts.host, currentHost))) {
376
397
  removeMatchingHeaders(/^authorization$/i, this._options.headers);
377
398
  }
378
399
 
@@ -394,9 +415,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
394
415
  this._performRequest();
395
416
  }
396
417
  catch (cause) {
397
- var error = new RedirectionError("Redirected request failed: " + cause.message);
398
- error.cause = cause;
399
- this.emit("error", error);
418
+ this.emit("error", new RedirectionError(cause));
400
419
  }
401
420
  }
402
421
  else {
@@ -506,7 +525,7 @@ function removeMatchingHeaders(regex, headers) {
506
525
  var lastValue;
507
526
  for (var header in headers) {
508
527
  if (regex.test(header)) {
509
- lastValue = headers[header];
528
+ lastValue = headers[header].toString().trim();
510
529
  delete headers[header];
511
530
  }
512
531
  }
@@ -514,9 +533,15 @@ function removeMatchingHeaders(regex, headers) {
514
533
  }
515
534
 
516
535
  function createErrorType(code, defaultMessage) {
517
- function CustomError(message) {
536
+ function CustomError(cause) {
518
537
  Error.captureStackTrace(this, this.constructor);
519
- this.message = message || defaultMessage;
538
+ if (!cause) {
539
+ this.message = defaultMessage;
540
+ }
541
+ else {
542
+ this.message = defaultMessage + ": " + cause.message;
543
+ this.cause = cause;
544
+ }
520
545
  }
521
546
  CustomError.prototype = new Error();
522
547
  CustomError.prototype.constructor = CustomError;
@@ -533,6 +558,11 @@ function abortRequest(request) {
533
558
  request.abort();
534
559
  }
535
560
 
561
+ function isSubdomainOf(subdomain, domain) {
562
+ const dot = subdomain.length - domain.length - 1;
563
+ return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
564
+ }
565
+
536
566
  // Exports
537
567
  module.exports = wrap({ http: http, https: https });
538
568
  module.exports.wrap = wrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "follow-redirects",
3
- "version": "1.14.4",
3
+ "version": "1.14.5",
4
4
  "description": "HTTP and HTTPS modules that follow redirects.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",