Add redirectSameOrigin

Some clients try to use CORS Anywhere, even for same-origin requests...
Add a new setting "redirectSameOrigin" to not waste server resources on
proxying such requests.

Fixes #42
This commit is contained in:
Rob Wu
2016-03-19 18:22:29 +01:00
parent bacbbe0cb0
commit e6695b8102
5 changed files with 109 additions and 0 deletions

View File

@@ -97,6 +97,9 @@ proxy requests. The following options are supported:
* array of strings `originWhitelist` - If set, requests whose origin is not listed are blocked.
If this list is empty, all origins are allowed.
Example: `['https://good.example.com', 'http://good.example.com']`
* boolean `redirectSameOrigin` - If true, requests to URLs from the same origin will not be proxied but redirected.
The primary purpose for this option is to save server resources by delegating the request to the client
(since same-origin requests should always succeed, even without proxying).
* array of strings `requireHeader` - If set, the request must include this header or the API will refuse to proxy.
Recommended if you want to prevent users from using the proxy for normal browsing.
Example: `['Origin', 'X-Requested-With']`.

View File

@@ -213,6 +213,7 @@ function getHandler(options, proxy) {
maxRedirects: 5, // Maximum number of redirects to be followed.
originBlacklist: [], // Requests from these origins will be blocked.
originWhitelist: [], // If non-empty, requests not from an origin in this list will be blocked.
redirectSameOrigin: false, // Redirect the client to the requested URL for same-origin requests.
requireHeader: null, // Require a header to be set?
removeHeaders: [], // Strip these request headers.
setHeaders: {}, // Set these request headers.
@@ -302,6 +303,17 @@ function getHandler(options, proxy) {
return;
}
if (corsAnywhere.redirectSameOrigin && origin && location.href[origin.length] === '/' &&
location.href.lastIndexOf(origin, 0) === 0) {
// Send a permanent redirect to offload the server. Badly coded clients should not waste our resources.
cors_headers.vary = 'origin';
cors_headers['cache-control'] = 'private';
cors_headers.location = location.href;
res.writeHead(301, 'Please use a direct request', cors_headers);
res.end();
return;
}
var isRequestedOverHttps = req.connection.encrypted || /^\s*https/.test(req.headers['x-forwarded-proto']);
var proxyBaseUrl = (isRequestedOverHttps ? 'https://' : 'http://') + req.headers.host;

View File

@@ -21,6 +21,7 @@ cors_proxy.createServer({
'x-heroku-dynos-in-use',
'x-request-start',
],
redirectSameOrigin: true,
httpProxyOptions: {
// Do not add X-Forwarded-For, etc. headers, because Heroku already adds it.
xfwd: false,

View File

@@ -102,6 +102,24 @@ nock('https://example.com')
.reply(200, 'Response from https://example.com')
;
nock('http://example.com.com')
.persist()
.get('/')
.reply(200, 'Response from example.com.com')
;
nock('http://example.com:1234')
.persist()
.get('/')
.reply(200, 'Response from example.com:1234')
;
nock('http://prefix.example.com')
.persist()
.get('/')
.reply(200, 'Response from prefix.example.com')
;
echoheaders('http://example.com');
echoheaders('http://example.com:1337');
echoheaders('https://example.com');

View File

@@ -505,6 +505,81 @@ describe('originWhitelist', function() {
});
});
describe('redirectSameOrigin', function() {
before(function() {
cors_anywhere = createServer({
redirectSameOrigin: true,
});
cors_anywhere_port = cors_anywhere.listen(0).address().port;
});
after(stopServer);
it('GET /example.com with Origin: http://example.com', function(done) {
request(cors_anywhere)
.get('/example.com/')
.set('Origin', 'http://example.com')
.expect('Access-Control-Allow-Origin', '*')
.expect('Cache-Control', 'private')
.expect('Vary', 'origin')
.expect('Location', 'http://example.com/')
.expect(301, done);
});
it('GET /example.com with Origin: https://example.com', function(done) {
// Not same-origin because of different schemes.
request(cors_anywhere)
.get('/example.com/')
.set('Origin', 'https://example.com')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from example.com', done);
});
it('GET /example.com with Origin: http://example.com:1234', function(done) {
// Not same-origin because of different ports.
request(cors_anywhere)
.get('/example.com/')
.set('Origin', 'http://example.com:1234')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from example.com', done);
});
it('GET /example.com:1234 with Origin: http://example.com', function(done) {
// Not same-origin because of different ports.
request(cors_anywhere)
.get('/example.com:1234/')
.set('Origin', 'http://example.com')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from example.com:1234', done);
});
it('GET /example.com with Origin: http://example.com.test', function(done) {
// Not same-origin because of different host names.
request(cors_anywhere)
.get('/example.com/')
.set('Origin', 'http://example.com.test')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from example.com', done);
});
it('GET /example.com.com with Origin: http://example.com', function(done) {
// Not same-origin because of different host names.
request(cors_anywhere)
.get('/example.com.com/')
.set('Origin', 'http://example.com')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from example.com.com', done);
});
it('GET /prefix.example.com with Origin: http://example.com', function(done) {
// Not same-origin because of different host names.
request(cors_anywhere)
.get('/prefix.example.com/')
.set('Origin', 'http://example.com')
.expect('Access-Control-Allow-Origin', '*')
.expect(200, 'Response from prefix.example.com', done);
});
});
describe('requireHeader', function() {
before(function() {
cors_anywhere = createServer({