follow-redirects 1.15.1 → 1.15.3

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 +64 -29
  2. package/package.json +2 -3
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",
@@ -33,6 +38,9 @@ var WriteAfterEndError = createErrorType(
33
38
  "write after end"
34
39
  );
35
40
 
41
+ // istanbul ignore next
42
+ var destroy = Writable.prototype.destroy || noop;
43
+
36
44
  // An HTTP(S) request that can be redirected
37
45
  function RedirectableRequest(options, responseCallback) {
38
46
  // Initialize the request
@@ -63,10 +71,17 @@ function RedirectableRequest(options, responseCallback) {
63
71
  RedirectableRequest.prototype = Object.create(Writable.prototype);
64
72
 
65
73
  RedirectableRequest.prototype.abort = function () {
66
- abortRequest(this._currentRequest);
74
+ destroyRequest(this._currentRequest);
75
+ this._currentRequest.abort();
67
76
  this.emit("abort");
68
77
  };
69
78
 
79
+ RedirectableRequest.prototype.destroy = function (error) {
80
+ destroyRequest(this._currentRequest, error);
81
+ destroy.call(this, error);
82
+ return this;
83
+ };
84
+
70
85
  // Writes buffered data to the current native request
71
86
  RedirectableRequest.prototype.write = function (data, encoding, callback) {
72
87
  // Writing is not allowed if end has been called
@@ -75,10 +90,10 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
75
90
  }
76
91
 
77
92
  // Validate input and shift parameters if necessary
78
- if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) {
93
+ if (!isString(data) && !isBuffer(data)) {
79
94
  throw new TypeError("data should be a string, Buffer or Uint8Array");
80
95
  }
81
- if (typeof encoding === "function") {
96
+ if (isFunction(encoding)) {
82
97
  callback = encoding;
83
98
  encoding = null;
84
99
  }
@@ -107,11 +122,11 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
107
122
  // Ends the current native request
108
123
  RedirectableRequest.prototype.end = function (data, encoding, callback) {
109
124
  // Shift parameters if necessary
110
- if (typeof data === "function") {
125
+ if (isFunction(data)) {
111
126
  callback = data;
112
127
  data = encoding = null;
113
128
  }
114
- else if (typeof encoding === "function") {
129
+ else if (isFunction(encoding)) {
115
130
  callback = encoding;
116
131
  encoding = null;
117
132
  }
@@ -179,6 +194,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
179
194
  self.removeListener("abort", clearTimer);
180
195
  self.removeListener("error", clearTimer);
181
196
  self.removeListener("response", clearTimer);
197
+ self.removeListener("close", clearTimer);
182
198
  if (callback) {
183
199
  self.removeListener("timeout", callback);
184
200
  }
@@ -205,6 +221,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
205
221
  this.on("abort", clearTimer);
206
222
  this.on("error", clearTimer);
207
223
  this.on("response", clearTimer);
224
+ this.on("close", clearTimer);
208
225
 
209
226
  return this;
210
227
  };
@@ -288,7 +305,7 @@ RedirectableRequest.prototype._performRequest = function () {
288
305
  url.format(this._options) :
289
306
  // When making a request to a proxy, […]
290
307
  // a client MUST send the target URI in absolute-form […].
291
- this._currentUrl = this._options.path;
308
+ this._options.path;
292
309
 
293
310
  // End a redirected request
294
311
  // (The first request must be ended explicitly with RedirectableRequest#end)
@@ -356,7 +373,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
356
373
  }
357
374
 
358
375
  // The response is a redirect, so abort the current request
359
- abortRequest(this._currentRequest);
376
+ destroyRequest(this._currentRequest);
360
377
  // Discard the remainder of the response to avoid waiting for data
361
378
  response.destroy();
362
379
 
@@ -409,7 +426,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
409
426
  redirectUrl = url.resolve(currentUrl, location);
410
427
  }
411
428
  catch (cause) {
412
- this.emit("error", new RedirectionError(cause));
429
+ this.emit("error", new RedirectionError({ cause: cause }));
413
430
  return;
414
431
  }
415
432
 
@@ -429,7 +446,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
429
446
  }
430
447
 
431
448
  // Evaluate the beforeRedirect callback
432
- if (typeof beforeRedirect === "function") {
449
+ if (isFunction(beforeRedirect)) {
433
450
  var responseDetails = {
434
451
  headers: response.headers,
435
452
  statusCode: statusCode,
@@ -454,7 +471,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
454
471
  this._performRequest();
455
472
  }
456
473
  catch (cause) {
457
- this.emit("error", new RedirectionError(cause));
474
+ this.emit("error", new RedirectionError({ cause: cause }));
458
475
  }
459
476
  };
460
477
 
@@ -476,15 +493,19 @@ function wrap(protocols) {
476
493
  // Executes a request, following redirects
477
494
  function request(input, options, callback) {
478
495
  // Parse parameters
479
- if (typeof input === "string") {
480
- var urlStr = input;
496
+ if (isString(input)) {
497
+ var parsed;
481
498
  try {
482
- input = urlToOptions(new URL(urlStr));
499
+ parsed = urlToOptions(new URL(input));
483
500
  }
484
501
  catch (err) {
485
502
  /* istanbul ignore next */
486
- input = url.parse(urlStr);
503
+ parsed = url.parse(input);
487
504
  }
505
+ if (!isString(parsed.protocol)) {
506
+ throw new InvalidUrlError({ input });
507
+ }
508
+ input = parsed;
488
509
  }
489
510
  else if (URL && (input instanceof URL)) {
490
511
  input = urlToOptions(input);
@@ -494,7 +515,7 @@ function wrap(protocols) {
494
515
  options = input;
495
516
  input = { protocol: protocol };
496
517
  }
497
- if (typeof options === "function") {
518
+ if (isFunction(options)) {
498
519
  callback = options;
499
520
  options = null;
500
521
  }
@@ -505,6 +526,9 @@ function wrap(protocols) {
505
526
  maxBodyLength: exports.maxBodyLength,
506
527
  }, input, options);
507
528
  options.nativeProtocols = nativeProtocols;
529
+ if (!isString(options.host) && !isString(options.hostname)) {
530
+ options.hostname = "::1";
531
+ }
508
532
 
509
533
  assert.equal(options.protocol, protocol, "protocol mismatch");
510
534
  debug("options", options);
@@ -562,37 +586,48 @@ function removeMatchingHeaders(regex, headers) {
562
586
  undefined : String(lastValue).trim();
563
587
  }
564
588
 
565
- function createErrorType(code, defaultMessage) {
566
- function CustomError(cause) {
589
+ function createErrorType(code, message, baseClass) {
590
+ // Create constructor
591
+ function CustomError(properties) {
567
592
  Error.captureStackTrace(this, this.constructor);
568
- if (!cause) {
569
- this.message = defaultMessage;
570
- }
571
- else {
572
- this.message = defaultMessage + ": " + cause.message;
573
- this.cause = cause;
574
- }
593
+ Object.assign(this, properties || {});
594
+ this.code = code;
595
+ this.message = this.cause ? message + ": " + this.cause.message : message;
575
596
  }
576
- CustomError.prototype = new Error();
597
+
598
+ // Attach constructor and set default properties
599
+ CustomError.prototype = new (baseClass || Error)();
577
600
  CustomError.prototype.constructor = CustomError;
578
601
  CustomError.prototype.name = "Error [" + code + "]";
579
- CustomError.prototype.code = code;
580
602
  return CustomError;
581
603
  }
582
604
 
583
- function abortRequest(request) {
605
+ function destroyRequest(request, error) {
584
606
  for (var event of events) {
585
607
  request.removeListener(event, eventHandlers[event]);
586
608
  }
587
609
  request.on("error", noop);
588
- request.abort();
610
+ request.destroy(error);
589
611
  }
590
612
 
591
613
  function isSubdomain(subdomain, domain) {
592
- const dot = subdomain.length - domain.length - 1;
614
+ assert(isString(subdomain) && isString(domain));
615
+ var dot = subdomain.length - domain.length - 1;
593
616
  return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
594
617
  }
595
618
 
619
+ function isString(value) {
620
+ return typeof value === "string" || value instanceof String;
621
+ }
622
+
623
+ function isFunction(value) {
624
+ return typeof value === "function";
625
+ }
626
+
627
+ function isBuffer(value) {
628
+ return typeof value === "object" && ("length" in value);
629
+ }
630
+
596
631
  // Exports
597
632
  module.exports = wrap({ http: http, https: https });
598
633
  module.exports.wrap = wrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "follow-redirects",
3
- "version": "1.15.1",
3
+ "version": "1.15.3",
4
4
  "description": "HTTP and HTTPS modules that follow redirects.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -11,9 +11,8 @@
11
11
  "node": ">=4.0"
12
12
  },
13
13
  "scripts": {
14
- "test": "npm run lint && npm run mocha",
15
14
  "lint": "eslint *.js test",
16
- "mocha": "nyc mocha"
15
+ "test": "nyc mocha"
17
16
  },
18
17
  "repository": {
19
18
  "type": "git",