follow-redirects 1.14.8 → 1.15.4
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 +252 -154
- package/package.json +2 -3
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
@@ -6,6 +6,29 @@ var Writable = require("stream").Writable;
|
|
6
6
|
var assert = require("assert");
|
7
7
|
var debug = require("./debug");
|
8
8
|
|
9
|
+
// Whether to use the native URL object or the legacy url module
|
10
|
+
var useNativeURL = false;
|
11
|
+
try {
|
12
|
+
assert(new URL());
|
13
|
+
}
|
14
|
+
catch (error) {
|
15
|
+
useNativeURL = error.code === "ERR_INVALID_URL";
|
16
|
+
}
|
17
|
+
|
18
|
+
// URL fields to preserve in copy operations
|
19
|
+
var preservedUrlFields = [
|
20
|
+
"auth",
|
21
|
+
"host",
|
22
|
+
"hostname",
|
23
|
+
"href",
|
24
|
+
"path",
|
25
|
+
"pathname",
|
26
|
+
"port",
|
27
|
+
"protocol",
|
28
|
+
"query",
|
29
|
+
"search",
|
30
|
+
];
|
31
|
+
|
9
32
|
// Create handlers that pass events from native requests
|
10
33
|
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
|
11
34
|
var eventHandlers = Object.create(null);
|
@@ -16,13 +39,19 @@ events.forEach(function (event) {
|
|
16
39
|
});
|
17
40
|
|
18
41
|
// Error types with codes
|
42
|
+
var InvalidUrlError = createErrorType(
|
43
|
+
"ERR_INVALID_URL",
|
44
|
+
"Invalid URL",
|
45
|
+
TypeError
|
46
|
+
);
|
19
47
|
var RedirectionError = createErrorType(
|
20
48
|
"ERR_FR_REDIRECTION_FAILURE",
|
21
49
|
"Redirected request failed"
|
22
50
|
);
|
23
51
|
var TooManyRedirectsError = createErrorType(
|
24
52
|
"ERR_FR_TOO_MANY_REDIRECTS",
|
25
|
-
"Maximum number of redirects exceeded"
|
53
|
+
"Maximum number of redirects exceeded",
|
54
|
+
RedirectionError
|
26
55
|
);
|
27
56
|
var MaxBodyLengthExceededError = createErrorType(
|
28
57
|
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
|
@@ -33,6 +62,9 @@ var WriteAfterEndError = createErrorType(
|
|
33
62
|
"write after end"
|
34
63
|
);
|
35
64
|
|
65
|
+
// istanbul ignore next
|
66
|
+
var destroy = Writable.prototype.destroy || noop;
|
67
|
+
|
36
68
|
// An HTTP(S) request that can be redirected
|
37
69
|
function RedirectableRequest(options, responseCallback) {
|
38
70
|
// Initialize the request
|
@@ -54,7 +86,13 @@ function RedirectableRequest(options, responseCallback) {
|
|
54
86
|
// React to responses of native requests
|
55
87
|
var self = this;
|
56
88
|
this._onNativeResponse = function (response) {
|
57
|
-
|
89
|
+
try {
|
90
|
+
self._processResponse(response);
|
91
|
+
}
|
92
|
+
catch (cause) {
|
93
|
+
self.emit("error", cause instanceof RedirectionError ?
|
94
|
+
cause : new RedirectionError({ cause: cause }));
|
95
|
+
}
|
58
96
|
};
|
59
97
|
|
60
98
|
// Perform the first request
|
@@ -63,10 +101,17 @@ function RedirectableRequest(options, responseCallback) {
|
|
63
101
|
RedirectableRequest.prototype = Object.create(Writable.prototype);
|
64
102
|
|
65
103
|
RedirectableRequest.prototype.abort = function () {
|
66
|
-
|
104
|
+
destroyRequest(this._currentRequest);
|
105
|
+
this._currentRequest.abort();
|
67
106
|
this.emit("abort");
|
68
107
|
};
|
69
108
|
|
109
|
+
RedirectableRequest.prototype.destroy = function (error) {
|
110
|
+
destroyRequest(this._currentRequest, error);
|
111
|
+
destroy.call(this, error);
|
112
|
+
return this;
|
113
|
+
};
|
114
|
+
|
70
115
|
// Writes buffered data to the current native request
|
71
116
|
RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
72
117
|
// Writing is not allowed if end has been called
|
@@ -75,10 +120,10 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
|
75
120
|
}
|
76
121
|
|
77
122
|
// Validate input and shift parameters if necessary
|
78
|
-
if (!(
|
123
|
+
if (!isString(data) && !isBuffer(data)) {
|
79
124
|
throw new TypeError("data should be a string, Buffer or Uint8Array");
|
80
125
|
}
|
81
|
-
if (
|
126
|
+
if (isFunction(encoding)) {
|
82
127
|
callback = encoding;
|
83
128
|
encoding = null;
|
84
129
|
}
|
@@ -107,11 +152,11 @@ RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
|
107
152
|
// Ends the current native request
|
108
153
|
RedirectableRequest.prototype.end = function (data, encoding, callback) {
|
109
154
|
// Shift parameters if necessary
|
110
|
-
if (
|
155
|
+
if (isFunction(data)) {
|
111
156
|
callback = data;
|
112
157
|
data = encoding = null;
|
113
158
|
}
|
114
|
-
else if (
|
159
|
+
else if (isFunction(encoding)) {
|
115
160
|
callback = encoding;
|
116
161
|
encoding = null;
|
117
162
|
}
|
@@ -179,6 +224,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
|
179
224
|
self.removeListener("abort", clearTimer);
|
180
225
|
self.removeListener("error", clearTimer);
|
181
226
|
self.removeListener("response", clearTimer);
|
227
|
+
self.removeListener("close", clearTimer);
|
182
228
|
if (callback) {
|
183
229
|
self.removeListener("timeout", callback);
|
184
230
|
}
|
@@ -205,6 +251,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
|
205
251
|
this.on("abort", clearTimer);
|
206
252
|
this.on("error", clearTimer);
|
207
253
|
this.on("response", clearTimer);
|
254
|
+
this.on("close", clearTimer);
|
208
255
|
|
209
256
|
return this;
|
210
257
|
};
|
@@ -263,32 +310,36 @@ RedirectableRequest.prototype._performRequest = function () {
|
|
263
310
|
var protocol = this._options.protocol;
|
264
311
|
var nativeProtocol = this._options.nativeProtocols[protocol];
|
265
312
|
if (!nativeProtocol) {
|
266
|
-
|
267
|
-
return;
|
313
|
+
throw new TypeError("Unsupported protocol " + protocol);
|
268
314
|
}
|
269
315
|
|
270
316
|
// If specified, use the agent corresponding to the protocol
|
271
317
|
// (HTTP and HTTPS use different types of agents)
|
272
318
|
if (this._options.agents) {
|
273
|
-
var scheme = protocol.
|
319
|
+
var scheme = protocol.slice(0, -1);
|
274
320
|
this._options.agent = this._options.agents[scheme];
|
275
321
|
}
|
276
322
|
|
277
|
-
// Create the native request
|
323
|
+
// Create the native request and set up its event handlers
|
278
324
|
var request = this._currentRequest =
|
279
325
|
nativeProtocol.request(this._options, this._onNativeResponse);
|
280
|
-
this._currentUrl = url.format(this._options);
|
281
|
-
|
282
|
-
// Set up event handlers
|
283
326
|
request._redirectable = this;
|
284
|
-
for (var
|
285
|
-
request.on(
|
327
|
+
for (var event of events) {
|
328
|
+
request.on(event, eventHandlers[event]);
|
286
329
|
}
|
287
330
|
|
331
|
+
// RFC7230§5.3.1: When making a request directly to an origin server, […]
|
332
|
+
// a client MUST send only the absolute path […] as the request-target.
|
333
|
+
this._currentUrl = /^\//.test(this._options.path) ?
|
334
|
+
url.format(this._options) :
|
335
|
+
// When making a request to a proxy, […]
|
336
|
+
// a client MUST send the target URI in absolute-form […].
|
337
|
+
this._options.path;
|
338
|
+
|
288
339
|
// End a redirected request
|
289
340
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
290
341
|
if (this._isRedirect) {
|
291
|
-
// Write the request entity and end
|
342
|
+
// Write the request entity and end
|
292
343
|
var i = 0;
|
293
344
|
var self = this;
|
294
345
|
var buffers = this._requestBodyBuffers;
|
@@ -336,98 +387,99 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
336
387
|
// the user agent MAY automatically redirect its request to the URI
|
337
388
|
// referenced by the Location field value,
|
338
389
|
// even if the specific status code is not understood.
|
339
|
-
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
|
-
}
|
353
|
-
|
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
|
-
}
|
369
390
|
|
370
|
-
|
371
|
-
|
391
|
+
// If the response is not a redirect; return it as-is
|
392
|
+
var location = response.headers.location;
|
393
|
+
if (!location || this._options.followRedirects === false ||
|
394
|
+
statusCode < 300 || statusCode >= 400) {
|
395
|
+
response.responseUrl = this._currentUrl;
|
396
|
+
response.redirects = this._redirects;
|
397
|
+
this.emit("response", response);
|
372
398
|
|
373
|
-
//
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
url.format(Object.assign(currentUrlParts, { host: currentHost }));
|
399
|
+
// Clean up
|
400
|
+
this._requestBodyBuffers = [];
|
401
|
+
return;
|
402
|
+
}
|
378
403
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
404
|
+
// The response is a redirect, so abort the current request
|
405
|
+
destroyRequest(this._currentRequest);
|
406
|
+
// Discard the remainder of the response to avoid waiting for data
|
407
|
+
response.destroy();
|
408
|
+
|
409
|
+
// RFC7231§6.4: A client SHOULD detect and intervene
|
410
|
+
// in cyclical redirections (i.e., "infinite" redirection loops).
|
411
|
+
if (++this._redirectCount > this._options.maxRedirects) {
|
412
|
+
throw new TooManyRedirectsError();
|
413
|
+
}
|
414
|
+
|
415
|
+
// Store the request headers if applicable
|
416
|
+
var requestHeaders;
|
417
|
+
var beforeRedirect = this._options.beforeRedirect;
|
418
|
+
if (beforeRedirect) {
|
419
|
+
requestHeaders = Object.assign({
|
420
|
+
// The Host header was set by nativeProtocol.request
|
421
|
+
Host: response.req.getHeader("host"),
|
422
|
+
}, this._options.headers);
|
423
|
+
}
|
424
|
+
|
425
|
+
// RFC7231§6.4: Automatic redirection needs to done with
|
426
|
+
// care for methods not known to be safe, […]
|
427
|
+
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
428
|
+
// the request method from POST to GET for the subsequent request.
|
429
|
+
var method = this._options.method;
|
430
|
+
if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
|
431
|
+
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
432
|
+
// the server is redirecting the user agent to a different resource […]
|
433
|
+
// A user agent can perform a retrieval request targeting that URI
|
434
|
+
// (a GET or HEAD request if using HTTP) […]
|
435
|
+
(statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
|
436
|
+
this._options.method = "GET";
|
437
|
+
// Drop a possible entity and headers related to it
|
438
|
+
this._requestBodyBuffers = [];
|
439
|
+
removeMatchingHeaders(/^content-/i, this._options.headers);
|
440
|
+
}
|
388
441
|
|
389
|
-
|
390
|
-
|
391
|
-
this._isRedirect = true;
|
392
|
-
var redirectUrlParts = url.parse(redirectUrl);
|
393
|
-
Object.assign(this._options, redirectUrlParts);
|
442
|
+
// Drop the Host header, as the redirect might lead to a different host
|
443
|
+
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
|
394
444
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
}
|
445
|
+
// If the redirect is relative, carry over the host of the last request
|
446
|
+
var currentUrlParts = parseUrl(this._currentUrl);
|
447
|
+
var currentHost = currentHostHeader || currentUrlParts.host;
|
448
|
+
var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
|
449
|
+
url.format(Object.assign(currentUrlParts, { host: currentHost }));
|
400
450
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
}
|
407
|
-
catch (err) {
|
408
|
-
this.emit("error", err);
|
409
|
-
return;
|
410
|
-
}
|
411
|
-
this._sanitizeOptions(this._options);
|
412
|
-
}
|
451
|
+
// Create the redirected request
|
452
|
+
var redirectUrl = resolveUrl(location, currentUrl);
|
453
|
+
debug("redirecting to", redirectUrl.href);
|
454
|
+
this._isRedirect = true;
|
455
|
+
spreadUrlObject(redirectUrl, this._options);
|
413
456
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
457
|
+
// Drop confidential headers when redirecting to a less secure protocol
|
458
|
+
// or to a different domain that is not a superdomain
|
459
|
+
if (redirectUrl.protocol !== currentUrlParts.protocol &&
|
460
|
+
redirectUrl.protocol !== "https:" ||
|
461
|
+
redirectUrl.host !== currentHost &&
|
462
|
+
!isSubdomain(redirectUrl.host, currentHost)) {
|
463
|
+
removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
|
421
464
|
}
|
422
|
-
else {
|
423
|
-
// The response is not a redirect; return it as-is
|
424
|
-
response.responseUrl = this._currentUrl;
|
425
|
-
response.redirects = this._redirects;
|
426
|
-
this.emit("response", response);
|
427
465
|
|
428
|
-
|
429
|
-
|
466
|
+
// Evaluate the beforeRedirect callback
|
467
|
+
if (isFunction(beforeRedirect)) {
|
468
|
+
var responseDetails = {
|
469
|
+
headers: response.headers,
|
470
|
+
statusCode: statusCode,
|
471
|
+
};
|
472
|
+
var requestDetails = {
|
473
|
+
url: currentUrl,
|
474
|
+
method: method,
|
475
|
+
headers: requestHeaders,
|
476
|
+
};
|
477
|
+
beforeRedirect(this._options, responseDetails, requestDetails);
|
478
|
+
this._sanitizeOptions(this._options);
|
430
479
|
}
|
480
|
+
|
481
|
+
// Perform the redirected request
|
482
|
+
this._performRequest();
|
431
483
|
};
|
432
484
|
|
433
485
|
// Wraps the key/value object of protocols with redirect functionality
|
@@ -447,26 +499,19 @@ function wrap(protocols) {
|
|
447
499
|
|
448
500
|
// Executes a request, following redirects
|
449
501
|
function request(input, options, callback) {
|
450
|
-
// Parse parameters
|
451
|
-
if (
|
452
|
-
|
453
|
-
try {
|
454
|
-
input = urlToOptions(new URL(urlStr));
|
455
|
-
}
|
456
|
-
catch (err) {
|
457
|
-
/* istanbul ignore next */
|
458
|
-
input = url.parse(urlStr);
|
459
|
-
}
|
502
|
+
// Parse parameters, ensuring that input is an object
|
503
|
+
if (isURL(input)) {
|
504
|
+
input = spreadUrlObject(input);
|
460
505
|
}
|
461
|
-
else if (
|
462
|
-
input =
|
506
|
+
else if (isString(input)) {
|
507
|
+
input = spreadUrlObject(parseUrl(input));
|
463
508
|
}
|
464
509
|
else {
|
465
510
|
callback = options;
|
466
|
-
options = input;
|
511
|
+
options = validateUrl(input);
|
467
512
|
input = { protocol: protocol };
|
468
513
|
}
|
469
|
-
if (
|
514
|
+
if (isFunction(options)) {
|
470
515
|
callback = options;
|
471
516
|
options = null;
|
472
517
|
}
|
@@ -477,6 +522,9 @@ function wrap(protocols) {
|
|
477
522
|
maxBodyLength: exports.maxBodyLength,
|
478
523
|
}, input, options);
|
479
524
|
options.nativeProtocols = nativeProtocols;
|
525
|
+
if (!isString(options.host) && !isString(options.hostname)) {
|
526
|
+
options.hostname = "::1";
|
527
|
+
}
|
480
528
|
|
481
529
|
assert.equal(options.protocol, protocol, "protocol mismatch");
|
482
530
|
debug("options", options);
|
@@ -499,27 +547,57 @@ function wrap(protocols) {
|
|
499
547
|
return exports;
|
500
548
|
}
|
501
549
|
|
502
|
-
/* istanbul ignore next */
|
503
550
|
function noop() { /* empty */ }
|
504
551
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
552
|
+
function parseUrl(input) {
|
553
|
+
var parsed;
|
554
|
+
/* istanbul ignore else */
|
555
|
+
if (useNativeURL) {
|
556
|
+
parsed = new URL(input);
|
557
|
+
}
|
558
|
+
else {
|
559
|
+
// Ensure the URL is valid and absolute
|
560
|
+
parsed = validateUrl(url.parse(input));
|
561
|
+
if (!isString(parsed.protocol)) {
|
562
|
+
throw new InvalidUrlError({ input });
|
563
|
+
}
|
564
|
+
}
|
565
|
+
return parsed;
|
566
|
+
}
|
567
|
+
|
568
|
+
function resolveUrl(relative, base) {
|
569
|
+
/* istanbul ignore next */
|
570
|
+
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
|
571
|
+
}
|
572
|
+
|
573
|
+
function validateUrl(input) {
|
574
|
+
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
|
575
|
+
throw new InvalidUrlError({ input: input.href || input });
|
576
|
+
}
|
577
|
+
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
|
578
|
+
throw new InvalidUrlError({ input: input.href || input });
|
521
579
|
}
|
522
|
-
return
|
580
|
+
return input;
|
581
|
+
}
|
582
|
+
|
583
|
+
function spreadUrlObject(urlObject, target) {
|
584
|
+
var spread = target || {};
|
585
|
+
for (var key of preservedUrlFields) {
|
586
|
+
spread[key] = urlObject[key];
|
587
|
+
}
|
588
|
+
|
589
|
+
// Fix IPv6 hostname
|
590
|
+
if (spread.hostname.startsWith("[")) {
|
591
|
+
spread.hostname = spread.hostname.slice(1, -1);
|
592
|
+
}
|
593
|
+
// Ensure port is a number
|
594
|
+
if (spread.port !== "") {
|
595
|
+
spread.port = Number(spread.port);
|
596
|
+
}
|
597
|
+
// Concatenate path
|
598
|
+
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
|
599
|
+
|
600
|
+
return spread;
|
523
601
|
}
|
524
602
|
|
525
603
|
function removeMatchingHeaders(regex, headers) {
|
@@ -534,40 +612,60 @@ function removeMatchingHeaders(regex, headers) {
|
|
534
612
|
undefined : String(lastValue).trim();
|
535
613
|
}
|
536
614
|
|
537
|
-
function createErrorType(code,
|
538
|
-
|
615
|
+
function createErrorType(code, message, baseClass) {
|
616
|
+
// Create constructor
|
617
|
+
function CustomError(properties) {
|
539
618
|
Error.captureStackTrace(this, this.constructor);
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
619
|
+
Object.assign(this, properties || {});
|
620
|
+
this.code = code;
|
621
|
+
this.message = this.cause ? message + ": " + this.cause.message : message;
|
622
|
+
}
|
623
|
+
|
624
|
+
// Attach constructor and set default properties
|
625
|
+
CustomError.prototype = new (baseClass || Error)();
|
626
|
+
Object.defineProperties(CustomError.prototype, {
|
627
|
+
constructor: {
|
628
|
+
value: CustomError,
|
629
|
+
enumerable: false,
|
630
|
+
},
|
631
|
+
name: {
|
632
|
+
value: "Error [" + code + "]",
|
633
|
+
enumerable: false,
|
634
|
+
},
|
635
|
+
});
|
552
636
|
return CustomError;
|
553
637
|
}
|
554
638
|
|
555
|
-
function
|
556
|
-
for (var
|
557
|
-
request.removeListener(
|
639
|
+
function destroyRequest(request, error) {
|
640
|
+
for (var event of events) {
|
641
|
+
request.removeListener(event, eventHandlers[event]);
|
558
642
|
}
|
559
643
|
request.on("error", noop);
|
560
|
-
request.
|
644
|
+
request.destroy(error);
|
561
645
|
}
|
562
646
|
|
563
|
-
function
|
564
|
-
|
565
|
-
|
566
|
-
}
|
567
|
-
const dot = subdomain.length - domain.length - 1;
|
647
|
+
function isSubdomain(subdomain, domain) {
|
648
|
+
assert(isString(subdomain) && isString(domain));
|
649
|
+
var dot = subdomain.length - domain.length - 1;
|
568
650
|
return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
|
569
651
|
}
|
570
652
|
|
653
|
+
function isString(value) {
|
654
|
+
return typeof value === "string" || value instanceof String;
|
655
|
+
}
|
656
|
+
|
657
|
+
function isFunction(value) {
|
658
|
+
return typeof value === "function";
|
659
|
+
}
|
660
|
+
|
661
|
+
function isBuffer(value) {
|
662
|
+
return typeof value === "object" && ("length" in value);
|
663
|
+
}
|
664
|
+
|
665
|
+
function isURL(value) {
|
666
|
+
return URL && value instanceof URL;
|
667
|
+
}
|
668
|
+
|
571
669
|
// Exports
|
572
670
|
module.exports = wrap({ http: http, https: https });
|
573
671
|
module.exports.wrap = wrap;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "follow-redirects",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.15.4",
|
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
|
-
"
|
15
|
+
"test": "nyc mocha"
|
17
16
|
},
|
18
17
|
"repository": {
|
19
18
|
"type": "git",
|