follow-redirects 1.14.4 → 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 +266 -133
- 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
|
}
|
@@ -169,10 +214,17 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
|
169
214
|
|
170
215
|
// Stops a timeout from triggering
|
171
216
|
function clearTimer() {
|
217
|
+
// Clear the timeout
|
172
218
|
if (self._timeout) {
|
173
219
|
clearTimeout(self._timeout);
|
174
220
|
self._timeout = null;
|
175
221
|
}
|
222
|
+
|
223
|
+
// Clean up all attached listeners
|
224
|
+
self.removeListener("abort", clearTimer);
|
225
|
+
self.removeListener("error", clearTimer);
|
226
|
+
self.removeListener("response", clearTimer);
|
227
|
+
self.removeListener("close", clearTimer);
|
176
228
|
if (callback) {
|
177
229
|
self.removeListener("timeout", callback);
|
178
230
|
}
|
@@ -196,8 +248,10 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
|
196
248
|
|
197
249
|
// Clean up on events
|
198
250
|
this.on("socket", destroyOnTimeout);
|
199
|
-
this.
|
200
|
-
this.
|
251
|
+
this.on("abort", clearTimer);
|
252
|
+
this.on("error", clearTimer);
|
253
|
+
this.on("response", clearTimer);
|
254
|
+
this.on("close", clearTimer);
|
201
255
|
|
202
256
|
return this;
|
203
257
|
};
|
@@ -256,32 +310,36 @@ RedirectableRequest.prototype._performRequest = function () {
|
|
256
310
|
var protocol = this._options.protocol;
|
257
311
|
var nativeProtocol = this._options.nativeProtocols[protocol];
|
258
312
|
if (!nativeProtocol) {
|
259
|
-
|
260
|
-
return;
|
313
|
+
throw new TypeError("Unsupported protocol " + protocol);
|
261
314
|
}
|
262
315
|
|
263
316
|
// If specified, use the agent corresponding to the protocol
|
264
317
|
// (HTTP and HTTPS use different types of agents)
|
265
318
|
if (this._options.agents) {
|
266
|
-
var scheme = protocol.
|
319
|
+
var scheme = protocol.slice(0, -1);
|
267
320
|
this._options.agent = this._options.agents[scheme];
|
268
321
|
}
|
269
322
|
|
270
|
-
// Create the native request
|
323
|
+
// Create the native request and set up its event handlers
|
271
324
|
var request = this._currentRequest =
|
272
325
|
nativeProtocol.request(this._options, this._onNativeResponse);
|
273
|
-
this._currentUrl = url.format(this._options);
|
274
|
-
|
275
|
-
// Set up event handlers
|
276
326
|
request._redirectable = this;
|
277
|
-
for (var
|
278
|
-
request.on(
|
327
|
+
for (var event of events) {
|
328
|
+
request.on(event, eventHandlers[event]);
|
279
329
|
}
|
280
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
|
+
|
281
339
|
// End a redirected request
|
282
340
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
283
341
|
if (this._isRedirect) {
|
284
|
-
// Write the request entity and end
|
342
|
+
// Write the request entity and end
|
285
343
|
var i = 0;
|
286
344
|
var self = this;
|
287
345
|
var buffers = this._requestBodyBuffers;
|
@@ -329,85 +387,99 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
|
329
387
|
// the user agent MAY automatically redirect its request to the URI
|
330
388
|
// referenced by the Location field value,
|
331
389
|
// even if the specific status code is not understood.
|
390
|
+
|
391
|
+
// If the response is not a redirect; return it as-is
|
332
392
|
var location = response.headers.location;
|
333
|
-
if (location
|
334
|
-
statusCode
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
response.destroy();
|
339
|
-
|
340
|
-
// RFC7231§6.4: A client SHOULD detect and intervene
|
341
|
-
// in cyclical redirections (i.e., "infinite" redirection loops).
|
342
|
-
if (++this._redirectCount > this._options.maxRedirects) {
|
343
|
-
this.emit("error", new TooManyRedirectsError());
|
344
|
-
return;
|
345
|
-
}
|
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);
|
346
398
|
|
347
|
-
//
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
|
352
|
-
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
353
|
-
// the server is redirecting the user agent to a different resource […]
|
354
|
-
// A user agent can perform a retrieval request targeting that URI
|
355
|
-
// (a GET or HEAD request if using HTTP) […]
|
356
|
-
(statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
|
357
|
-
this._options.method = "GET";
|
358
|
-
// Drop a possible entity and headers related to it
|
359
|
-
this._requestBodyBuffers = [];
|
360
|
-
removeMatchingHeaders(/^content-/i, this._options.headers);
|
361
|
-
}
|
399
|
+
// Clean up
|
400
|
+
this._requestBodyBuffers = [];
|
401
|
+
return;
|
402
|
+
}
|
362
403
|
|
363
|
-
|
364
|
-
|
365
|
-
|
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();
|
366
408
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
Object.assign(this._options, redirectUrlParts);
|
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
|
+
}
|
373
414
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
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
|
+
}
|
378
424
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
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
|
+
}
|
391
441
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
442
|
+
// Drop the Host header, as the redirect might lead to a different host
|
443
|
+
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
|
444
|
+
|
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 }));
|
450
|
+
|
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);
|
456
|
+
|
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);
|
401
464
|
}
|
402
|
-
else {
|
403
|
-
// The response is not a redirect; return it as-is
|
404
|
-
response.responseUrl = this._currentUrl;
|
405
|
-
response.redirects = this._redirects;
|
406
|
-
this.emit("response", response);
|
407
465
|
|
408
|
-
|
409
|
-
|
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);
|
410
479
|
}
|
480
|
+
|
481
|
+
// Perform the redirected request
|
482
|
+
this._performRequest();
|
411
483
|
};
|
412
484
|
|
413
485
|
// Wraps the key/value object of protocols with redirect functionality
|
@@ -427,26 +499,19 @@ function wrap(protocols) {
|
|
427
499
|
|
428
500
|
// Executes a request, following redirects
|
429
501
|
function request(input, options, callback) {
|
430
|
-
// Parse parameters
|
431
|
-
if (
|
432
|
-
|
433
|
-
try {
|
434
|
-
input = urlToOptions(new URL(urlStr));
|
435
|
-
}
|
436
|
-
catch (err) {
|
437
|
-
/* istanbul ignore next */
|
438
|
-
input = url.parse(urlStr);
|
439
|
-
}
|
502
|
+
// Parse parameters, ensuring that input is an object
|
503
|
+
if (isURL(input)) {
|
504
|
+
input = spreadUrlObject(input);
|
440
505
|
}
|
441
|
-
else if (
|
442
|
-
input =
|
506
|
+
else if (isString(input)) {
|
507
|
+
input = spreadUrlObject(parseUrl(input));
|
443
508
|
}
|
444
509
|
else {
|
445
510
|
callback = options;
|
446
|
-
options = input;
|
511
|
+
options = validateUrl(input);
|
447
512
|
input = { protocol: protocol };
|
448
513
|
}
|
449
|
-
if (
|
514
|
+
if (isFunction(options)) {
|
450
515
|
callback = options;
|
451
516
|
options = null;
|
452
517
|
}
|
@@ -457,6 +522,9 @@ function wrap(protocols) {
|
|
457
522
|
maxBodyLength: exports.maxBodyLength,
|
458
523
|
}, input, options);
|
459
524
|
options.nativeProtocols = nativeProtocols;
|
525
|
+
if (!isString(options.host) && !isString(options.hostname)) {
|
526
|
+
options.hostname = "::1";
|
527
|
+
}
|
460
528
|
|
461
529
|
assert.equal(options.protocol, protocol, "protocol mismatch");
|
462
530
|
debug("options", options);
|
@@ -479,27 +547,57 @@ function wrap(protocols) {
|
|
479
547
|
return exports;
|
480
548
|
}
|
481
549
|
|
482
|
-
/* istanbul ignore next */
|
483
550
|
function noop() { /* empty */ }
|
484
551
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
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 });
|
501
576
|
}
|
502
|
-
|
577
|
+
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
|
578
|
+
throw new InvalidUrlError({ input: input.href || input });
|
579
|
+
}
|
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;
|
503
601
|
}
|
504
602
|
|
505
603
|
function removeMatchingHeaders(regex, headers) {
|
@@ -510,27 +608,62 @@ function removeMatchingHeaders(regex, headers) {
|
|
510
608
|
delete headers[header];
|
511
609
|
}
|
512
610
|
}
|
513
|
-
return lastValue
|
611
|
+
return (lastValue === null || typeof lastValue === "undefined") ?
|
612
|
+
undefined : String(lastValue).trim();
|
514
613
|
}
|
515
614
|
|
516
|
-
function createErrorType(code,
|
517
|
-
|
615
|
+
function createErrorType(code, message, baseClass) {
|
616
|
+
// Create constructor
|
617
|
+
function CustomError(properties) {
|
518
618
|
Error.captureStackTrace(this, this.constructor);
|
519
|
-
this
|
619
|
+
Object.assign(this, properties || {});
|
620
|
+
this.code = code;
|
621
|
+
this.message = this.cause ? message + ": " + this.cause.message : message;
|
520
622
|
}
|
521
|
-
|
522
|
-
|
523
|
-
CustomError.prototype
|
524
|
-
CustomError.prototype
|
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
|
+
});
|
525
636
|
return CustomError;
|
526
637
|
}
|
527
638
|
|
528
|
-
function
|
529
|
-
for (var
|
530
|
-
request.removeListener(
|
639
|
+
function destroyRequest(request, error) {
|
640
|
+
for (var event of events) {
|
641
|
+
request.removeListener(event, eventHandlers[event]);
|
531
642
|
}
|
532
643
|
request.on("error", noop);
|
533
|
-
request.
|
644
|
+
request.destroy(error);
|
645
|
+
}
|
646
|
+
|
647
|
+
function isSubdomain(subdomain, domain) {
|
648
|
+
assert(isString(subdomain) && isString(domain));
|
649
|
+
var dot = subdomain.length - domain.length - 1;
|
650
|
+
return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
|
651
|
+
}
|
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;
|
534
667
|
}
|
535
668
|
|
536
669
|
// Exports
|
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",
|