follow-redirects 1.14.1 → 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 (3) hide show
  1. package/debug.js +2 -1
  2. package/index.js +58 -21
  3. package/package.json +1 -1
package/debug.js CHANGED
@@ -6,7 +6,8 @@ module.exports = function () {
6
6
  /* eslint global-require: off */
7
7
  debug = require("debug")("follow-redirects");
8
8
  }
9
- catch (error) {
9
+ catch (error) { /* */ }
10
+ if (typeof debug !== "function") {
10
11
  debug = function () { /* */ };
11
12
  }
12
13
  }
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",
@@ -147,10 +147,8 @@ RedirectableRequest.prototype.removeHeader = function (name) {
147
147
  // Global timeout for all underlying requests
148
148
  RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
149
149
  var self = this;
150
- if (callback) {
151
- this.on("timeout", callback);
152
- }
153
150
 
151
+ // Destroys the socket on timeout
154
152
  function destroyOnTimeout(socket) {
155
153
  socket.setTimeout(msecs);
156
154
  socket.removeListener("timeout", socket.destroy);
@@ -169,18 +167,32 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
169
167
  destroyOnTimeout(socket);
170
168
  }
171
169
 
172
- // Prevent a timeout from triggering
170
+ // Stops a timeout from triggering
173
171
  function clearTimer() {
174
- clearTimeout(this._timeout);
172
+ // Clear the timeout
173
+ if (self._timeout) {
174
+ clearTimeout(self._timeout);
175
+ self._timeout = null;
176
+ }
177
+
178
+ // Clean up all attached listeners
179
+ self.removeListener("abort", clearTimer);
180
+ self.removeListener("error", clearTimer);
181
+ self.removeListener("response", clearTimer);
175
182
  if (callback) {
176
183
  self.removeListener("timeout", callback);
177
184
  }
178
- if (!this.socket) {
185
+ if (!self.socket) {
179
186
  self._currentRequest.removeListener("socket", startTimer);
180
187
  }
181
188
  }
182
189
 
183
- // Start the timer when the socket is opened
190
+ // Attach callback if passed
191
+ if (callback) {
192
+ this.on("timeout", callback);
193
+ }
194
+
195
+ // Start the timer if or when the socket is opened
184
196
  if (this.socket) {
185
197
  startTimer(this.socket);
186
198
  }
@@ -188,9 +200,11 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
188
200
  this._currentRequest.once("socket", startTimer);
189
201
  }
190
202
 
203
+ // Clean up on events
191
204
  this.on("socket", destroyOnTimeout);
192
- this.once("response", clearTimer);
193
- this.once("error", clearTimer);
205
+ this.on("abort", clearTimer);
206
+ this.on("error", clearTimer);
207
+ this.on("response", clearTimer);
194
208
 
195
209
  return this;
196
210
  };
@@ -354,18 +368,32 @@ RedirectableRequest.prototype._processResponse = function (response) {
354
368
  }
355
369
 
356
370
  // Drop the Host header, as the redirect might lead to a different host
357
- var previousHostName = removeMatchingHeaders(/^host$/i, this._options.headers) ||
358
- 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
+ }
359
388
 
360
389
  // Create the redirected request
361
- var redirectUrl = url.resolve(this._currentUrl, location);
362
390
  debug("redirecting to", redirectUrl);
363
391
  this._isRedirect = true;
364
392
  var redirectUrlParts = url.parse(redirectUrl);
365
393
  Object.assign(this._options, redirectUrlParts);
366
394
 
367
- // Drop the Authorization header if redirecting to another host
368
- if (redirectUrlParts.hostname !== previousHostName) {
395
+ // Drop the Authorization header if redirecting to another domain
396
+ if (!(redirectUrlParts.host === currentHost || isSubdomainOf(redirectUrlParts.host, currentHost))) {
369
397
  removeMatchingHeaders(/^authorization$/i, this._options.headers);
370
398
  }
371
399
 
@@ -387,9 +415,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
387
415
  this._performRequest();
388
416
  }
389
417
  catch (cause) {
390
- var error = new RedirectionError("Redirected request failed: " + cause.message);
391
- error.cause = cause;
392
- this.emit("error", error);
418
+ this.emit("error", new RedirectionError(cause));
393
419
  }
394
420
  }
395
421
  else {
@@ -499,7 +525,7 @@ function removeMatchingHeaders(regex, headers) {
499
525
  var lastValue;
500
526
  for (var header in headers) {
501
527
  if (regex.test(header)) {
502
- lastValue = headers[header];
528
+ lastValue = headers[header].toString().trim();
503
529
  delete headers[header];
504
530
  }
505
531
  }
@@ -507,9 +533,15 @@ function removeMatchingHeaders(regex, headers) {
507
533
  }
508
534
 
509
535
  function createErrorType(code, defaultMessage) {
510
- function CustomError(message) {
536
+ function CustomError(cause) {
511
537
  Error.captureStackTrace(this, this.constructor);
512
- 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
+ }
513
545
  }
514
546
  CustomError.prototype = new Error();
515
547
  CustomError.prototype.constructor = CustomError;
@@ -526,6 +558,11 @@ function abortRequest(request) {
526
558
  request.abort();
527
559
  }
528
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
+
529
566
  // Exports
530
567
  module.exports = wrap({ http: http, https: https });
531
568
  module.exports.wrap = wrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "follow-redirects",
3
- "version": "1.14.1",
3
+ "version": "1.14.5",
4
4
  "description": "HTTP and HTTPS modules that follow redirects.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",