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