From bd9af5849fb2298f06b7693964806839e1bfcf3f Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Mon, 25 Aug 2014 13:39:25 +0200 Subject: [PATCH] Upgrade from http-proxy 0.10.x to 1.3.0 --- lib/cors-anywhere.js | 89 ++++++++++++++++++++------------------------ package.json | 4 +- server.js | 6 +-- 3 files changed, 45 insertions(+), 54 deletions(-) diff --git a/lib/cors-anywhere.js b/lib/cors-anywhere.js index e294ead..2948afd 100644 --- a/lib/cors-anywhere.js +++ b/lib/cors-anywhere.js @@ -1,9 +1,10 @@ -// © 2013 Rob W +// © 2013 - 2014 Rob Wu // Released under the MIT license 'use strict'; /* jshint node:true, eqnull:true, sub:true, quotmark:single, unused:true */ +var http = require('http'); var httpProxy = require('http-proxy'); var net = require('net'); var url = require('url'); @@ -37,7 +38,7 @@ function showUsage(headers, response) { * @return {boolean} Whether the requested resource can be accessed. */ function isValidHostName(hostname) { - return !( + return !!( regexp_tld.test(hostname) || net.isIPv4(hostname) || net.isIPv6(hostname) @@ -85,24 +86,20 @@ function proxyRequest(req, res, proxy) { req.headers.host = location.host; // Start proxying the request - proxy.proxyRequest(req, res, { - host: location.hostname, - port: location.port, - target: { - https: location.isHttps - } + proxy.web(req, res, { + target: location }); } - /** * "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 + * https://github.com/nodejitsu/node-http-proxy/blob/05f0b891/lib/http-proxy/passes/web-incoming.js#L147 * * 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 response {ClientRequest} The response of the proxied request * @param req {IncomingMessage} Incoming HTTP request, augmented with property corsAnywhereRequestState * @param req.corsAnywhereRequestState {object} * @param req.corsAnywhereRequestState.location {object} See parseURL @@ -110,11 +107,10 @@ function proxyRequest(req, res, proxy) { * @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) { +function onProxyResponse(response, req, res) { /* jshint validthis:true */ var proxy = this; var requestState = req.corsAnywhereRequestState; @@ -145,28 +141,25 @@ function onProxyResponse(req, res, response) { requestState.location = parseURL(locationHeader); // ### Dispose the current proxied request - // Verified assumption: When proxy.proxyRequest is called for the first time, - // there are no event listeners on the "req" object. - - // First remove the "end" event, to avoid the req.end() call by node-http-proxy/http-proxy - // https://github.com/nodejitsu/node-http-proxy/blob/ebbba73e/lib/node-http-proxy/http-proxy.js#L310-319 - response.removeAllListeners('end'); - // Trigger disposal of the reverseProxy - // https://github.com/nodejitsu/node-http-proxy/blob/ebbba73e/lib/node-http-proxy/http-proxy.js#L375-L378 - req.emit('aborted'); - // Remove all listeners (=events reset to initial state) - req.removeAllListeners(); - - // Initiate a new proxy request. - proxyRequest(req, res, proxy); - // Trigger reverseProxy.end() to initiate the proxy - // The event listener is added at the end of HttpProxy.prototype.proxyRequest, synchronously. - // https://github.com/nodejitsu/node-http-proxy/blob/ebbba73e/lib/node-http-proxy/http-proxy.js#L407-L415 - req.emit('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.'); + // Haha - hack! This should be fixed when (if?) node-http-proxy supports cancelation of requests.. + // Shadow all methods that mutate the |res| object. + // See https://github.com/nodejitsu/node-http-proxy/blob/05f0b891/lib/http-proxy/passes/web-outgoing.js + var setHeader = res.setHeader; + var writeHead = res.writeHead; + res.setHeader = res.writeHead = function noop() {}; + response.on = function noop2() {}; + response.pipe = function(res) { + res.setHeader = setHeader; + res.writeHead = writeHead; + // Trigger proxyReq.abort() (this is not of any imporance, it's just used to stop wasting resources.) + // https://github.com/nodejitsu/node-http-proxy/blob/05f0b891/lib/http-proxy/passes/web-incoming.js#L125-L128 + req.emit('aborted'); + // Remove all listeners (=reset events to initial state) + req.removeAllListeners(); + // Initiate a new proxy request. + proxyRequest(req, res, proxy); + }; + return; } } response.headers['location'] = requestState.proxyBaseUrl + '/' + locationHeader; @@ -214,7 +207,7 @@ function parseURL(req_url) { } // Request handler factory -var getHandler = exports.getHandler = function(options) { +var getHandler = exports.getHandler = function(options, proxy) { var corsAnywhere = { maxRedirects: 5, // Maximum number of redirects to be followed. requireHeader: null, // Require a header to be set? @@ -245,7 +238,7 @@ var getHandler = exports.getHandler = function(options) { }); }; - return function(req, res, proxy) { + return function(req, res) { var cors_headers = withCORS({}, req); if (req.method == 'OPTIONS') { // Pre-flight request. Reply successfully: @@ -278,7 +271,7 @@ var getHandler = exports.getHandler = function(options) { return; } - if (isValidHostName(location.hostname)) { + if (!isValidHostName(location.hostname)) { // Don't even try to proxy invalid hosts (such as /favicon.ico, /robots.txt) res.writeHead(404, 'Invalid host', cors_headers); res.end('Invalid host: ' + location.hostname); @@ -305,11 +298,6 @@ var getHandler = exports.getHandler = function(options) { proxyBaseUrl: proxyBaseUrl }; - if (!proxy.hasCorsAnywhereResponseHandler) { // Runs once per HttpProxy instance - proxy.on('proxyResponse', onProxyResponse); - proxy.hasCorsAnywhereResponseHandler = true; - } - proxyRequest(req, res, proxy); }; }; @@ -321,9 +309,7 @@ exports.createServer = function createServer(options) { // Default options: var httpProxyOptions = { - xforward: { - enable: true // Append X-Forwarded-* headers - } + xfwd: true, // Append X-Forwarded-* headers }; // Allow user to override defaults and add own options if (options.httpProxyOptions) { @@ -332,14 +318,21 @@ exports.createServer = function createServer(options) { }); } - var handler = getHandler(options); - var server = httpProxy.createServer(httpProxyOptions, handler); + var proxy = httpProxy.createServer(httpProxyOptions); + var server = http.createServer(getHandler(options, proxy)); // When the server fails, just show a 404 instead of Internal server error - server.proxy.on('proxyError', function(err, req, res) { + proxy.on('error', function(err, req, res) { + if (res._headerSent) { + // E.g. when the server replies with an invalid Content-Length value, + // causing the response to end as expected while triggering the + // "HPE_INVALID_CONSTANT" error. + return; + } res.writeHead(404, {'Access-Control-Allow-Origin': '*'}); res.end('Not found because of proxy error: ' + err); }); + proxy.on('proxyRes', onProxyResponse); return server; }; diff --git a/package.json b/package.json index a3b3f3d..e404916 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.9", "description": "CORS Anywhere is a reverse proxy which adds CORS headers to the proxied request. Request URL is taken from the path", "license": "MIT", - "author": "Rob Wu ", + "author": "Rob Wu ", "repository": { "type": "git", "url": "https://github.com/Rob--W/cors-anywhere.git" @@ -21,7 +21,7 @@ ], "main": "./lib/cors-anywhere.js", "dependencies": { - "http-proxy": "~0.10" + "http-proxy": "1.3.0" }, "devDependencies": { "webkit-devtools-agent": "~0.2.1" diff --git a/server.js b/server.js index 129fdf2..59f2902 100644 --- a/server.js +++ b/server.js @@ -15,10 +15,8 @@ cors_proxy.createServer({ 'x-request-start' ], httpProxyOptions: { - enable: { - // Do not add X-Forwarded-For, etc. headers, because Heroku already adds it. - xforward: false - } + // Do not add X-Forwarded-For, etc. headers, because Heroku already adds it. + xfwd: false } }).listen(port, host, function() { console.log('Running CORS Anywhere on ' + host + ':' + port);