Socket
Socket
Sign inDemoInstall

follow-redirects

Package Overview
Dependencies
2
Maintainers
3
Versions
65
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.6 to 1.3.0

2

http.js

@@ -1,1 +0,1 @@

module.exports = require('./').http;
module.exports = require("./").http;

@@ -1,1 +0,1 @@

module.exports = require('./').https;
module.exports = require("./").https;

@@ -1,24 +0,18 @@

'use strict';
var url = require('url');
var assert = require('assert');
var http = require('http');
var https = require('https');
var Writable = require('stream').Writable;
var debug = require('debug')('follow-redirects');
var url = require("url");
var http = require("http");
var https = require("https");
var assert = require("assert");
var Writable = require("stream").Writable;
var debug = require("debug")("follow-redirects");
var nativeProtocols = {'http:': http, 'https:': https};
var schemes = {};
var exports = module.exports = {
maxRedirects: 21
};
// RFC7231§4.2.1: Of the request methods defined by this specification,
// the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe.
var safeMethods = {GET: true, HEAD: true, OPTIONS: true, TRACE: true};
var SAFE_METHODS = { GET: true, HEAD: true, OPTIONS: true, TRACE: true };
// Create handlers that pass events from native requests
var eventHandlers = Object.create(null);
['abort', 'aborted', 'error', 'socket'].forEach(function (event) {
eventHandlers[event] = function (arg) {
this._redirectable.emit(event, arg);
};
["abort", "aborted", "error", "socket", "timeout"].forEach(function (event) {
eventHandlers[event] = function (arg) {
this._redirectable.emit(event, arg);
};
});

@@ -28,32 +22,34 @@

function RedirectableRequest(options, responseCallback) {
// Initialize the request
Writable.call(this);
this._options = options;
this._redirectCount = 0;
this._bufferedWrites = [];
// Initialize the request
Writable.call(this);
this._options = options;
this._redirectCount = 0;
this._requestBodyLength = 0;
this._requestBodyBuffers = [];
// Attach a callback if passed
if (responseCallback) {
this.on('response', responseCallback);
}
// Attach a callback if passed
if (responseCallback) {
this.on("response", responseCallback);
}
// React to responses of native requests
var self = this;
this._onNativeResponse = function (response) {
self._processResponse(response);
};
// React to responses of native requests
var self = this;
this._onNativeResponse = function (response) {
self._processResponse(response);
};
// Complete the URL object when necessary
if (!options.pathname && options.path) {
var searchPos = options.path.indexOf('?');
if (searchPos < 0) {
options.pathname = options.path;
} else {
options.pathname = options.path.substring(0, searchPos);
options.search = options.path.substring(searchPos);
}
}
// Complete the URL object when necessary
if (!options.pathname && options.path) {
var searchPos = options.path.indexOf("?");
if (searchPos < 0) {
options.pathname = options.path;
}
else {
options.pathname = options.path.substring(0, searchPos);
options.search = options.path.substring(searchPos);
}
}
// Perform the first request
this._performRequest();
// Perform the first request
this._performRequest();
}

@@ -64,44 +60,42 @@ RedirectableRequest.prototype = Object.create(Writable.prototype);

RedirectableRequest.prototype._performRequest = function () {
// If specified, use the agent corresponding to the protocol
// (HTTP and HTTPS use different types of agents)
var protocol = this._options.protocol;
if (this._options.agents) {
this._options.agent = this._options.agents[schemes[protocol]];
}
// Load the native protocol
var protocol = this._options.protocol;
var nativeProtocol = this._options.nativeProtocols[protocol];
// Create the native request
var nativeProtocol = nativeProtocols[protocol];
var request = this._currentRequest =
nativeProtocol.request(this._options, this._onNativeResponse);
this._currentUrl = url.format(this._options);
// If specified, use the agent corresponding to the protocol
// (HTTP and HTTPS use different types of agents)
if (this._options.agents) {
var scheme = protocol.substr(0, protocol.length - 1);
this._options.agent = this._options.agents[scheme];
}
// Set up event handlers
request._redirectable = this;
for (var event in eventHandlers) {
/* istanbul ignore else */
if (event) {
request.on(event, eventHandlers[event]);
}
}
// Create the native request
var request = this._currentRequest =
nativeProtocol.request(this._options, this._onNativeResponse);
this._currentUrl = url.format(this._options);
// End a redirected request
// (The first request must be ended explicitly with RedirectableRequest#end)
if (this._isRedirect) {
// If the request doesn't have en entity, end directly.
var bufferedWrites = this._bufferedWrites;
if (bufferedWrites.length === 0) {
request.end();
// Otherwise, write the request entity and end afterwards.
} else {
var i = 0;
(function writeNext() {
if (i < bufferedWrites.length) {
var bufferedWrite = bufferedWrites[i++];
request.write(bufferedWrite.data, bufferedWrite.encoding, writeNext);
} else {
request.end();
}
})();
}
}
// Set up event handlers
request._redirectable = this;
for (var event in eventHandlers) {
/* istanbul ignore else */
if (event) {
request.on(event, eventHandlers[event]);
}
}
// End a redirected request
// (The first request must be ended explicitly with RedirectableRequest#end)
if (this._isRedirect) {
// Write the request entity and end.
var requestBodyBuffers = this._requestBodyBuffers;
(function writeNext() {
if (requestBodyBuffers.length !== 0) {
var buffer = requestBodyBuffers.pop();
request.write(buffer.data, buffer.encoding, writeNext);
}
else {
request.end();
}
}());
}
};

@@ -111,61 +105,63 @@

RedirectableRequest.prototype._processResponse = function (response) {
// RFC7231§6.4: The 3xx (Redirection) class of status code indicates
// that further action needs to be taken by the user agent in order to
// fulfill the request. If a Location header field is provided,
// the user agent MAY automatically redirect its request to the URI
// referenced by the Location field value,
// even if the specific status code is not understood.
var location = response.headers.location;
if (location && this._options.followRedirects !== false &&
response.statusCode >= 300 && response.statusCode < 400) {
// RFC7231§6.4: A client SHOULD detect and intervene
// in cyclical redirections (i.e., "infinite" redirection loops).
if (++this._redirectCount > this._options.maxRedirects) {
return this.emit('error', new Error('Max redirects exceeded.'));
}
// RFC7231§6.4: The 3xx (Redirection) class of status code indicates
// that further action needs to be taken by the user agent in order to
// fulfill the request. If a Location header field is provided,
// the user agent MAY automatically redirect its request to the URI
// referenced by the Location field value,
// even if the specific status code is not understood.
var location = response.headers.location;
if (location && this._options.followRedirects !== false &&
response.statusCode >= 300 && response.statusCode < 400) {
// RFC7231§6.4: A client SHOULD detect and intervene
// in cyclical redirections (i.e., "infinite" redirection loops).
if (++this._redirectCount > this._options.maxRedirects) {
this.emit("error", new Error("Max redirects exceeded."));
return;
}
// RFC7231§6.4: Automatic redirection needs to done with
// care for methods not known to be safe […],
// since the user might not wish to redirect an unsafe request.
// RFC7231§6.4.7: The 307 (Temporary Redirect) status code indicates
// that the target resource resides temporarily under a different URI
// and the user agent MUST NOT change the request method
// if it performs an automatic redirection to that URI.
var header;
var headers = this._options.headers;
if (response.statusCode !== 307 && !(this._options.method in safeMethods)) {
this._options.method = 'GET';
// Drop a possible entity and headers related to it
this._bufferedWrites = [];
for (header in headers) {
if (/^content-/i.test(header)) {
delete headers[header];
}
}
}
// RFC7231§6.4: Automatic redirection needs to done with
// care for methods not known to be safe […],
// since the user might not wish to redirect an unsafe request.
// RFC7231§6.4.7: The 307 (Temporary Redirect) status code indicates
// that the target resource resides temporarily under a different URI
// and the user agent MUST NOT change the request method
// if it performs an automatic redirection to that URI.
var header;
var headers = this._options.headers;
if (response.statusCode !== 307 && !(this._options.method in SAFE_METHODS)) {
this._options.method = "GET";
// Drop a possible entity and headers related to it
this._requestBodyBuffers = [];
for (header in headers) {
if (/^content-/i.test(header)) {
delete headers[header];
}
}
}
// Drop the Host header, as the redirect might lead to a different host
if (!this._isRedirect) {
for (header in headers) {
if (/^host$/i.test(header)) {
delete headers[header];
}
}
}
// Drop the Host header, as the redirect might lead to a different host
if (!this._isRedirect) {
for (header in headers) {
if (/^host$/i.test(header)) {
delete headers[header];
}
}
}
// Perform the redirected request
var redirectUrl = url.resolve(this._currentUrl, location);
debug('redirecting to', redirectUrl);
Object.assign(this._options, url.parse(redirectUrl));
this._isRedirect = true;
this._performRequest();
} else {
// The response is not a redirect; return it as-is
response.responseUrl = this._currentUrl;
this.emit('response', response);
// Perform the redirected request
var redirectUrl = url.resolve(this._currentUrl, location);
debug("redirecting to", redirectUrl);
Object.assign(this._options, url.parse(redirectUrl));
this._isRedirect = true;
this._performRequest();
}
else {
// The response is not a redirect; return it as-is
response.responseUrl = this._currentUrl;
this.emit("response", response);
// Clean up
delete this._options;
delete this._bufferedWrites;
}
// Clean up
delete this._options;
delete this._requestBodyBuffers;
}
};

@@ -175,3 +171,3 @@

RedirectableRequest.prototype.abort = function () {
this._currentRequest.abort();
this._currentRequest.abort();
};

@@ -181,3 +177,3 @@

RedirectableRequest.prototype.flushHeaders = function () {
this._currentRequest.flushHeaders();
this._currentRequest.flushHeaders();
};

@@ -187,3 +183,3 @@

RedirectableRequest.prototype.setNoDelay = function (noDelay) {
this._currentRequest.setNoDelay(noDelay);
this._currentRequest.setNoDelay(noDelay);
};

@@ -193,3 +189,3 @@

RedirectableRequest.prototype.setSocketKeepAlive = function (enable, initialDelay) {
this._currentRequest.setSocketKeepAlive(enable, initialDelay);
this._currentRequest.setSocketKeepAlive(enable, initialDelay);
};

@@ -199,3 +195,3 @@

RedirectableRequest.prototype.setTimeout = function (timeout, callback) {
this._currentRequest.setTimeout(timeout, callback);
this._currentRequest.setTimeout(timeout, callback);
};

@@ -205,4 +201,11 @@

RedirectableRequest.prototype.write = function (data, encoding, callback) {
this._currentRequest.write(data, encoding, callback);
this._bufferedWrites.push({data: data, encoding: encoding});
if (this._requestBodyLength + data.length <= this._options.maxBodyLength) {
this._requestBodyLength += data.length;
this._requestBodyBuffers.push({ data: data, encoding: encoding });
this._currentRequest.write(data, encoding, callback);
}
else {
this.emit("error", new Error("Request body larger than maxBodyLength limit"));
this.abort();
}
};

@@ -212,37 +215,59 @@

RedirectableRequest.prototype.end = function (data, encoding, callback) {
this._currentRequest.end(data, encoding, callback);
if (data) {
this._bufferedWrites.push({data: data, encoding: encoding});
}
var currentRequest = this._currentRequest;
if (!data) {
currentRequest.end(null, null, callback);
}
else {
this.write(data, encoding, function () {
currentRequest.end(null, null, callback);
});
}
};
// Export a redirecting wrapper for each native protocol
Object.keys(nativeProtocols).forEach(function (protocol) {
var scheme = schemes[protocol] = protocol.substr(0, protocol.length - 1);
var nativeProtocol = nativeProtocols[protocol];
var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
// Wraps the key/value object of protocols with redirect functionality
function wrap(protocols) {
// Default settings
var exports = {
maxRedirects: 21,
maxBodyLength: 10 * 1024 * 1024,
};
// Executes an HTTP request, following redirects
wrappedProtocol.request = function (options, callback) {
if (typeof options === 'string') {
options = url.parse(options);
options.maxRedirects = exports.maxRedirects;
} else {
options = Object.assign({
maxRedirects: exports.maxRedirects,
protocol: protocol
}, options);
}
assert.equal(options.protocol, protocol, 'protocol mismatch');
debug('options', options);
// Wrap each protocol
var nativeProtocols = {};
Object.keys(protocols).forEach(function (scheme) {
var protocol = scheme + ":";
var nativeProtocol = nativeProtocols[protocol] = protocols[scheme];
var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
return new RedirectableRequest(options, callback);
};
// Executes a request, following redirects
wrappedProtocol.request = function (options, callback) {
if (typeof options === "string") {
options = url.parse(options);
options.maxRedirects = exports.maxRedirects;
}
else {
options = Object.assign({
protocol: protocol,
maxRedirects: exports.maxRedirects,
maxBodyLength: exports.maxBodyLength,
}, options);
}
options.nativeProtocols = nativeProtocols;
assert.equal(options.protocol, protocol, "protocol mismatch");
debug("options", options);
return new RedirectableRequest(options, callback);
};
// Executes a GET request, following redirects
wrappedProtocol.get = function (options, callback) {
var request = wrappedProtocol.request(options, callback);
request.end();
return request;
};
});
// Executes a GET request, following redirects
wrappedProtocol.get = function (options, callback) {
var request = wrappedProtocol.request(options, callback);
request.end();
return request;
};
});
return exports;
}
// Exports
module.exports = wrap({ http: http, https: https });
module.exports.wrap = wrap;
{
"name": "follow-redirects",
"version": "1.2.6",
"version": "1.3.0",
"description": "HTTP and HTTPS modules that follow redirects.",

@@ -10,3 +10,5 @@ "main": "index.js",

"scripts": {
"test": "xo && BLUEBIRD_DEBUG=1 nyc mocha"
"test": "npm run lint && npm run mocha",
"lint": "eslint *.js test",
"mocha": "nyc mocha"
},

@@ -52,6 +54,6 @@ "repository": {

"coveralls": "^3.0.0",
"eslint": "^4.14.0",
"express": "^4.13.0",
"mocha": "^4.0.1",
"nyc": "^11.3.0",
"xo": "^0.17.1"
"nyc": "^11.3.0"
},

@@ -64,8 +66,3 @@ "license": "MIT",

]
},
"xo": {
"envs": [
"mocha"
]
}
}

@@ -8,3 +8,2 @@ ## Follow Redirects

[![Coverage Status](https://coveralls.io/repos/olalonde/follow-redirects/badge.svg?branch=master)](https://coveralls.io/r/olalonde/follow-redirects?branch=master)
[![Code Climate](https://codeclimate.com/github/olalonde/follow-redirects/badges/gpa.svg)](https://codeclimate.com/github/olalonde/follow-redirects)
[![Dependency Status](https://david-dm.org/olalonde/follow-redirects.svg)](https://david-dm.org/olalonde/follow-redirects)

@@ -52,2 +51,3 @@ [![devDependency Status](https://david-dm.org/olalonde/follow-redirects/dev-status.svg)](https://david-dm.org/olalonde/follow-redirects#info=devDependencies)

followRedirects.maxRedirects = 10;
followRedirects.maxBodyLength = 20 * 1024 * 1024; // 20 MB
```

@@ -59,3 +59,5 @@

- `maxBodyLength` (default: 10MB) – sets the maximum size of the request body; if exceeded, an error will be emitted.
### Per-request options

@@ -79,5 +81,23 @@ Per-request options are set by passing an `options` object:

- `maxBodyLength` (default: 10MB) – sets the maximum size of the request body; if exceeded, an error will be emitted.
- `agents` (default: `undefined`) – sets the `agent` option per protocol, since HTTP and HTTPS use different agents. Example value: `{ http: new http.Agent(), https: new https.Agent() }`
### Advanced usage
By default, `follow-redirects` will use the Node.js default implementations
of [`http`](https://nodejs.org/api/http.html)
and [`https`](https://nodejs.org/api/https.html).
To enable features such as caching and/or intermediate request tracking,
you might instead want to wrap `follow-redirects` around custom protocol implementations:
```javascript
var followRedirects = require('follow-redirects').wrap({
http: require('your-custom-http'),
https: require('your-custom-https'),
});
```
Such custom protocols only need an implementation of the `request` method.
## Browserify Usage

@@ -84,0 +104,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc