rail
Version:
An enhanced HTTP/RESTful API Client
169 lines (135 loc) • 4.45 kB
JavaScript
;
var path = require('path');
var tools = require('../tools');
function RedirectPlugin(rail, options) {
if (!(this instanceof RedirectPlugin)) {
return new RedirectPlugin(rail, options);
}
this._rail = rail;
this._interceptrequest = null;
this._interceptresponse = null;
this.codes = options.codes || [301, 302, 308];
this.limit = options.limit || 1;
this.sameHost = options.sameHost || false;
this.allowUpgrade = options.allowUpgrade === false ? false : true;
this.allowDowngrade = options.allowDowngrade || false;
this._setup();
}
module.exports = RedirectPlugin;
RedirectPlugin.prototype._setup = function() {
var self = this;
var rail = this._rail;
rail.on('plugin-configure', function(call, options) {
self._configure(options);
});
rail.on('plugin-response', function(call, options, response) {
// check if request has been redirected
if (options.request.method === 'GET' &&
response.headers.location &&
self.codes.indexOf(response.statusCode) > -1) {
options.redirect = options.redirect || {};
if (options.redirect.limit > 0) {
--options.redirect.limit;
// intercept to give other plugins a chance to do their work
// as we are going to kill the response in the intercept
call.__intercept('response', self._interceptresponse);
} else {
call.emit('warn', 'redirect', 'blocked', 'limit reached');
}
}
});
this._interceptrequest = function(call, options, request) {
// blackhole
};
this._interceptresponse = function(call, options, response) {
self._interceptResponse(call, options, response);
};
};
RedirectPlugin.prototype._interceptResponse = function(call, options, response) {
var self = this;
var p, url, configuration;
var location = response.headers.location;
if (!location) {
return call.__emit('response', response);
}
// create a new request configuration on the stack
p = location.indexOf('://');
if (p > 0) {
url = tools.parseURL(location);
if (url.host !== options.request.host &&
options.redirect.sameHost === true) {
call.emit('warn', 'redirect', 'blocked', 'different host');
return call.__emit('response', response);
} else if (options.proto !== url.proto) {
if (url.proto === 'http' &&
options.redirect.allowDowngrade === false) {
call.emit('warn', 'redirect', 'blocked', 'protocol downgrade');
return call.__emit('response', response);
} else if (options.proto === 'http' &&
options.redirect.allowUpgrade === false) {
call.emit('warn', 'redirect', 'blocked', 'protocol upgrade');
return call.__emit('response', response);
}
}
configuration = call.__configure({
proto: url.proto,
host: url.host,
port: url.port,
path: url.path
});
} else {
configuration = call.__configure({
path: path.resolve(options.request.path, location)
});
}
function finish() {
call.__clear();
call.__request(function(err2, request) {
if (request) {
call.__intercept('request', self._interceptrequest);
request.end();
call.emit('redirect', configuration, response);
} else {
call.__emit('response', response);
}
});
}
// check if buffer plugin already handled the response body
if (response.buffer !== undefined) {
finish();
} else {
// dump obsolete response
response.on('readable', function() {
response.read();
});
// send next request
response.on('end', finish);
}
};
RedirectPlugin.prototype._configure = function(options) {
if (options.redirect) {
if (options.redirect.limit === undefined) {
options.redirect.limit = this.limit;
}
if (options.redirect.sameHost === undefined) {
options.redirect.sameHost = this.sameHost;
}
if (options.redirect.allowDowngrade === undefined) {
options.redirect.allowDowngrade = this.allowDowngrade;
}
if (options.redirect.allowUpgrade === undefined) {
options.redirect.allowUpgrade = this.allowUpgrade;
}
} else if (options.redirect === false) {
options.redirect = {
limit: 0
};
} else {
options.redirect = {
limit: this.limit,
sameHost: this.sameHost,
allowDowngrade: this.allowDowngrade,
allowUpgrade: this.allowUpgrade
};
}
};