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