Remove manual redirect handling

From now on, redirects will automatically be handled by the browser.
Using the API by clients has become extremely easy.

Included JavaScript / jQuery snippets in the documentation to
demonstrate that it's easy to use the API.
This commit is contained in:
Rob Wu
2013-08-27 18:51:04 +02:00
parent 61d55ae41e
commit 9410ff5afa
5 changed files with 57 additions and 52 deletions

View File

@@ -9,9 +9,6 @@ cookies. Requesting [user credentials](http://www.w3.org/TR/cors/#user-credentia
The app can be configured to require a header for proxying a request, for example to avoid
a direct visit from the browser.
Redirects are not automatically followed. Instead, the server replies with http status code 333 and
includes an absolute URL in the `Location` response header.
The package also includes a Procfile, to run the app on Heroku. More information about
Heroku can be found at https://devcenter.heroku.com/articles/nodejs.
@@ -43,13 +40,45 @@ Live examples:
* https://cors-anywhere.herokuapp.com/
* https://robwu.nl/cors-anywhere.html - This demo shows how to use the API.
Includes a redirect handler (including loop detection) and shows that the POST also works.
## Documentation
### Client
Learn how to use the API in a web app by viewing the source code of [demo.html](demo.html) and reading [lib/help.txt](lib/help.txt).
To use the API, just prefix the URL with the API URL. Take a look at [demo.html](demo.html) for an example.
A concise summary of the documentation is provided at [lib/help.txt](lib/help.txt).
If you want to automatically enable cross-domain requests when needed, use the following snippet:
```javascript
(function() {
var cors_api_host = 'cors-anywhere.herokuapp.com/';
var cors_api_url = (window.location.protocol === 'http:' ? 'http://' : 'https://') + cors_api_host;
var slice = [].slice;
var origin = window.location.protocol + '//' + window.location.host;
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
var args = slice.call(arguments);
var targetOrigin = /^https?:\/\/([^\/]+)/i.exec(args[1]);
if (targetOrigin && targetOrigin[0].toLowerCase() !== origin &&
targetOrigin[1] !== cors_api_host) {
args[1] = cors_api_url + args[1];
}
return open.apply(this, args);
};
})();
```
If you're using jQuery, you can also use the following code **instead of** the previous one:
```javascript
jQuery.ajaxPrefilter(function(options) {
if (options.crossDomain && jQuery.support.cors) {
options.url = (window.location.protocol === 'http:' ? 'http:' : 'https:') +
'//cors-anywhere.herokuapp.com/' + options.url;
}
});
```
### Server
@@ -60,9 +89,11 @@ The module exports two properties: `getHandler` and `createServer`.
* `createServer(options)` creates a server with the default handler.
The following options are recognized by both methods:
* 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']`.
* array of lowercase strings `removeHeaders` - Exclude certain headers from being included in the request.
* 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']`.
* array of lowercase strings `removeHeaders` - Exclude certain headers from being included in the request.
Example: `["cookie"]`
`createServer` recognizes the following option as well:

View File

@@ -66,34 +66,13 @@ textarea {
<script>
var protocol = location.protocol === 'http:' ? 'http:' : 'https:';
var cors_api_url = protocol + '//cors-anywhere.herokuapp.com/';
var cors_api_redirect_cache = {};
function doCORSRequest(options, redirectCount) {
var x = new XMLHttpRequest();
x.open(options.method, cors_api_url + options.url);
x.onload = function() {
if (x.status === 333) {
redirectCount = +redirectCount ? +redirectCount + 1 : 1;
var redirectInfo = /^(\d+) (.*)$/.exec(x.statusText);
if (redirectInfo && redirectCount <= 5) {
var originalStatus = +redirectInfo[1];
var url = redirectInfo[2];
if (originalStatus === 307 || originalStatus === 308) {
// Correctly deal with method-preserving redirects
options.url = url;
doCORSRequest(options, redirectCount);
} else {
// Otherwise just change to GET
doCORSRequest({
method: 'GET',
url: url,
success: options.success,
error: options.error
}, redirectCount);
}
} else {
options.error(options, x.status, x.statusText);
}
} else if (x.status >= 200 && x.status < 300 || x.status === 304) {
if (x.status >= 200 && x.status < 300 || x.status === 304) {
options.success(options, x.status, x.statusText, x.responseText);
} else {
options.error(options, x.status, x.statusText);

View File

@@ -64,11 +64,16 @@ function withCORS(headers, request) {
function isForbidden(host) {
return false; // TODO
}
function proxyRequest(req, res, proxy, full_url, proxyOptions) {
function proxyRequest(req, res, proxy, full_url, isRequestedOverHttps, proxyOptions) {
if (isForbidden(proxyOptions.host)) {
res.writeHead(403, 'Refused to visit', withCORS({'Location': full_url}, req));
return;
}
var realHost = req.headers.host;
// Let the "Host" header be the host part of the path (including port, if specified).
req.headers.host = full_url.split('/', 3)[2];
// Hook res.writeHead
var res_writeHead = res.writeHead;
@@ -84,13 +89,10 @@ function proxyRequest(req, res, proxy, full_url, proxyOptions) {
if (statusCode === 301 || statusCode === 302 || statusCode === 303 || statusCode === 307 || statusCode === 308) {
var locationHeader = headers['location'] || res.getHeader('location');
if (locationHeader) {
headers['location'] = url.resolve(full_url, locationHeader);
res.removeHeader('location');
headers['location'] = (isRequestedOverHttps ? 'https://' : 'http://') + realHost + '/' +
url.resolve(full_url, locationHeader);
}
// Put redirect URL in status text so that user agents that do not recognize the Access-Control-Expose-Headers
// response header can still read the target URL.
reasonPhrase = statusCode + ' ' + (headers['location'] || '');
// Don't use 301 or 302 because browsers may cancel the request (observed in Chrome with a custom request header)
statusCode = 333;
}
// Don't slip through cookies
@@ -202,14 +204,13 @@ var getHandler = exports.getHandler = function(options) {
// Change the requested path:
req.url = path;
var isRequestedOverHttps = req.connection.encrypted || /^\s*https/.test(req.headers['x-forwarded-proto']);
corsAnywhere.removeHeaders.forEach(function(header) {
delete req.headers[header];
});
// Only add port if it was explicitly set
req.headers.host = hostname + (match[4] ? ':' + port : '');
proxyRequest(req, res, proxy, full_url, {
proxyRequest(req, res, proxy, full_url, isRequestedOverHttps, {
host: hostname,
port: port,
target: {

View File

@@ -10,13 +10,6 @@ If the protocol is omitted, it defaults to http (https if port 443 is specified)
Cookies are disabled and stripped from requests.
Redirects are not automatically followed: The API response has status code 333.
The client ought to confirm this redirection by creating a new request (the url
is available in the Location response header).
For user agents who do not support the Access-Control-Expose-Headers response header,
the information is available in the status text as "<HTTP STATUS CODE> <LOCATION HEADER>".
The requested URL is available in the X-Request-URL response header. Non-existence of this
header implies that the requested URL was not recognized.
@@ -25,5 +18,6 @@ or the X-Requested-With header to be set. To avoid unnecessary preflight (OPTION
it's recommended to not manually set these headers in your code.
Demo : https://robwu.nl/cors-anywhere.html
Source code : https://github.com/Rob--W/cors-anywhere/
Demo : https://robwu.nl/cors-anywhere.html
Source code : https://github.com/Rob--W/cors-anywhere/
Documentation : https://github.com/Rob--W/cors-anywhere/#documentation

View File

@@ -1,6 +1,6 @@
{
"name": "cors-anywhere",
"version": "0.1.5",
"version": "0.1.6",
"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 <gwnRob@gmail.com>",