mirror of
https://github.com/d0zingcat/cors-anywhere.git
synced 2026-05-13 15:09:25 +00:00
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:
47
README.md
47
README.md
@@ -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:
|
||||
|
||||
25
demo.html
25
demo.html
@@ -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);
|
||||
|
||||
@@ -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: {
|
||||
|
||||
12
lib/help.txt
12
lib/help.txt
@@ -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
|
||||
|
||||
@@ -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>",
|
||||
|
||||
Reference in New Issue
Block a user