Bind proxyRequest event handler once per proxy

Optimization: Create onProxyResponse handler only once,
and bind it only once per HttpProxy instance
(opposed to binding it for every request)
This commit is contained in:
Rob Wu
2013-08-28 19:48:52 +02:00
parent 96d237e7b3
commit d7a6564574

View File

@@ -84,12 +84,9 @@ function isForbidden(host) {
* @param req {ServerRequest} Incoming http request
* @param res {ServerResponse} Outgoing (proxied) http request
* @param proxy {HttpProxy}
* @param location {object} See parseURL
* @param requestState.proxyBaseUrl {string} Base URL of the CORS API endpoint.
* @param requestState.maxRedirects {number} Maximum number of redirects
* @param requestState.redirectCount_ {number} Internally used to count redirects
*/
function proxyRequest(req, res, proxy, location, requestState) {
function proxyRequest(req, res, proxy) {
var location = req.corsAnywhereRequestState.location;
if (isForbidden(location.hostname)) {
res.writeHead(403, 'Refused to visit', withCORS({'Location': location.full_url}, req));
return;
@@ -99,42 +96,6 @@ function proxyRequest(req, res, proxy, location, requestState) {
// Let the "Host" header be the host part of the path (including port, if specified).
req.headers.host = location.host;
// "Allow observer to modify headers or abort response"
// https://github.com/nodejitsu/node-http-proxy/blob/ebbba73e/lib/node-http-proxy/http-proxy.js#L321-L322
proxy.once('proxyResponse', function(req, res, response) {
var statusCode = response.statusCode;
// Handle redirects
if (statusCode === 301 || statusCode === 302 || statusCode === 303 || statusCode === 307 || statusCode === 308) {
var locationHeader = response.headers['location'];
if (locationHeader) {
locationHeader = url.resolve(location.full_url, locationHeader);
if (statusCode === 301 || statusCode === 302 || statusCode === 303) {
// Exclude 307 & 308, because they are rare, and require preserving the method + request body
requestState.redirectCount_ = requestState.requestState_ + 1 || 1;
if (requestState.redirectCount_ <= requestState.maxRedirects) {
req.method = 'GET';
proxyRequest(req, res, proxy, parseURL(locationHeader), requestState);
response.end();
// The proxyResponse event is wrapped in a try-catch, throwing an error
// prevents the response from being passed to the client.
throw new Error('Prevent current response from being passed through.');
}
}
response.headers['location'] = requestState.proxyBaseUrl + '/' + locationHeader;
}
}
withCORS(response.headers, req);
// Don't slip through cookies
delete response.headers['set-cookie'];
delete response.headers['set-cookie2'];
response.headers['x-request-url'] = location.full_url;
});
// Start proxying the request
proxy.proxyRequest(req, res, {
host: location.hostname,
@@ -143,9 +104,67 @@ function proxyRequest(req, res, proxy, location, requestState) {
https: location.isHttps
}
});
if (requestState.redirectCount_) {
req.emit('end');
}
/**
* "Allow observer to modify headers or abort response"
* https://github.com/nodejitsu/node-http-proxy/blob/ebbba73e/lib/node-http-proxy/http-proxy.js#L321-L322
*
* This method modifies the response headers of the proxied response.
* If a redirect is detected, the response is not sent to the client,
* and a new request is initiated.
*
* @param req {IncomingMessage} Incoming HTTP request, augmented with property corsAnywhereRequestState
* @param req.corsAnywhereRequestState {object}
* @param req.corsAnywhereRequestState.location {object} See parseURL
* @param req.corsAnywhereRequestState.proxyBaseUrl {string} Base URL of the CORS API endpoint
* @param req.corsAnywhereRequestState.maxRedirects {number} Maximum number of redirects
* @param req.corsAnywhereRequestState.redirectCount_ {number} Internally used to count redirects
* @param res {ServerResponse} Outgoing (proxied) HTTP request
* @param response {ClientRequest} The
*
* @this {HttpProxy}
*/
function onProxyResponse(req, res, response) {
/* jshint validthis:true */
var proxy = this;
var requestState = req.corsAnywhereRequestState;
var statusCode = response.statusCode;
// Handle redirects
if (statusCode === 301 || statusCode === 302 || statusCode === 303 || statusCode === 307 || statusCode === 308) {
var locationHeader = response.headers['location'];
if (locationHeader) {
locationHeader = url.resolve(requestState.location.full_url, locationHeader);
if (statusCode === 301 || statusCode === 302 || statusCode === 303) {
// Exclude 307 & 308, because they are rare, and require preserving the method + request body
requestState.redirectCount_ = requestState.requestState_ + 1 || 1;
if (requestState.redirectCount_ <= requestState.maxRedirects) {
req.method = 'GET';
requestState.location = parseURL(locationHeader);
proxyRequest(req, res, proxy);
// TODO: Is it possible to trigger reverseProxy.end() (from node-http-proxy) without
// manually emiting the "event" event on req?
req.emit('end');
response.end();
// The proxyResponse event is wrapped in a try-catch, throwing an error
// prevents the response from being passed to the client.
throw new Error('Prevent current response from being passed through.');
}
}
response.headers['location'] = requestState.proxyBaseUrl + '/' + locationHeader;
}
}
withCORS(response.headers, req);
// Don't slip through cookies
delete response.headers['set-cookie'];
delete response.headers['set-cookie2'];
response.headers['x-request-url'] = requestState.location.full_url;
}
@@ -265,10 +284,19 @@ var getHandler = exports.getHandler = function(options) {
delete req.headers[header];
});
proxyRequest(req, res, proxy, location, {
req.corsAnywhereRequestState = {
location: location,
maxRedirects: corsAnywhere.maxRedirects,
proxyBaseUrl: proxyBaseUrl
});
};
if (!proxy.hasCorsAnywhereResponseHandler) { // Runs once per HttpProxy instance
proxy.on('proxyResponse', onProxyResponse);
proxy.hasCorsAnywhereResponseHandler = true;
}
proxyRequest(req, res, proxy);
};
};