diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 404abb22..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -coverage/ diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index c264cd3a..00000000 --- a/.eslintrc +++ /dev/null @@ -1,28 +0,0 @@ -{ - "env": { - "node": true - }, - "rules": { - "array-bracket-spacing": [2, "never"], - "block-scoped-var": 2, - "brace-style": [2, "1tbs", {"allowSingleLine": true}], - "comma-dangle": [2, "always-multiline"], - "computed-property-spacing": [2, "never"], - "curly": 2, - "eol-last": 2, - "eqeqeq": [2, "smart"], - "max-len": [1, 125], - "new-cap": 1, - "no-extend-native": 2, - "no-mixed-spaces-and-tabs": 2, - "no-trailing-spaces": 2, - "no-undef": 2, - "no-unused-vars": 1, - "no-use-before-define": [2, "nofunc"], - "object-curly-spacing": [2, "never"], - "quotes": [2, "single", "avoid-escape"], - "semi": [2, "always"], - "keyword-spacing": 2, - "space-unary-ops": 2 - } -} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b6be0ec3..00000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*.swp -*.tmp -*.log - -coverage/ -node_modules/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d253b993..00000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -node_js: - - 0.10 - - 4 - - 6 - - 7 - - 8 - - 9 - - 11 - - 12 - - 13 - - 13 - - 14 - - 15 -script: - - npm run lint - - npm run test - - npm run test-coverage && cat coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf coverage diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e5b2fc3f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM node:20 + +WORKDIR /usr/src/app + +COPY . . + +EXPOSE 3000 +CMD [ "node", "server.js" ] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e0e1c21d..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (C) 2013 - 2021 Rob Wu - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Procfile b/Procfile deleted file mode 100644 index 489b2700..00000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node server.js diff --git a/README.md b/README.md index ec7fd646..4613316a 100644 --- a/README.md +++ b/README.md @@ -1,191 +1,5 @@ -[![Build Status](https://travis-ci.com/Rob--W/cors-anywhere.svg?branch=master)](https://travis-ci.com/Rob--W/cors-anywhere) -[![Coverage Status](https://coveralls.io/repos/github/Rob--W/cors-anywhere/badge.svg?branch=master)](https://coveralls.io/github/Rob--W/cors-anywhere?branch=master) +# Quick fix for git.news -**CORS Anywhere** is a NodeJS proxy which adds CORS headers to the proxied request. +Mocking an empty response (for https://git.news) from reddit API since it became a paid api. -The url to proxy is literally taken from the path, validated and proxied. The protocol -part of the proxied URI is optional, and defaults to "http". If port 443 is specified, -the protocol defaults to "https". - -This package does not put any restrictions on the http methods or headers, except for -cookies. Requesting [user credentials](http://www.w3.org/TR/cors/#user-credentials) is disallowed. -The app can be configured to require a header for proxying a request, for example to avoid -a direct visit from the browser. - -## Example - -```javascript -// Listen on a specific host via the HOST environment variable -var host = process.env.HOST || '0.0.0.0'; -// Listen on a specific port via the PORT environment variable -var port = process.env.PORT || 8080; - -var cors_proxy = require('cors-anywhere'); -cors_proxy.createServer({ - originWhitelist: [], // Allow all origins - requireHeader: ['origin', 'x-requested-with'], - removeHeaders: ['cookie', 'cookie2'] -}).listen(port, host, function() { - console.log('Running CORS Anywhere on ' + host + ':' + port); -}); - -``` -Request examples: - -* `http://localhost:8080/http://google.com/` - Google.com with CORS headers -* `http://localhost:8080/google.com` - Same as previous. -* `http://localhost:8080/google.com:443` - Proxies `https://google.com/` -* `http://localhost:8080/` - Shows usage text, as defined in `libs/help.txt` -* `http://localhost:8080/favicon.ico` - Replies 404 Not found - -Live examples: - -* https://cors-anywhere.herokuapp.com/ -* https://robwu.nl/cors-anywhere.html - This demo shows how to use the API. - -## Documentation - -### Client - -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). - -**Note: as of February 2021, access to the demo server requires an opt-in**, -see: https://github.com/Rob--W/cors-anywhere/issues/301 - -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 = '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 = 'https://cors-anywhere.herokuapp.com/' + options.url; - } -}); -``` - -### Server - -The module exports `createServer(options)`, which creates a server that handles -proxy requests. The following options are supported: - -* function `getProxyForUrl` - If set, specifies which intermediate proxy to use for a given URL. - If the return value is void, a direct request is sent. The default implementation is - [`proxy-from-env`](https://github.com/Rob--W/proxy-from-env), which respects the standard proxy - environment variables (e.g. `https_proxy`, `no_proxy`, etc.). -* array of strings `originBlacklist` - If set, requests whose origin is listed are blocked. - Example: `['https://bad.example.com', 'http://bad.example.com']` -* 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']` -* function `handleInitialRequest` - If set, it is called with the request, response and a parsed - URL of the requested destination (null if unavailable). If the function returns true, the request - will not be handled further. Then the function is responsible for handling the request. - This feature can be used to passively monitor requests, for example for logging (return false). -* function `checkRateLimit` - If set, it is called with the origin (string) of the request. If this - function returns a non-empty string, the request is rejected and the string is send to the client. -* 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']`. -* array of lowercase strings `removeHeaders` - Exclude certain headers from being included in the request. - Example: `["cookie"]` -* dictionary of lowercase strings `setHeaders` - Set headers for the request (overwrites existing ones). - Example: `{"x-powered-by": "CORS Anywhere"}` -* number `corsMaxAge` - If set, an Access-Control-Max-Age request header with this value (in seconds) will be added. - Example: `600` - Allow CORS preflight request to be cached by the browser for 10 minutes. -* string `helpFile` - Set the help file (shown at the homepage). - Example: `"myCustomHelpText.txt"` - -For advanced users, the following options are also provided. - -* `httpProxyOptions` - Under the hood, [http-proxy](https://github.com/nodejitsu/node-http-proxy) - is used to proxy requests. Use this option if you really need to pass options - to http-proxy. The documentation for these options can be found [here](https://github.com/nodejitsu/node-http-proxy#options). -* `httpsOptions` - If set, a `https.Server` will be created. The given options are passed to the - [`https.createServer`](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener) method. - -For even more advanced usage (building upon CORS Anywhere), -see the sample code in [test/test-examples.js](test/test-examples.js). - -### Demo server - -A public demo of CORS Anywhere is available at https://cors-anywhere.herokuapp.com. This server is -only provided so that you can easily and quickly try out CORS Anywhere. To ensure that the service -stays available to everyone, the number of requests per period is limited, except for requests from -some explicitly whitelisted origins. - -**Note: as of February 2021, access to the demo server requires an opt-in**, -see: https://github.com/Rob--W/cors-anywhere/issues/301 - -If you expect lots of traffic, please host your own instance of CORS Anywhere, and make sure that -the CORS Anywhere server only whitelists your site to prevent others from using your instance of -CORS Anywhere as an open proxy. - -For instance, to run a CORS Anywhere server that accepts any request from some example.com sites on -port 8080, use: -``` -export PORT=8080 -export CORSANYWHERE_WHITELIST=https://example.com,http://example.com,http://example.com:8080 -node server.js -``` - -This application can immediately be run on Heroku, see https://devcenter.heroku.com/articles/nodejs -for instructions. Note that their [Acceptable Use Policy](https://www.heroku.com/policy/aup) forbids -the use of Heroku for operating an open proxy, so make sure that you either enforce a whitelist as -shown above, or severly rate-limit the number of requests. - -For example, to blacklist abuse.example.com and rate-limit everything to 50 requests per 3 minutes, -except for my.example.com and my2.example.com (which may be unlimited), use: - -``` -export PORT=8080 -export CORSANYWHERE_BLACKLIST=https://abuse.example.com,http://abuse.example.com -export CORSANYWHERE_RATELIMIT='50 3 my.example.com my2.example.com' -node server.js -``` - - -## License - -Copyright (C) 2013 - 2021 Rob Wu - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Keep in mind that the api still works on the mobile app: https://gitnews.learn.uno diff --git a/demo.html b/demo.html deleted file mode 100644 index 185c67a7..00000000 --- a/demo.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - -Demo of CORS Anywhere - - - - -
- CORS Anywhere demo • GithubLive server. - - - -
-
- -
- - - - diff --git a/lib/cors-anywhere.js b/lib/cors-anywhere.js deleted file mode 100644 index eb4f47c5..00000000 --- a/lib/cors-anywhere.js +++ /dev/null @@ -1,462 +0,0 @@ -// © 2013 - 2016 Rob Wu -// Released under the MIT license - -'use strict'; - -var httpProxy = require('http-proxy'); -var net = require('net'); -var url = require('url'); -var regexp_tld = require('./regexp-top-level-domain'); -var getProxyForUrl = require('proxy-from-env').getProxyForUrl; - -var help_text = {}; -function showUsage(help_file, headers, response) { - var isHtml = /\.html$/.test(help_file); - headers['content-type'] = isHtml ? 'text/html' : 'text/plain'; - if (help_text[help_file] != null) { - response.writeHead(200, headers); - response.end(help_text[help_file]); - } else { - require('fs').readFile(help_file, 'utf8', function(err, data) { - if (err) { - console.error(err); - response.writeHead(500, headers); - response.end(); - } else { - help_text[help_file] = data; - showUsage(help_file, headers, response); // Recursive call, but since data is a string, the recursion will end - } - }); - } -} - -/** - * Check whether the specified hostname is valid. - * - * @param hostname {string} Host name (excluding port) of requested resource. - * @return {boolean} Whether the requested resource can be accessed. - */ -function isValidHostName(hostname) { - return !!( - regexp_tld.test(hostname) || - net.isIPv4(hostname) || - net.isIPv6(hostname) - ); -} - -/** - * Adds CORS headers to the response headers. - * - * @param headers {object} Response headers - * @param request {ServerRequest} - */ -function withCORS(headers, request) { - headers['access-control-allow-origin'] = '*'; - var corsMaxAge = request.corsAnywhereRequestState.corsMaxAge; - if (request.method === 'OPTIONS' && corsMaxAge) { - headers['access-control-max-age'] = corsMaxAge; - } - if (request.headers['access-control-request-method']) { - headers['access-control-allow-methods'] = request.headers['access-control-request-method']; - delete request.headers['access-control-request-method']; - } - if (request.headers['access-control-request-headers']) { - headers['access-control-allow-headers'] = request.headers['access-control-request-headers']; - delete request.headers['access-control-request-headers']; - } - - headers['access-control-expose-headers'] = Object.keys(headers).join(','); - - return headers; -} - -/** - * Performs the actual proxy request. - * - * @param req {ServerRequest} Incoming http request - * @param res {ServerResponse} Outgoing (proxied) http request - * @param proxy {HttpProxy} - */ -function proxyRequest(req, res, proxy) { - var location = req.corsAnywhereRequestState.location; - req.url = location.path; - - var proxyOptions = { - changeOrigin: false, - prependPath: false, - target: location, - headers: { - host: location.host, - }, - // HACK: Get hold of the proxyReq object, because we need it later. - // https://github.com/nodejitsu/node-http-proxy/blob/v1.11.1/lib/http-proxy/passes/web-incoming.js#L144 - buffer: { - pipe: function(proxyReq) { - var proxyReqOn = proxyReq.on; - // Intercepts the handler that connects proxyRes to res. - // https://github.com/nodejitsu/node-http-proxy/blob/v1.11.1/lib/http-proxy/passes/web-incoming.js#L146-L158 - proxyReq.on = function(eventName, listener) { - if (eventName !== 'response') { - return proxyReqOn.call(this, eventName, listener); - } - return proxyReqOn.call(this, 'response', function(proxyRes) { - if (onProxyResponse(proxy, proxyReq, proxyRes, req, res)) { - try { - listener(proxyRes); - } catch (err) { - // Wrap in try-catch because an error could occur: - // "RangeError: Invalid status code: 0" - // https://github.com/Rob--W/cors-anywhere/issues/95 - // https://github.com/nodejitsu/node-http-proxy/issues/1080 - - // Forward error (will ultimately emit the 'error' event on our proxy object): - // https://github.com/nodejitsu/node-http-proxy/blob/v1.11.1/lib/http-proxy/passes/web-incoming.js#L134 - proxyReq.emit('error', err); - } - } - }); - }; - return req.pipe(proxyReq); - }, - }, - }; - - var proxyThroughUrl = req.corsAnywhereRequestState.getProxyForUrl(location.href); - if (proxyThroughUrl) { - proxyOptions.target = proxyThroughUrl; - proxyOptions.toProxy = true; - // If a proxy URL was set, req.url must be an absolute URL. Then the request will not be sent - // directly to the proxied URL, but through another proxy. - req.url = location.href; - } - - // Start proxying the request - try { - proxy.web(req, res, proxyOptions); - } catch (err) { - proxy.emit('error', err, req, res); - } -} - -/** - * 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. - * - * client (req) -> CORS Anywhere -> (proxyReq) -> other server - * client (res) <- CORS Anywhere <- (proxyRes) <- other server - * - * @param proxy {HttpProxy} - * @param proxyReq {ClientRequest} The outgoing request to the other server. - * @param proxyRes {ServerResponse} The response from the other server. - * @param req {IncomingMessage} Incoming HTTP request, augmented with property corsAnywhereRequestState - * @param req.corsAnywhereRequestState {object} - * @param req.corsAnywhereRequestState.location {object} See parseURL - * @param req.corsAnywhereRequestState.getProxyForUrl {function} See proxyRequest - * @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 response to the client that wanted to proxy the HTTP request. - * - * @returns {boolean} true if http-proxy should continue to pipe proxyRes to res. - */ -function onProxyResponse(proxy, proxyReq, proxyRes, req, res) { - var requestState = req.corsAnywhereRequestState; - - var statusCode = proxyRes.statusCode; - - if (!requestState.redirectCount_) { - res.setHeader('x-request-url', requestState.location.href); - } - // Handle redirects - if (statusCode === 301 || statusCode === 302 || statusCode === 303 || statusCode === 307 || statusCode === 308) { - var locationHeader = proxyRes.headers.location; - var parsedLocation; - if (locationHeader) { - locationHeader = url.resolve(requestState.location.href, locationHeader); - parsedLocation = parseURL(locationHeader); - } - if (parsedLocation) { - if (statusCode === 301 || statusCode === 302 || statusCode === 303) { - // Exclude 307 & 308, because they are rare, and require preserving the method + request body - requestState.redirectCount_ = requestState.redirectCount_ + 1 || 1; - if (requestState.redirectCount_ <= requestState.maxRedirects) { - // Handle redirects within the server, because some clients (e.g. Android Stock Browser) - // cancel redirects. - // Set header for debugging purposes. Do not try to parse it! - res.setHeader('X-CORS-Redirect-' + requestState.redirectCount_, statusCode + ' ' + locationHeader); - - req.method = 'GET'; - req.headers['content-length'] = '0'; - delete req.headers['content-type']; - requestState.location = parsedLocation; - - // Remove all listeners (=reset events to initial state) - req.removeAllListeners(); - - // Remove the error listener so that the ECONNRESET "error" that - // may occur after aborting a request does not propagate to res. - // https://github.com/nodejitsu/node-http-proxy/blob/v1.11.1/lib/http-proxy/passes/web-incoming.js#L134 - proxyReq.removeAllListeners('error'); - proxyReq.once('error', function catchAndIgnoreError() {}); - proxyReq.abort(); - - // Initiate a new proxy request. - proxyRequest(req, res, proxy); - return false; - } - } - proxyRes.headers.location = requestState.proxyBaseUrl + '/' + locationHeader; - } - } - - // Strip cookies - delete proxyRes.headers['set-cookie']; - delete proxyRes.headers['set-cookie2']; - - proxyRes.headers['x-final-url'] = requestState.location.href; - withCORS(proxyRes.headers, req); - return true; -} - - -/** - * @param req_url {string} The requested URL (scheme is optional). - * @return {object} URL parsed using url.parse - */ -function parseURL(req_url) { - var match = req_url.match(/^(?:(https?:)?\/\/)?(([^\/?]+?)(?::(\d{0,5})(?=[\/?]|$))?)([\/?][\S\s]*|$)/i); - // ^^^^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^ - // 1:protocol 3:hostname 4:port 5:path + query string - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // 2:host - if (!match) { - return null; - } - if (!match[1]) { - if (/^https?:/i.test(req_url)) { - // The pattern at top could mistakenly parse "http:///" as host="http:" and path=///. - return null; - } - // Scheme is omitted. - if (req_url.lastIndexOf('//', 0) === -1) { - // "//" is omitted. - req_url = '//' + req_url; - } - req_url = (match[4] === '443' ? 'https:' : 'http:') + req_url; - } - var parsed = url.parse(req_url); - if (!parsed.hostname) { - // "http://:1/" and "http:/notenoughslashes" could end up here. - return null; - } - return parsed; -} - -// Request handler factory -function getHandler(options, proxy) { - var corsAnywhere = { - handleInitialRequest: null, // Function that may handle the request instead, by returning a truthy value. - getProxyForUrl: getProxyForUrl, // Function that specifies the proxy to use - 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. - checkRateLimit: null, // Function that may enforce a rate-limit by returning a non-empty string. - 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. - corsMaxAge: 0, // If set, an Access-Control-Max-Age header with this value (in seconds) will be added. - helpFile: __dirname + '/help.txt', - }; - - Object.keys(corsAnywhere).forEach(function(option) { - if (Object.prototype.hasOwnProperty.call(options, option)) { - corsAnywhere[option] = options[option]; - } - }); - - // Convert corsAnywhere.requireHeader to an array of lowercase header names, or null. - if (corsAnywhere.requireHeader) { - if (typeof corsAnywhere.requireHeader === 'string') { - corsAnywhere.requireHeader = [corsAnywhere.requireHeader.toLowerCase()]; - } else if (!Array.isArray(corsAnywhere.requireHeader) || corsAnywhere.requireHeader.length === 0) { - corsAnywhere.requireHeader = null; - } else { - corsAnywhere.requireHeader = corsAnywhere.requireHeader.map(function(headerName) { - return headerName.toLowerCase(); - }); - } - } - var hasRequiredHeaders = function(headers) { - return !corsAnywhere.requireHeader || corsAnywhere.requireHeader.some(function(headerName) { - return Object.hasOwnProperty.call(headers, headerName); - }); - }; - - return function(req, res) { - req.corsAnywhereRequestState = { - getProxyForUrl: corsAnywhere.getProxyForUrl, - maxRedirects: corsAnywhere.maxRedirects, - corsMaxAge: corsAnywhere.corsMaxAge, - }; - - var cors_headers = withCORS({}, req); - if (req.method === 'OPTIONS') { - // Pre-flight request. Reply successfully: - res.writeHead(200, cors_headers); - res.end(); - return; - } - - var location = parseURL(req.url.slice(1)); - - if (corsAnywhere.handleInitialRequest && corsAnywhere.handleInitialRequest(req, res, location)) { - return; - } - - if (!location) { - // Special case http:/notenoughslashes, because new users of the library frequently make the - // mistake of putting this application behind a server/router that normalizes the URL. - // See https://github.com/Rob--W/cors-anywhere/issues/238#issuecomment-629638853 - if (/^\/https?:\/[^/]/i.test(req.url)) { - res.writeHead(400, 'Missing slash', cors_headers); - res.end('The URL is invalid: two slashes are needed after the http(s):.'); - return; - } - // Invalid API call. Show how to correctly use the API - showUsage(corsAnywhere.helpFile, cors_headers, res); - return; - } - - if (location.host === 'iscorsneeded') { - // Is CORS needed? This path is provided so that API consumers can test whether it's necessary - // to use CORS. The server's reply is always No, because if they can read it, then CORS headers - // are not necessary. - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.end('no'); - return; - } - - if (location.port > 65535) { - // Port is higher than 65535 - res.writeHead(400, 'Invalid port', cors_headers); - res.end('Port number too large: ' + location.port); - return; - } - - if (!/^\/https?:/.test(req.url) && !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); - return; - } - - if (!hasRequiredHeaders(req.headers)) { - res.writeHead(400, 'Header required', cors_headers); - res.end('Missing required request header. Must specify one of: ' + corsAnywhere.requireHeader); - return; - } - - var origin = req.headers.origin || ''; - if (corsAnywhere.originBlacklist.indexOf(origin) >= 0) { - res.writeHead(403, 'Forbidden', cors_headers); - res.end('The origin "' + origin + '" was blacklisted by the operator of this proxy.'); - return; - } - - if (corsAnywhere.originWhitelist.length && corsAnywhere.originWhitelist.indexOf(origin) === -1) { - res.writeHead(403, 'Forbidden', cors_headers); - res.end('The origin "' + origin + '" was not whitelisted by the operator of this proxy.'); - return; - } - - var rateLimitMessage = corsAnywhere.checkRateLimit && corsAnywhere.checkRateLimit(origin); - if (rateLimitMessage) { - res.writeHead(429, 'Too Many Requests', cors_headers); - res.end('The origin "' + origin + '" has sent too many requests.\n' + rateLimitMessage); - 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; - - corsAnywhere.removeHeaders.forEach(function(header) { - delete req.headers[header]; - }); - - Object.keys(corsAnywhere.setHeaders).forEach(function(header) { - req.headers[header] = corsAnywhere.setHeaders[header]; - }); - - req.corsAnywhereRequestState.location = location; - req.corsAnywhereRequestState.proxyBaseUrl = proxyBaseUrl; - - proxyRequest(req, res, proxy); - }; -} - -// Create server with default and given values -// Creator still needs to call .listen() -exports.createServer = function createServer(options) { - options = options || {}; - - // Default options: - var httpProxyOptions = { - xfwd: true, // Append X-Forwarded-* headers - secure: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0', - }; - // Allow user to override defaults and add own options - if (options.httpProxyOptions) { - Object.keys(options.httpProxyOptions).forEach(function(option) { - httpProxyOptions[option] = options.httpProxyOptions[option]; - }); - } - - var proxy = httpProxy.createServer(httpProxyOptions); - var requestHandler = getHandler(options, proxy); - var server; - if (options.httpsOptions) { - server = require('https').createServer(options.httpsOptions, requestHandler); - } else { - server = require('http').createServer(requestHandler); - } - - // When the server fails, just show a 404 instead of Internal server error - proxy.on('error', function(err, req, res) { - if (res.headersSent) { - // This could happen when a protocol error occurs when an error occurs - // after the headers have been received (and forwarded). Do not write - // the headers because it would generate an error. - // Prior to Node 13.x, the stream would have ended. - // As of Node 13.x, we must explicitly close it. - if (res.writableEnded === false) { - res.end(); - } - return; - } - - // When the error occurs after setting headers but before writing the response, - // then any previously set headers must be removed. - var headerNames = res.getHeaderNames ? res.getHeaderNames() : Object.keys(res._headers || {}); - headerNames.forEach(function(name) { - res.removeHeader(name); - }); - - res.writeHead(404, {'Access-Control-Allow-Origin': '*'}); - res.end('Not found because of proxy error: ' + err); - }); - - return server; -}; diff --git a/lib/help.txt b/lib/help.txt deleted file mode 100644 index 3d0d654f..00000000 --- a/lib/help.txt +++ /dev/null @@ -1,30 +0,0 @@ -This API enables cross-origin requests to anywhere. - -Usage: - -/ Shows help -/iscorsneeded This is the only resource on this host which is served without CORS headers. -/ Create a request to , and includes CORS headers in the response. - -If the protocol is omitted, it defaults to http (https if port 443 is specified). - -Cookies are disabled and stripped from requests. - -Redirects are automatically followed. For debugging purposes, each followed redirect results -in the addition of a X-CORS-Redirect-n header, where n starts at 1. These headers are not -accessible by the XMLHttpRequest API. -After 5 redirects, redirects are not followed any more. The redirect response is sent back -to the browser, which can choose to follow the redirect (handled automatically by the browser). - -The requested URL is available in the X-Request-URL response header. -The final URL, after following all redirects, is available in the X-Final-URL response header. - - -To prevent the use of the proxy for casual browsing, the API requires either the Origin -or the X-Requested-With header to be set. To avoid unnecessary preflight (OPTIONS) requests, -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/ -Documentation : https://github.com/Rob--W/cors-anywhere/#documentation diff --git a/lib/rate-limit.js b/lib/rate-limit.js deleted file mode 100644 index ae4616e7..00000000 --- a/lib/rate-limit.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; -module.exports = function createRateLimitChecker(CORSANYWHERE_RATELIMIT) { - // Configure rate limit. The following format is accepted for CORSANYWHERE_RATELIMIT: - // - // where is a space-separated list of strings or regexes (/.../) that - // matches the whole host (ports have to be listed explicitly if applicable). - // cannot be zero. - // - // Examples: - // - Allow any origin to make one request per 5 minutes: - // 1 5 - // - // - Allow example.com to make an unlimited number of requests, and the others 1 per 5 minutes. - // 1 5 example.com - // - // - Allow example.com, or any subdomain to make any number of requests and block the rest: - // 0 1 /(.*\.)?example\.com/ - // - // - Allow example.com and www.example.com, and block the rest: - // 0 1 example.com www.example.com - var rateLimitConfig = /^(\d+) (\d+)(?:\s*$|\s+(.+)$)/.exec(CORSANYWHERE_RATELIMIT); - if (!rateLimitConfig) { - // No rate limit by default. - return function checkRateLimit() {}; - } - var maxRequestsPerPeriod = parseInt(rateLimitConfig[1]); - var periodInMinutes = parseInt(rateLimitConfig[2]); - var unlimitedPattern = rateLimitConfig[3]; // Will become a RegExp or void. - if (unlimitedPattern) { - var unlimitedPatternParts = []; - unlimitedPattern.trim().split(/\s+/).forEach(function(unlimitedHost, i) { - var startsWithSlash = unlimitedHost.charAt(0) === '/'; - var endsWithSlash = unlimitedHost.slice(-1) === '/'; - if (startsWithSlash || endsWithSlash) { - if (unlimitedHost.length === 1 || !startsWithSlash || !endsWithSlash) { - throw new Error('Invalid CORSANYWHERE_RATELIMIT. Regex at index ' + i + - ' must start and end with a slash ("/").'); - } - unlimitedHost = unlimitedHost.slice(1, -1); - // Throws if the pattern is invalid. - new RegExp(unlimitedHost); - } else { - // Just escape RegExp characters even though they cannot appear in a host name. - // The only actual important escape is the dot. - unlimitedHost = unlimitedHost.replace(/[$()*+.?[\\\]^{|}]/g, '\\$&'); - } - unlimitedPatternParts.push(unlimitedHost); - }); - unlimitedPattern = new RegExp('^(?:' + unlimitedPatternParts.join('|') + ')$', 'i'); - } - - var accessedHosts = Object.create(null); - setInterval(function() { - accessedHosts = Object.create(null); - }, periodInMinutes * 60000); - - var rateLimitMessage = 'The number of requests is limited to ' + maxRequestsPerPeriod + - (periodInMinutes === 1 ? ' per minute' : ' per ' + periodInMinutes + ' minutes') + '. ' + - 'Please self-host CORS Anywhere if you need more quota. ' + - 'See https://github.com/Rob--W/cors-anywhere#demo-server'; - - return function checkRateLimit(origin) { - var host = origin.replace(/^[\w\-]+:\/\//i, ''); - if (unlimitedPattern && unlimitedPattern.test(host)) { - return; - } - var count = accessedHosts[host] || 0; - ++count; - if (count > maxRequestsPerPeriod) { - return rateLimitMessage; - } - accessedHosts[host] = count; - }; -}; diff --git a/lib/regexp-top-level-domain.js b/lib/regexp-top-level-domain.js deleted file mode 100644 index f7a6764b..00000000 --- a/lib/regexp-top-level-domain.js +++ /dev/null @@ -1,8 +0,0 @@ -// Based on http://data.iana.org/TLD/tlds-alpha-by-domain.txt -// '/\\.(?:' + document.body.firstChild.textContent.trim().split('\n').slice(1).join('|') + ')$/i'; - -/* eslint max-len:0 */ - -// # Version 2021031700, Last Updated Wed Mar 17 07:07:01 2021 UTC -var regexp = /\.(?:AAA|AARP|ABARTH|ABB|ABBOTT|ABBVIE|ABC|ABLE|ABOGADO|ABUDHABI|AC|ACADEMY|ACCENTURE|ACCOUNTANT|ACCOUNTANTS|ACO|ACTOR|AD|ADAC|ADS|ADULT|AE|AEG|AERO|AETNA|AF|AFAMILYCOMPANY|AFL|AFRICA|AG|AGAKHAN|AGENCY|AI|AIG|AIRBUS|AIRFORCE|AIRTEL|AKDN|AL|ALFAROMEO|ALIBABA|ALIPAY|ALLFINANZ|ALLSTATE|ALLY|ALSACE|ALSTOM|AM|AMAZON|AMERICANEXPRESS|AMERICANFAMILY|AMEX|AMFAM|AMICA|AMSTERDAM|ANALYTICS|ANDROID|ANQUAN|ANZ|AO|AOL|APARTMENTS|APP|APPLE|AQ|AQUARELLE|AR|ARAB|ARAMCO|ARCHI|ARMY|ARPA|ART|ARTE|AS|ASDA|ASIA|ASSOCIATES|AT|ATHLETA|ATTORNEY|AU|AUCTION|AUDI|AUDIBLE|AUDIO|AUSPOST|AUTHOR|AUTO|AUTOS|AVIANCA|AW|AWS|AX|AXA|AZ|AZURE|BA|BABY|BAIDU|BANAMEX|BANANAREPUBLIC|BAND|BANK|BAR|BARCELONA|BARCLAYCARD|BARCLAYS|BAREFOOT|BARGAINS|BASEBALL|BASKETBALL|BAUHAUS|BAYERN|BB|BBC|BBT|BBVA|BCG|BCN|BD|BE|BEATS|BEAUTY|BEER|BENTLEY|BERLIN|BEST|BESTBUY|BET|BF|BG|BH|BHARTI|BI|BIBLE|BID|BIKE|BING|BINGO|BIO|BIZ|BJ|BLACK|BLACKFRIDAY|BLOCKBUSTER|BLOG|BLOOMBERG|BLUE|BM|BMS|BMW|BN|BNPPARIBAS|BO|BOATS|BOEHRINGER|BOFA|BOM|BOND|BOO|BOOK|BOOKING|BOSCH|BOSTIK|BOSTON|BOT|BOUTIQUE|BOX|BR|BRADESCO|BRIDGESTONE|BROADWAY|BROKER|BROTHER|BRUSSELS|BS|BT|BUDAPEST|BUGATTI|BUILD|BUILDERS|BUSINESS|BUY|BUZZ|BV|BW|BY|BZ|BZH|CA|CAB|CAFE|CAL|CALL|CALVINKLEIN|CAM|CAMERA|CAMP|CANCERRESEARCH|CANON|CAPETOWN|CAPITAL|CAPITALONE|CAR|CARAVAN|CARDS|CARE|CAREER|CAREERS|CARS|CASA|CASE|CASH|CASINO|CAT|CATERING|CATHOLIC|CBA|CBN|CBRE|CBS|CC|CD|CENTER|CEO|CERN|CF|CFA|CFD|CG|CH|CHANEL|CHANNEL|CHARITY|CHASE|CHAT|CHEAP|CHINTAI|CHRISTMAS|CHROME|CHURCH|CI|CIPRIANI|CIRCLE|CISCO|CITADEL|CITI|CITIC|CITY|CITYEATS|CK|CL|CLAIMS|CLEANING|CLICK|CLINIC|CLINIQUE|CLOTHING|CLOUD|CLUB|CLUBMED|CM|CN|CO|COACH|CODES|COFFEE|COLLEGE|COLOGNE|COM|COMCAST|COMMBANK|COMMUNITY|COMPANY|COMPARE|COMPUTER|COMSEC|CONDOS|CONSTRUCTION|CONSULTING|CONTACT|CONTRACTORS|COOKING|COOKINGCHANNEL|COOL|COOP|CORSICA|COUNTRY|COUPON|COUPONS|COURSES|CPA|CR|CREDIT|CREDITCARD|CREDITUNION|CRICKET|CROWN|CRS|CRUISE|CRUISES|CSC|CU|CUISINELLA|CV|CW|CX|CY|CYMRU|CYOU|CZ|DABUR|DAD|DANCE|DATA|DATE|DATING|DATSUN|DAY|DCLK|DDS|DE|DEAL|DEALER|DEALS|DEGREE|DELIVERY|DELL|DELOITTE|DELTA|DEMOCRAT|DENTAL|DENTIST|DESI|DESIGN|DEV|DHL|DIAMONDS|DIET|DIGITAL|DIRECT|DIRECTORY|DISCOUNT|DISCOVER|DISH|DIY|DJ|DK|DM|DNP|DO|DOCS|DOCTOR|DOG|DOMAINS|DOT|DOWNLOAD|DRIVE|DTV|DUBAI|DUCK|DUNLOP|DUPONT|DURBAN|DVAG|DVR|DZ|EARTH|EAT|EC|ECO|EDEKA|EDU|EDUCATION|EE|EG|EMAIL|EMERCK|ENERGY|ENGINEER|ENGINEERING|ENTERPRISES|EPSON|EQUIPMENT|ER|ERICSSON|ERNI|ES|ESQ|ESTATE|ET|ETISALAT|EU|EUROVISION|EUS|EVENTS|EXCHANGE|EXPERT|EXPOSED|EXPRESS|EXTRASPACE|FAGE|FAIL|FAIRWINDS|FAITH|FAMILY|FAN|FANS|FARM|FARMERS|FASHION|FAST|FEDEX|FEEDBACK|FERRARI|FERRERO|FI|FIAT|FIDELITY|FIDO|FILM|FINAL|FINANCE|FINANCIAL|FIRE|FIRESTONE|FIRMDALE|FISH|FISHING|FIT|FITNESS|FJ|FK|FLICKR|FLIGHTS|FLIR|FLORIST|FLOWERS|FLY|FM|FO|FOO|FOOD|FOODNETWORK|FOOTBALL|FORD|FOREX|FORSALE|FORUM|FOUNDATION|FOX|FR|FREE|FRESENIUS|FRL|FROGANS|FRONTDOOR|FRONTIER|FTR|FUJITSU|FUJIXEROX|FUN|FUND|FURNITURE|FUTBOL|FYI|GA|GAL|GALLERY|GALLO|GALLUP|GAME|GAMES|GAP|GARDEN|GAY|GB|GBIZ|GD|GDN|GE|GEA|GENT|GENTING|GEORGE|GF|GG|GGEE|GH|GI|GIFT|GIFTS|GIVES|GIVING|GL|GLADE|GLASS|GLE|GLOBAL|GLOBO|GM|GMAIL|GMBH|GMO|GMX|GN|GODADDY|GOLD|GOLDPOINT|GOLF|GOO|GOODYEAR|GOOG|GOOGLE|GOP|GOT|GOV|GP|GQ|GR|GRAINGER|GRAPHICS|GRATIS|GREEN|GRIPE|GROCERY|GROUP|GS|GT|GU|GUARDIAN|GUCCI|GUGE|GUIDE|GUITARS|GURU|GW|GY|HAIR|HAMBURG|HANGOUT|HAUS|HBO|HDFC|HDFCBANK|HEALTH|HEALTHCARE|HELP|HELSINKI|HERE|HERMES|HGTV|HIPHOP|HISAMITSU|HITACHI|HIV|HK|HKT|HM|HN|HOCKEY|HOLDINGS|HOLIDAY|HOMEDEPOT|HOMEGOODS|HOMES|HOMESENSE|HONDA|HORSE|HOSPITAL|HOST|HOSTING|HOT|HOTELES|HOTELS|HOTMAIL|HOUSE|HOW|HR|HSBC|HT|HU|HUGHES|HYATT|HYUNDAI|IBM|ICBC|ICE|ICU|ID|IE|IEEE|IFM|IKANO|IL|IM|IMAMAT|IMDB|IMMO|IMMOBILIEN|IN|INC|INDUSTRIES|INFINITI|INFO|ING|INK|INSTITUTE|INSURANCE|INSURE|INT|INTERNATIONAL|INTUIT|INVESTMENTS|IO|IPIRANGA|IQ|IR|IRISH|IS|ISMAILI|IST|ISTANBUL|IT|ITAU|ITV|IVECO|JAGUAR|JAVA|JCB|JE|JEEP|JETZT|JEWELRY|JIO|JLL|JM|JMP|JNJ|JO|JOBS|JOBURG|JOT|JOY|JP|JPMORGAN|JPRS|JUEGOS|JUNIPER|KAUFEN|KDDI|KE|KERRYHOTELS|KERRYLOGISTICS|KERRYPROPERTIES|KFH|KG|KH|KI|KIA|KIM|KINDER|KINDLE|KITCHEN|KIWI|KM|KN|KOELN|KOMATSU|KOSHER|KP|KPMG|KPN|KR|KRD|KRED|KUOKGROUP|KW|KY|KYOTO|KZ|LA|LACAIXA|LAMBORGHINI|LAMER|LANCASTER|LANCIA|LAND|LANDROVER|LANXESS|LASALLE|LAT|LATINO|LATROBE|LAW|LAWYER|LB|LC|LDS|LEASE|LECLERC|LEFRAK|LEGAL|LEGO|LEXUS|LGBT|LI|LIDL|LIFE|LIFEINSURANCE|LIFESTYLE|LIGHTING|LIKE|LILLY|LIMITED|LIMO|LINCOLN|LINDE|LINK|LIPSY|LIVE|LIVING|LIXIL|LK|LLC|LLP|LOAN|LOANS|LOCKER|LOCUS|LOFT|LOL|LONDON|LOTTE|LOTTO|LOVE|LPL|LPLFINANCIAL|LR|LS|LT|LTD|LTDA|LU|LUNDBECK|LUXE|LUXURY|LV|LY|MA|MACYS|MADRID|MAIF|MAISON|MAKEUP|MAN|MANAGEMENT|MANGO|MAP|MARKET|MARKETING|MARKETS|MARRIOTT|MARSHALLS|MASERATI|MATTEL|MBA|MC|MCKINSEY|MD|ME|MED|MEDIA|MEET|MELBOURNE|MEME|MEMORIAL|MEN|MENU|MERCKMSD|MG|MH|MIAMI|MICROSOFT|MIL|MINI|MINT|MIT|MITSUBISHI|MK|ML|MLB|MLS|MM|MMA|MN|MO|MOBI|MOBILE|MODA|MOE|MOI|MOM|MONASH|MONEY|MONSTER|MORMON|MORTGAGE|MOSCOW|MOTO|MOTORCYCLES|MOV|MOVIE|MP|MQ|MR|MS|MSD|MT|MTN|MTR|MU|MUSEUM|MUTUAL|MV|MW|MX|MY|MZ|NA|NAB|NAGOYA|NAME|NATIONWIDE|NATURA|NAVY|NBA|NC|NE|NEC|NET|NETBANK|NETFLIX|NETWORK|NEUSTAR|NEW|NEWS|NEXT|NEXTDIRECT|NEXUS|NF|NFL|NG|NGO|NHK|NI|NICO|NIKE|NIKON|NINJA|NISSAN|NISSAY|NL|NO|NOKIA|NORTHWESTERNMUTUAL|NORTON|NOW|NOWRUZ|NOWTV|NP|NR|NRA|NRW|NTT|NU|NYC|NZ|OBI|OBSERVER|OFF|OFFICE|OKINAWA|OLAYAN|OLAYANGROUP|OLDNAVY|OLLO|OM|OMEGA|ONE|ONG|ONL|ONLINE|ONYOURSIDE|OOO|OPEN|ORACLE|ORANGE|ORG|ORGANIC|ORIGINS|OSAKA|OTSUKA|OTT|OVH|PA|PAGE|PANASONIC|PARIS|PARS|PARTNERS|PARTS|PARTY|PASSAGENS|PAY|PCCW|PE|PET|PF|PFIZER|PG|PH|PHARMACY|PHD|PHILIPS|PHONE|PHOTO|PHOTOGRAPHY|PHOTOS|PHYSIO|PICS|PICTET|PICTURES|PID|PIN|PING|PINK|PIONEER|PIZZA|PK|PL|PLACE|PLAY|PLAYSTATION|PLUMBING|PLUS|PM|PN|PNC|POHL|POKER|POLITIE|PORN|POST|PR|PRAMERICA|PRAXI|PRESS|PRIME|PRO|PROD|PRODUCTIONS|PROF|PROGRESSIVE|PROMO|PROPERTIES|PROPERTY|PROTECTION|PRU|PRUDENTIAL|PS|PT|PUB|PW|PWC|PY|QA|QPON|QUEBEC|QUEST|QVC|RACING|RADIO|RAID|RE|READ|REALESTATE|REALTOR|REALTY|RECIPES|RED|REDSTONE|REDUMBRELLA|REHAB|REISE|REISEN|REIT|RELIANCE|REN|RENT|RENTALS|REPAIR|REPORT|REPUBLICAN|REST|RESTAURANT|REVIEW|REVIEWS|REXROTH|RICH|RICHARDLI|RICOH|RIL|RIO|RIP|RMIT|RO|ROCHER|ROCKS|RODEO|ROGERS|ROOM|RS|RSVP|RU|RUGBY|RUHR|RUN|RW|RWE|RYUKYU|SA|SAARLAND|SAFE|SAFETY|SAKURA|SALE|SALON|SAMSCLUB|SAMSUNG|SANDVIK|SANDVIKCOROMANT|SANOFI|SAP|SARL|SAS|SAVE|SAXO|SB|SBI|SBS|SC|SCA|SCB|SCHAEFFLER|SCHMIDT|SCHOLARSHIPS|SCHOOL|SCHULE|SCHWARZ|SCIENCE|SCJOHNSON|SCOT|SD|SE|SEARCH|SEAT|SECURE|SECURITY|SEEK|SELECT|SENER|SERVICES|SES|SEVEN|SEW|SEX|SEXY|SFR|SG|SH|SHANGRILA|SHARP|SHAW|SHELL|SHIA|SHIKSHA|SHOES|SHOP|SHOPPING|SHOUJI|SHOW|SHOWTIME|SI|SILK|SINA|SINGLES|SITE|SJ|SK|SKI|SKIN|SKY|SKYPE|SL|SLING|SM|SMART|SMILE|SN|SNCF|SO|SOCCER|SOCIAL|SOFTBANK|SOFTWARE|SOHU|SOLAR|SOLUTIONS|SONG|SONY|SOY|SPA|SPACE|SPORT|SPOT|SPREADBETTING|SR|SRL|SS|ST|STADA|STAPLES|STAR|STATEBANK|STATEFARM|STC|STCGROUP|STOCKHOLM|STORAGE|STORE|STREAM|STUDIO|STUDY|STYLE|SU|SUCKS|SUPPLIES|SUPPLY|SUPPORT|SURF|SURGERY|SUZUKI|SV|SWATCH|SWIFTCOVER|SWISS|SX|SY|SYDNEY|SYSTEMS|SZ|TAB|TAIPEI|TALK|TAOBAO|TARGET|TATAMOTORS|TATAR|TATTOO|TAX|TAXI|TC|TCI|TD|TDK|TEAM|TECH|TECHNOLOGY|TEL|TEMASEK|TENNIS|TEVA|TF|TG|TH|THD|THEATER|THEATRE|TIAA|TICKETS|TIENDA|TIFFANY|TIPS|TIRES|TIROL|TJ|TJMAXX|TJX|TK|TKMAXX|TL|TM|TMALL|TN|TO|TODAY|TOKYO|TOOLS|TOP|TORAY|TOSHIBA|TOTAL|TOURS|TOWN|TOYOTA|TOYS|TR|TRADE|TRADING|TRAINING|TRAVEL|TRAVELCHANNEL|TRAVELERS|TRAVELERSINSURANCE|TRUST|TRV|TT|TUBE|TUI|TUNES|TUSHU|TV|TVS|TW|TZ|UA|UBANK|UBS|UG|UK|UNICOM|UNIVERSITY|UNO|UOL|UPS|US|UY|UZ|VA|VACATIONS|VANA|VANGUARD|VC|VE|VEGAS|VENTURES|VERISIGN|VERSICHERUNG|VET|VG|VI|VIAJES|VIDEO|VIG|VIKING|VILLAS|VIN|VIP|VIRGIN|VISA|VISION|VIVA|VIVO|VLAANDEREN|VN|VODKA|VOLKSWAGEN|VOLVO|VOTE|VOTING|VOTO|VOYAGE|VU|VUELOS|WALES|WALMART|WALTER|WANG|WANGGOU|WATCH|WATCHES|WEATHER|WEATHERCHANNEL|WEBCAM|WEBER|WEBSITE|WED|WEDDING|WEIBO|WEIR|WF|WHOSWHO|WIEN|WIKI|WILLIAMHILL|WIN|WINDOWS|WINE|WINNERS|WME|WOLTERSKLUWER|WOODSIDE|WORK|WORKS|WORLD|WOW|WS|WTC|WTF|XBOX|XEROX|XFINITY|XIHUAN|XIN|XN--11B4C3D|XN--1CK2E1B|XN--1QQW23A|XN--2SCRJ9C|XN--30RR7Y|XN--3BST00M|XN--3DS443G|XN--3E0B707E|XN--3HCRJ9C|XN--3OQ18VL8PN36A|XN--3PXU8K|XN--42C2D9A|XN--45BR5CYL|XN--45BRJ9C|XN--45Q11C|XN--4DBRK0CE|XN--4GBRIM|XN--54B7FTA0CC|XN--55QW42G|XN--55QX5D|XN--5SU34J936BGSG|XN--5TZM5G|XN--6FRZ82G|XN--6QQ986B3XL|XN--80ADXHKS|XN--80AO21A|XN--80AQECDR1A|XN--80ASEHDB|XN--80ASWG|XN--8Y0A063A|XN--90A3AC|XN--90AE|XN--90AIS|XN--9DBQ2A|XN--9ET52U|XN--9KRT00A|XN--B4W605FERD|XN--BCK1B9A5DRE4C|XN--C1AVG|XN--C2BR7G|XN--CCK2B3B|XN--CCKWCXETD|XN--CG4BKI|XN--CLCHC0EA0B2G2A9GCD|XN--CZR694B|XN--CZRS0T|XN--CZRU2D|XN--D1ACJ3B|XN--D1ALF|XN--E1A4C|XN--ECKVDTC9D|XN--EFVY88H|XN--FCT429K|XN--FHBEI|XN--FIQ228C5HS|XN--FIQ64B|XN--FIQS8S|XN--FIQZ9S|XN--FJQ720A|XN--FLW351E|XN--FPCRJ9C3D|XN--FZC2C9E2C|XN--FZYS8D69UVGM|XN--G2XX48C|XN--GCKR3F0F|XN--GECRJ9C|XN--GK3AT1E|XN--H2BREG3EVE|XN--H2BRJ9C|XN--H2BRJ9C8C|XN--HXT814E|XN--I1B6B1A6A2E|XN--IMR513N|XN--IO0A7I|XN--J1AEF|XN--J1AMH|XN--J6W193G|XN--JLQ480N2RG|XN--JLQ61U9W7B|XN--JVR189M|XN--KCRX77D1X4A|XN--KPRW13D|XN--KPRY57D|XN--KPUT3I|XN--L1ACC|XN--LGBBAT1AD8J|XN--MGB9AWBF|XN--MGBA3A3EJT|XN--MGBA3A4F16A|XN--MGBA7C0BBN0A|XN--MGBAAKC7DVF|XN--MGBAAM7A8H|XN--MGBAB2BD|XN--MGBAH1A3HJKRD|XN--MGBAI9AZGQP6J|XN--MGBAYH7GPA|XN--MGBBH1A|XN--MGBBH1A71E|XN--MGBC0A9AZCG|XN--MGBCA7DZDO|XN--MGBCPQ6GPA1A|XN--MGBERP4A5D4AR|XN--MGBGU82A|XN--MGBI4ECEXP|XN--MGBPL2FH|XN--MGBT3DHD|XN--MGBTX2B|XN--MGBX4CD0AB|XN--MIX891F|XN--MK1BU44C|XN--MXTQ1M|XN--NGBC5AZD|XN--NGBE9E0A|XN--NGBRX|XN--NODE|XN--NQV7F|XN--NQV7FS00EMA|XN--NYQY26A|XN--O3CW4H|XN--OGBPF8FL|XN--OTU796D|XN--P1ACF|XN--P1AI|XN--PGBS0DH|XN--PSSY2U|XN--Q7CE6A|XN--Q9JYB4C|XN--QCKA1PMC|XN--QXA6A|XN--QXAM|XN--RHQV96G|XN--ROVU88B|XN--RVC1E0AM3E|XN--S9BRJ9C|XN--SES554G|XN--T60B56A|XN--TCKWE|XN--TIQ49XQYJ|XN--UNUP4Y|XN--VERMGENSBERATER-CTB|XN--VERMGENSBERATUNG-PWB|XN--VHQUV|XN--VUQ861B|XN--W4R85EL8FHU5DNRA|XN--W4RS40L|XN--WGBH1C|XN--WGBL6A|XN--XHQ521B|XN--XKC2AL3HYE2A|XN--XKC2DL3A5EE0H|XN--Y9A3AQ|XN--YFRO4I67O|XN--YGBI2AMMX|XN--ZFR164B|XXX|XYZ|YACHTS|YAHOO|YAMAXUN|YANDEX|YE|YODOBASHI|YOGA|YOKOHAMA|YOU|YOUTUBE|YT|YUN|ZA|ZAPPOS|ZARA|ZERO|ZIP|ZM|ZONE|ZUERICH|ZW)$/i; -module.exports = regexp; diff --git a/package.json b/package.json deleted file mode 100644 index 9d01a3b2..00000000 --- a/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "cors-anywhere", - "version": "0.4.4", - "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 ", - "repository": { - "type": "git", - "url": "https://github.com/Rob--W/cors-anywhere.git" - }, - "bugs": { - "url": "https://github.com/Rob--W/cors-anywhere/issues/", - "email": "rob@robwu.nl" - }, - "keywords": [ - "cors", - "cross-domain", - "http-proxy", - "proxy", - "heroku" - ], - "main": "./lib/cors-anywhere.js", - "files": [ - "lib/", - "test/", - "Procfile", - "demo.html", - "server.js" - ], - "dependencies": { - "http-proxy": "1.11.1", - "proxy-from-env": "0.0.1" - }, - "devDependencies": { - "coveralls": "^2.11.6", - "eslint": "^2.2.0", - "istanbul": "^0.4.2", - "lolex": "^1.5.0", - "mocha": "^3.4.2", - "nock": "^8.2.1", - "supertest": "^2.0.1" - }, - "scripts": { - "lint": "eslint .", - "test": "mocha ./test/test*.js --reporter spec", - "test-coverage": "istanbul cover ./node_modules/.bin/_mocha -- test/test.js test/test-ratelimit.js --reporter spec" - }, - "engines": { - "node": ">=0.10.0" - } -} diff --git a/server.js b/server.js index 757ac1d0..e3e2f627 100644 --- a/server.js +++ b/server.js @@ -1,49 +1,32 @@ -// Listen on a specific host via the HOST environment variable -var host = process.env.HOST || '0.0.0.0'; -// Listen on a specific port via the PORT environment variable -var port = process.env.PORT || 8080; +const http = require("http"); -// Grab the blacklist from the command-line so that we can update the blacklist without deploying -// again. CORS Anywhere is open by design, and this blacklist is not used, except for countering -// immediate abuse (e.g. denial of service). If you want to block all origins except for some, -// use originWhitelist instead. -var originBlacklist = parseEnvList(process.env.CORSANYWHERE_BLACKLIST); -var originWhitelist = parseEnvList(process.env.CORSANYWHERE_WHITELIST); -function parseEnvList(env) { - if (!env) { - return []; +const port = 3000; +const allowedOrigin = "https://git.news"; // Replace with your specific domain + +const responseObject = { + data: { + children: [], + }, +}; + +const server = http.createServer((req, res) => { + // CORS headers + res.setHeader("Access-Control-Allow-Origin", allowedOrigin); + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); + + // Handle preflight requests + if (req.method === "OPTIONS") { + res.writeHead(204); + res.end(); + return; } - return env.split(','); -} -// Set up rate-limiting to avoid abuse of the public CORS Anywhere server. -var checkRateLimit = require('./lib/rate-limit')(process.env.CORSANYWHERE_RATELIMIT); + // Handle regular requests + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify(responseObject)); +}); -var cors_proxy = require('./lib/cors-anywhere'); -cors_proxy.createServer({ - originBlacklist: originBlacklist, - originWhitelist: originWhitelist, - requireHeader: ['origin', 'x-requested-with'], - checkRateLimit: checkRateLimit, - removeHeaders: [ - 'cookie', - 'cookie2', - // Strip Heroku-specific headers - 'x-request-start', - 'x-request-id', - 'via', - 'connect-time', - 'total-route-time', - // Other Heroku added debug headers - // 'x-forwarded-for', - // 'x-forwarded-proto', - // 'x-forwarded-port', - ], - redirectSameOrigin: true, - httpProxyOptions: { - // 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); +server.listen(port, () => { + console.log(`Server running at http://localhost:${port}/`); }); diff --git a/test/cert.pem b/test/cert.pem deleted file mode 100644 index 36844221..00000000 --- a/test/cert.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBsTCCARoCCQDp0DuED0RAJzANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJj -b3JzLWFueXdoZXJlIHRlc3QwHhcNMTUwNTA2MDcyOTM1WhcNMTUwNjA1MDcyOTM1 -WjAdMRswGQYDVQQDDBJjb3JzLWFueXdoZXJlIHRlc3QwgZ8wDQYJKoZIhvcNAQEB -BQADgY0AMIGJAoGBALzTF5ClJKvkB6h9h7kLORV+mMV3ySDs+oGZn0NgXM+yb9Zh -69r5e95zZJl/V432LFdy0hkEcVteUkC2REWG8D4COGfiwWsXyZdaP1qqLpDpPAMm -v6xFHjW6rVuxzfr4GUjE0Zh9Fg2R2SbtCOcHS/LZoDVOqOvn6+urP6XFY4aFAgMB -AAEwDQYJKoZIhvcNAQELBQADgYEAYXMhS8ouff/c8lSUUs/CLh010cj5RPk/ivS7 -aN2PArzQ6pZvhpgJKf7XAQksBtLYYZMzIpG6W8zhPSbqzly7lELAdE+sxcbbfu8A -FMjNVFQ2Fm1c8ImX8qpE3nhVrPAiwfPjGBqKHTl730gvbh1XH9TC4O4dZcbEomX3 -5MsxQfc= ------END CERTIFICATE----- diff --git a/test/child.js b/test/child.js deleted file mode 100644 index c95fc0a3..00000000 --- a/test/child.js +++ /dev/null @@ -1,64 +0,0 @@ -// When this module is loaded, CORS Anywhere is started. -// Then, a request is generated to warm up the server (just in case). -// Then the base URL of CORS Anywhere is sent to the parent process. -// ... -// When the parent process is done, it sends an empty message to this child -// process, which in turn records the change in used heap space. -// The difference in heap space is finally sent back to the parent process. -// ... -// The parent process should then kill this child. - -process.on('uncaughtException', function(e) { - console.error('Uncaught exception in child process: ' + e); - console.error(e.stack); - process.exit(-1); -}); - -// Invoke memoryUsage() without using its result to make sure that any internal -// datastructures that supports memoryUsage() is initialized and won't pollute -// the memory usage measurement later on. -process.memoryUsage(); - -var heapUsedStart = 0; -function getMemoryUsage(callback) { - // Note: Requires --expose-gc - // 6 is the minimum amount of gc() calls before calling gc() again does not - // reduce memory any more. - for (var i = 0; i < 6; ++i) { - global.gc(); - } - callback(process.memoryUsage().heapUsed); -} - -var server; -if (process.argv.indexOf('use-http-instead-of-cors-anywhere') >= 0) { - server = require('http').createServer(function(req, res) { res.end(); }); -} else { - server = require('../').createServer(); -} - -server.listen(0, function() { - // Perform 1 request to warm up. - require('http').get({ - hostname: '127.0.0.1', - port: server.address().port, - path: '/http://invalid:99999', - agent: false, - }, function() { - notifyParent(); - }); - - function notifyParent() { - getMemoryUsage(function(usage) { - heapUsedStart = usage; - process.send('http://127.0.0.1:' + server.address().port + '/'); - }); - } -}); - -process.once('message', function() { - getMemoryUsage(function(heapUsedEnd) { - var delta = heapUsedEnd - heapUsedStart; - process.send(delta); - }); -}); diff --git a/test/customHelp.html b/test/customHelp.html deleted file mode 100644 index 2ced07a7..00000000 --- a/test/customHelp.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

Custom HTML help!!

- - \ No newline at end of file diff --git a/test/customHelp.txt b/test/customHelp.txt deleted file mode 100644 index 5d61f641..00000000 --- a/test/customHelp.txt +++ /dev/null @@ -1 +0,0 @@ -Server is OK!! diff --git a/test/dummy.txt b/test/dummy.txt deleted file mode 100644 index eaf5f751..00000000 --- a/test/dummy.txt +++ /dev/null @@ -1 +0,0 @@ -dummy content diff --git a/test/key.pem b/test/key.pem deleted file mode 100644 index 5680ec9f..00000000 --- a/test/key.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQC80xeQpSSr5AeofYe5CzkVfpjFd8kg7PqBmZ9DYFzPsm/WYeva -+Xvec2SZf1eN9ixXctIZBHFbXlJAtkRFhvA+Ajhn4sFrF8mXWj9aqi6Q6TwDJr+s -RR41uq1bsc36+BlIxNGYfRYNkdkm7QjnB0vy2aA1Tqjr5+vrqz+lxWOGhQIDAQAB -AoGBAISy8OelN01Zlowxk/VWTsqtSl3UHdP21uHHfWaTTQZlxzTpYiBknkmp3LQH -CxfoPidCuSX9ulBUzAdQUFBwUVp8wyPIRjpNyRiD58dLNxG0G+OACqnLxNWqIf6F -vS3UqrRGIA5u+GSz+0g3DAeVA5JmsAyHQGkJsh3pcuD8/7wNAkEA7MScGfySy9td -dDBekVU5/GaVg4DA4ELtDNfa99ARB89XP0ps/XrOPEL9yxTjWIHH+qxuhpfG6zGN -ouxZlvBT9wJBAMwpig4A4JE8M8pBDwMY4213gud8B1grQTbhz5bv51aTaIEQFcxw -sGfEmAfVToI+kVTrdFggy42YCSMSvwuF4mMCQQDZHkqPwf/TlSwT2i8+UstD28aL -uswkWvsKZf9UdKbJZKd7UIK1x6HLvRsC2frJNOnvw6PvJMuy7dQWbWqScXxtAkBv -/5msdO68vbnriiUiHdUliBpXwsKEq7Xq1ZV7x7+wzszVgG106ZzcUAzWvz2CVbCE -VWZNsi/4TR82DmKff6LhAkBA/xceWaZjxh5dkWkIrMFWd2GFhGlpfwYw7oELwRL8 -RYXzc1Mr2fDdZDgwgjg67JQqIhOQ3E4RGKPgZ+E7Pk3/ ------END RSA PRIVATE KEY----- diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index 61a1d019..00000000 --- a/test/setup.js +++ /dev/null @@ -1,157 +0,0 @@ -var nock = require('nock'); -if (parseInt(process.versions.node, 10) >= 8) { - // See DEP0066 at https://nodejs.org/api/deprecations.html. - // _headers and _headerNames have been removed from Node v8, which causes - // nock <= 9.0.13 to fail. The snippet below monkey-patches the library, see - // https://github.com/node-nock/nock/pull/929/commits/f6369d0edd2a172024124f - // for the equivalent logic without proxies. - Object.defineProperty(require('http').ClientRequest.prototype, '_headers', { - get: function() { - var request = this; - // eslint-disable-next-line no-undef - return new Proxy(request.getHeaders(), { - set: function(target, property, value) { - request.setHeader(property, value); - return true; - }, - }); - }, - set: function() { - // Ignore. - }, - }); -} - -nock.enableNetConnect('127.0.0.1'); - -function echoheaders(origin) { - nock(origin) - .persist() - .get('/echoheaders') - .reply(function() { - var headers = this.req.headers; - var excluded_headers = [ - 'accept-encoding', - 'user-agent', - 'connection', - // Remove this header since its value is platform-specific. - 'x-forwarded-for', - 'test-include-xfwd', - ]; - if (!('test-include-xfwd' in headers)) { - excluded_headers.push('x-forwarded-port'); - excluded_headers.push('x-forwarded-proto'); - } - var response = {}; - Object.keys(headers).forEach(function(name) { - if (excluded_headers.indexOf(name) === -1) { - response[name] = headers[name]; - } - }); - return response; - }); -} - -nock('http://example.com') - .persist() - .get('/') - .reply(200, 'Response from example.com') - - .post('/echopost') - .reply(200, function(uri, requestBody) { - return requestBody; - }) - - .get('/setcookie') - .reply(200, '', { - 'Set-Cookie': 'x', - 'Set-Cookie2': 'y', - 'Set-Cookie3': 'z', // This is not a special cookie setting header. - }) - - .get('/redirecttarget') - .reply(200, 'redirect target', { - 'Some-header': 'value', - }) - - .head('/redirect') - .reply(302, '', { - Location: '/redirecttarget', - }) - - .get('/redirect') - .reply(302, 'redirecting...', { - 'header at redirect': 'should not be here', - Location: '/redirecttarget', - }) - - .get('/redirectposttarget') - .reply(200, 'post target') - - .post('/redirectposttarget') - .reply(200, 'post target (POST)') - - .post('/redirectpost') - .reply(302, 'redirecting...', { - Location: '/redirectposttarget', - }) - - .post('/redirect307') - .reply(307, 'redirecting...', { - Location: '/redirectposttarget', - }) - - .get('/redirect2redirect') - .reply(302, 'redirecting to redirect...', { - Location: '/redirect', - }) - - .get('/redirectloop') - .reply(302, 'redirecting ad infinitum...', { - Location: '/redirectloop', - }) - - .get('/redirectwithoutlocation') - .reply(302, 'maybe found') - - .get('/redirectinvalidlocation') - .reply(302, 'redirecting to junk...', { - Location: 'http:///', - }) - - .get('/proxyerror') - .replyWithError('throw node') -; - -nock('https://example.com') - .persist() - .get('/') - .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'); -echoheaders('https://example.com:1337'); - -nock('http://robots.txt') - .get('/') - .reply(200, 'this is http://robots.txt'); diff --git a/test/test-examples.js b/test/test-examples.js deleted file mode 100644 index 94759d18..00000000 --- a/test/test-examples.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * CORS Anywhere is designed for use as a standalone server. Sometimes you want - * to have extra functionality on top of the default CORS server. If it may be - * useful to others, please open a feature request on the issue tracker at - * https://github.com/Rob--W/cors-anywhere/issues. - * - * If it is only useful to your application, look below for some examples. - * These examples are provided as-is without guarantees. Use at your own risk. - */ - -/* eslint-env mocha */ -require('./setup'); - -var createServer = require('../').createServer; -var assert = require('assert'); -var request = require('supertest'); - -var http = require('http'); - -describe('Examples', function() { - // Note: In the examples below we don't listen on any port after calling - // createServer() because it is not needed to start listening on a port if the - // CORS Anywhere is only used internally. - - // And normally you have to listen on some port, like this: - // - // http_server.listen(port_number); - // - // But in these test, the call to request() automatically handles that part so - // the examples don't have an explicit .listen() call. - - it('Rewrite proxy URL', function(done) { - var cors_anywhere = createServer(); - - var http_server = http.createServer(function(req, res) { - // For testing, check whether req.url is the same as what we input below. - assert.strictEqual(req.url, '/dummy-for-testing'); - - // Basic example: Always proxy example.com. - req.url = '/http://example.com'; - - cors_anywhere.emit('request', req, res); - }); - - request(http_server) - .get('/dummy-for-testing') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'Response from example.com', done); - }); - - it('Transform response to uppercase (streaming)', function(done) { - var cors_anywhere = createServer(); - - var http_server = http.createServer(function(req, res) { - var originalWrite = res.write; - - res.write = function(data, encoding, callback) { - if (Buffer.isBuffer(data)) { - data = data.toString(); - } - - assert.strictEqual(typeof data, 'string'); - - // This example shows how to transform the response to upper case. - data = data.toUpperCase(); - - originalWrite.call(this, data, encoding, callback); - }; - - cors_anywhere.emit('request', req, res); - }); - - request(http_server) - .get('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'RESPONSE FROM EXAMPLE.COM', done); - }); - - it('Transform response to uppercase (buffered)', function(done) { - var cors_anywhere = createServer(); - - var http_server = http.createServer(function(req, res) { - var originalWrite = res.write; - var originalEnd = res.end; - - var buffers = []; - - res.write = function(data, encoding, callback) { - assert.ok(Buffer.isBuffer(data) || typeof data === 'string'); - - buffers.push(data); - if (callback) { - process.nextTick(callback, null); - } - }; - res.end = function(data, encoding, callback) { - if (data) { - this.write(data, encoding); - } - - // After calling .end(), .write shouldn't be called any more. So let's - // restore it so that the default error handling for writing to closed - // streams would occur. - this.write = originalWrite; - - // Combine all chunks. Note that we're assuming that all chunks are - // utf8 strings or buffers whose content is utf8-encoded. If this - // assumption is not true, then you have to update the .write method - // above. - data = buffers.join(''); - - // This example shows how to transform the response to upper case. - data = data.toUpperCase(); - - // .end should be called once, so let's restore it so that any default - // error handling occurs if it occurs again. - this.end = originalEnd; - this.end(data, 'utf8', callback); - }; - - cors_anywhere.emit('request', req, res); - }); - - request(http_server) - .get('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'RESPONSE FROM EXAMPLE.COM', done); - }); -}); - diff --git a/test/test-memory.js b/test/test-memory.js deleted file mode 100644 index d7011649..00000000 --- a/test/test-memory.js +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint-env mocha */ -// Run this specific test using: -// npm test -- -f memory -var http = require('http'); -var path = require('path'); -var url = require('url'); -var fork = require('child_process').fork; - -describe('memory usage', function() { - var cors_api_url; - - var server; - var cors_anywhere_child; - before(function(done) { - server = http.createServer(function(req, res) { - res.writeHead(200); - res.end(); - }).listen(0, function() { - done(); - }); - }); - - after(function(done) { - server.close(function() { - done(); - }); - }); - - beforeEach(function(done) { - var cors_module_path = path.join(__dirname, 'child'); - var args = []; - // Uncomment this if you want to compare the performance of CORS Anywhere - // with the standard no-op http module. - // args.push('use-http-instead-of-cors-anywhere'); - var nodeOptionsArgs = ['--expose-gc']; - var nodeMajorV = parseInt(process.versions.node, 10); - // Node 11.3.0+, 10.14.0+, 8.14.0+, 6.15.0+ restrict header sizes - // (CVE-2018-12121), and need to be passed the --max-http-header-size flag - // to not reject large headers. - if (nodeMajorV >= 11 || - nodeMajorV === 10 || - nodeMajorV === 8 || - nodeMajorV === 6) { - nodeOptionsArgs.push('--max-http-header-size=60000'); - } - cors_anywhere_child = fork(cors_module_path, args, { - execArgv: nodeOptionsArgs, - }); - cors_anywhere_child.once('message', function(cors_url) { - cors_api_url = cors_url; - done(); - }); - }); - - afterEach(function() { - cors_anywhere_child.kill(); - }); - - /** - * Perform N CORS Anywhere proxy requests to a simple test server. - * - * @param {number} n - number of repetitions. - * @param {number} requestSize - Approximate size of request in kilobytes. - * @param {number} memMax - Expected maximum memory usage in kilobytes. - * @param {function} done - Upon success, called without arguments. - * Upon failure, called with the error as parameter. - */ - function performNRequests(n, requestSize, memMax, done) { - var remaining = n; - var request = url.parse( - cors_api_url + 'http://127.0.0.1:' + server.address().port); - request.agent = false; // Force Connection: Close - request.headers = { - 'Long-header': new Array(requestSize * 1e3).join('x'), - }; - (function requestAgain() { - if (remaining-- === 0) { - cors_anywhere_child.once('message', function(memory_usage_delta) { - console.log('Memory usage delta: ' + memory_usage_delta + - ' (' + n + ' requests of ' + requestSize + ' kb each)'); - if (memory_usage_delta > memMax * 1e3) { - // Note: Even if this error is reached, always profile (e.g. using - // node-inspector) whether it is a true leak, and not e.g. noise - // caused by the implementation of V8/Node.js. - // Uncomment args.push('use-http-instead-of-cors-anywhere') at the - // fork() call to get a sense of what's normal. - throw new Error('Possible memory leak: ' + memory_usage_delta + - ' bytes was not released, which exceeds the ' + memMax + - ' kb limit by ' + - Math.round(memory_usage_delta / memMax / 10 - 100) + '%.'); - } - done(); - }); - cors_anywhere_child.send(null); - return; - } - http.request(request, function() { - requestAgain(); - }).on('error', function(error) { - done(error); - }).end(); - })(); - } - - it('100 GET requests 50k', function(done) { - // This test is just for comparison with the following tests. - performNRequests(100, 50, 1200, done); - }); - - // 100x 1k and 100x 50k for comparison. - // Both should use about the same amount of memory if there is no leak. - it('1000 GET requests 1k', function(done) { - // Every request should complete within 10ms. - this.timeout(1000 * 10); - performNRequests(1000, 1, 2000, done); - }); - it('1000 GET requests 50k', function(done) { - // Every request should complete within 10ms. - this.timeout(1000 * 10); - performNRequests(1000, 50, 2000, done); - }); -}); diff --git a/test/test-ratelimit.js b/test/test-ratelimit.js deleted file mode 100644 index 84aa8e94..00000000 --- a/test/test-ratelimit.js +++ /dev/null @@ -1,231 +0,0 @@ -/* eslint-env mocha */ - -var createRateLimitChecker = require('../lib/rate-limit'); - -var lolex = require('lolex'); -var assert = require('assert'); - -function assertNotLimited(rateLimitReturnValue) { - if (rateLimitReturnValue) { - assert.fail('Expected no limit, but got ' + rateLimitReturnValue); - } -} - -function assertLimited(rateLimitReturnValue, limit, period) { - var msg; - if (period === 1) { - msg = 'The number of requests is limited to ' + limit + ' per minute. '; - } else { - msg = 'The number of requests is limited to ' + limit + ' per ' + period + ' minutes. '; - } - msg += 'Please self-host CORS Anywhere if you need more quota. ' + - 'See https://github.com/Rob--W/cors-anywhere#demo-server'; - assert.equal(rateLimitReturnValue, msg); -} - -describe('Rate limit', function() { - var clock; - beforeEach(function() { - clock = lolex.install(); - }); - afterEach(function() { - clock.uninstall(); - }); - it('is unlimited by default', function() { - var checkRateLimit = createRateLimitChecker(); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('https://example.com')); - assertNotLimited(checkRateLimit('https://example.com:1234')); - - checkRateLimit = createRateLimitChecker(''); - assertNotLimited(checkRateLimit('http://example.com')); - - checkRateLimit = createRateLimitChecker(' '); - assertNotLimited(checkRateLimit('http://example.com')); - }); - - it('zero per minute / 5 minutes', function() { - var checkRateLimit = createRateLimitChecker('0 1'); - assertLimited(checkRateLimit('http://example.com'), 0, 1); - assertLimited(checkRateLimit('https://example.com'), 0, 1); - - checkRateLimit = createRateLimitChecker('0 5'); - assertLimited(checkRateLimit('http://example.com'), 0, 5); - assertLimited(checkRateLimit('https://example.com'), 0, 5); - }); - - it('one per minute', function() { - var checkRateLimit = createRateLimitChecker('1 1'); - assertNotLimited(checkRateLimit('http://example.com')); - assertLimited(checkRateLimit('http://example.com'), 1, 1); - assertNotLimited(checkRateLimit('http://example.com:1234')); - assertLimited(checkRateLimit('http://example.com:1234'), 1, 1); - - clock.tick(59000); - assertLimited(checkRateLimit('http://example.com'), 1, 1); - - clock.tick(1000); - assertNotLimited(checkRateLimit('http://example.com')); - assertLimited(checkRateLimit('http://example.com'), 1, 1); - assertNotLimited(checkRateLimit('http://example.com:1234')); - assertLimited(checkRateLimit('http://example.com:1234'), 1, 1); - }); - - it('different domains, one per minute', function() { - var checkRateLimit = createRateLimitChecker('1 1'); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://example.net')); - assertNotLimited(checkRateLimit('http://wexample.net')); - assertNotLimited(checkRateLimit('http://xample.net')); - assertNotLimited(checkRateLimit('http://www.example.net')); - assertLimited(checkRateLimit('http://example.com'), 1, 1); - assertLimited(checkRateLimit('http://example.net'), 1, 1); - assertLimited(checkRateLimit('http://wexample.net'), 1, 1); - assertLimited(checkRateLimit('http://xample.net'), 1, 1); - assertLimited(checkRateLimit('http://www.example.net'), 1, 1); - - clock.tick(60000); // 1 minute - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://example.net')); - assertNotLimited(checkRateLimit('http://wexample.net')); - assertNotLimited(checkRateLimit('http://xample.net')); - assertNotLimited(checkRateLimit('http://www.example.net')); - }); - - it('unlimited domains, string', function() { - var checkRateLimit = createRateLimitChecker('1 2 example.com'); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://example.com')); - - assertNotLimited(checkRateLimit('http://wexample.com')); - assertNotLimited(checkRateLimit('http://xample.com')); - assertNotLimited(checkRateLimit('http://www.example.com')); - assertLimited(checkRateLimit('http://wexample.com'), 1, 2); - assertLimited(checkRateLimit('http://xample.com'), 1, 2); - assertLimited(checkRateLimit('http://www.example.com'), 1, 2); - }); - - it('unlimited domains, RegExp', function() { - var checkRateLimit = createRateLimitChecker('1 2 /example\\.com/'); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://example.com')); - - assertNotLimited(checkRateLimit('http://wexample.com')); - assertNotLimited(checkRateLimit('http://xample.com')); - assertNotLimited(checkRateLimit('http://www.example.com')); - assertLimited(checkRateLimit('http://wexample.com'), 1, 2); - assertLimited(checkRateLimit('http://xample.com'), 1, 2); - assertLimited(checkRateLimit('http://www.example.com'), 1, 2); - }); - - it('multiple domains, string', function() { - var checkRateLimit = createRateLimitChecker('1 2 a b cc '); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://cc')); - assertNotLimited(checkRateLimit('http://cc')); - assertNotLimited(checkRateLimit('http://c')); - assertLimited(checkRateLimit('http://c'), 1, 2); - }); - - it('multiple domains, RegExp', function() { - var checkRateLimit = createRateLimitChecker('1 2 /a/ /b/ /cc/ '); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://cc')); - assertNotLimited(checkRateLimit('http://cc')); - assertNotLimited(checkRateLimit('http://ccc')); - assertLimited(checkRateLimit('http://ccc'), 1, 2); - }); - - it('multiple domains, string and RegExp', function() { - var checkRateLimit = createRateLimitChecker('1 2 a /b/'); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://ab')); - assertLimited(checkRateLimit('http://ab'), 1, 2); - }); - - it('multiple domains, RegExp and string', function() { - var checkRateLimit = createRateLimitChecker('1 2 /a/ b'); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://a')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://b')); - assertNotLimited(checkRateLimit('http://ab')); - assertLimited(checkRateLimit('http://ab'), 1, 2); - }); - - it('wildcard subdomains', function() { - var checkRateLimit = createRateLimitChecker('0 1 /(.*\\.)?example\\.com/'); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://www.example.com')); - assertLimited(checkRateLimit('http://xexample.com'), 0, 1); - assertLimited(checkRateLimit('http://example.com.br'), 0, 1); - }); - - it('wildcard ports', function() { - var checkRateLimit = createRateLimitChecker('0 1 /example\\.com(:\\d{1,5})?/'); - assertNotLimited(checkRateLimit('http://example.com')); - assertNotLimited(checkRateLimit('http://example.com:1234')); - }); - - it('empty host', function() { - var checkRateLimit = createRateLimitChecker('0 1'); - assertLimited(checkRateLimit(''), 0, 1); - // Empty host actually means empty origin. But let's also test for 'http://'. - assertLimited(checkRateLimit('http://'), 0, 1); - - checkRateLimit = createRateLimitChecker('0 1 '); - assertLimited(checkRateLimit(''), 0, 1); - assertLimited(checkRateLimit('http://'), 0, 1); - - checkRateLimit = createRateLimitChecker('0 1 //'); - assertNotLimited(checkRateLimit('')); - assertNotLimited(checkRateLimit('http://')); - }); - - it('null origin', function() { - var checkRateLimit = createRateLimitChecker('0 1'); - assertLimited(checkRateLimit('null'), 0, 1); - assertLimited(checkRateLimit('http://null'), 0, 1); - - checkRateLimit = createRateLimitChecker('0 1 null'); - assertNotLimited(checkRateLimit('null')); - assertNotLimited(checkRateLimit('http://null')); - - checkRateLimit = createRateLimitChecker('0 1 /null/'); - assertNotLimited(checkRateLimit('null')); - assertNotLimited(checkRateLimit('http://null')); - }); - - it('case-insensitive', function() { - var checkRateLimit = createRateLimitChecker('0 1 NULL'); - assertNotLimited(checkRateLimit('null')); - assertNotLimited(checkRateLimit('http://null')); - - checkRateLimit = createRateLimitChecker('0 1 /NULL/'); - assertNotLimited(checkRateLimit('null')); - assertNotLimited(checkRateLimit('http://null')); - }); - - it('bad input', function() { - assert.throws(function() { - createRateLimitChecker('0 1 /'); - }, /Invalid CORSANYWHERE_RATELIMIT\. Regex at index 0 must start and end with a slash \("\/"\)\./); - - assert.throws(function() { - createRateLimitChecker('0 1 a /'); - }, /Invalid CORSANYWHERE_RATELIMIT\. Regex at index 1 must start and end with a slash \("\/"\)\./); - - assert.throws(function() { - createRateLimitChecker('0 1 /(/'); - }, /Invalid regular expression/); - }); -}); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 613978ce..00000000 --- a/test/test.js +++ /dev/null @@ -1,1287 +0,0 @@ -/* eslint-env mocha */ -require('./setup'); - -var createServer = require('../').createServer; -var request = require('supertest'); -var path = require('path'); -var http = require('http'); -var https = require('https'); -var fs = require('fs'); -var assert = require('assert'); - -var helpTextPath = path.join(__dirname, '../lib/help.txt'); -var helpText = fs.readFileSync(helpTextPath, {encoding: 'utf8'}); - -request.Test.prototype.expectJSON = function(json, done) { - this.expect(function(res) { - // Assume that the response can be parsed as JSON (otherwise it throws). - var actual = JSON.parse(res.text); - assert.deepEqual(actual, json); - }); - return done ? this.end(done) : this; -}; - -request.Test.prototype.expectNoHeader = function(header, done) { - this.expect(function(res) { - if (header.toLowerCase() in res.headers) { - return new Error('Unexpected header in response: ' + header); - } - }); - return done ? this.end(done) : this; -}; - -var cors_anywhere; -var cors_anywhere_port; -function stopServer(done) { - cors_anywhere.close(function() { - done(); - }); - cors_anywhere = null; -} - -describe('Basic functionality', function() { - before(function() { - cors_anywhere = createServer(); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /', function(done) { - request(cors_anywhere) - .get('/') - .type('text/plain') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, helpText, done); - }); - - it('GET /iscorsneeded', function(done) { - request(cors_anywhere) - .get('/iscorsneeded') - .expectNoHeader('access-control-allow-origin', done); - }); - - it('GET /example.com:65536', function(done) { - request(cors_anywhere) - .get('/example.com:65536') - .expect('Access-Control-Allow-Origin', '*') - .expect(400, 'Port number too large: 65536', done); - }); - - it('GET /favicon.ico', function(done) { - request(cors_anywhere) - .get('/favicon.ico') - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Invalid host: favicon.ico', done); - }); - - it('GET /robots.txt', function(done) { - request(cors_anywhere) - .get('/robots.txt') - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Invalid host: robots.txt', done); - }); - - it('GET /http://robots.txt should be proxied', function(done) { - request(cors_anywhere) - .get('/http://robots.txt') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'this is http://robots.txt', done); - }); - - it('GET /example.com', function(done) { - request(cors_anywhere) - .get('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'Response from example.com', done); - }); - - it('GET /example.com:80', function(done) { - request(cors_anywhere) - .get('/example.com:80') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com:80/') - .expect(200, 'Response from example.com', done); - }); - - it('GET /example.com:443', function(done) { - request(cors_anywhere) - .get('/example.com:443') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'https://example.com:443/') - .expect(200, 'Response from https://example.com', done); - }); - - it('GET //example.com', function(done) { - // '/example.com' is an invalid URL. - request(cors_anywhere) - .get('//example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, helpText, done); - }); - - it('GET /http://:1234', function(done) { - // 'http://:1234' is an invalid URL. - request(cors_anywhere) - .get('/http://:1234') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, helpText, done); - }); - - it('GET /http:///', function(done) { - // 'http://:1234' is an invalid URL. - request(cors_anywhere) - .get('/http:///') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, helpText, done); - }); - - it('GET /http:/notenoughslashes', function(done) { - // 'http:/notenoughslashes' is an invalid URL. - request(cors_anywhere) - .get('/http:/notenoughslashes') - .expect('Access-Control-Allow-Origin', '*') - .expect(400, 'The URL is invalid: two slashes are needed after the http(s):.', done); - }); - - - it('GET ///example.com', function(done) { - // API base URL (with trailing slash) + '//example.com' - request(cors_anywhere) - .get('///example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'Response from example.com', done); - }); - - it('GET /http://example.com', function(done) { - request(cors_anywhere) - .get('/http://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/') - .expect(200, 'Response from example.com', done); - }); - - it('POST plain text', function(done) { - request(cors_anywhere) - .post('/example.com/echopost') - .send('{"this is a request body & should not be mangled":1.00}') - .expect('Access-Control-Allow-Origin', '*') - .expect('{"this is a request body & should not be mangled":1.00}', done); - }); - - it('POST file', function(done) { - request(cors_anywhere) - .post('/example.com/echopost') - .attach('file', path.join(__dirname, 'dummy.txt')) - .expect('Access-Control-Allow-Origin', '*') - .expect(/\r\nContent-Disposition: form-data; name="file"; filename="dummy.txt"\r\nContent-Type: text\/plain\r\n\r\ndummy content\n\r\n/, done); // eslint-disable-line max-len - }); - - it('HEAD with redirect should be followed', function(done) { - // Redirects are automatically followed, because redirects are to be - // followed automatically per specification regardless of the HTTP verb. - request(cors_anywhere) - .head('/example.com/redirect') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('some-header', 'value') - .expect('x-request-url', 'http://example.com/redirect') - .expect('x-cors-redirect-1', '302 http://example.com/redirecttarget') - .expect('x-final-url', 'http://example.com/redirecttarget') - .expect('access-control-expose-headers', /some-header,x-final-url/) - .expectNoHeader('header at redirect') - .expect(200, undefined, done); - }); - - it('GET with redirect should be followed', function(done) { - request(cors_anywhere) - .get('/example.com/redirect') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('some-header', 'value') - .expect('x-request-url', 'http://example.com/redirect') - .expect('x-cors-redirect-1', '302 http://example.com/redirecttarget') - .expect('x-final-url', 'http://example.com/redirecttarget') - .expect('access-control-expose-headers', /some-header,x-final-url/) - .expectNoHeader('header at redirect') - .expect(200, 'redirect target', done); - }); - - it('GET with redirect loop should interrupt', function(done) { - request(cors_anywhere) - .get('/example.com/redirectloop') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/redirectloop') - .expect('x-cors-redirect-1', '302 http://example.com/redirectloop') - .expect('x-cors-redirect-2', '302 http://example.com/redirectloop') - .expect('x-cors-redirect-3', '302 http://example.com/redirectloop') - .expect('x-cors-redirect-4', '302 http://example.com/redirectloop') - .expect('x-cors-redirect-5', '302 http://example.com/redirectloop') - .expect('Location', /^http:\/\/127.0.0.1:\d+\/http:\/\/example.com\/redirectloop$/) - .expect(302, 'redirecting ad infinitum...', done); - }); - - it('POST with 302 redirect should be followed', function(done) { - request(cors_anywhere) - .post('/example.com/redirectpost') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/redirectpost') - .expect('x-cors-redirect-1', '302 http://example.com/redirectposttarget') - .expect('x-final-url', 'http://example.com/redirectposttarget') - .expect('access-control-expose-headers', /x-final-url/) - .expect(200, 'post target', done); - }); - - it('GET with 302 redirect without Location header should not be followed', function(done) { - // There is nothing to follow, so let the browser decide what to do with it. - request(cors_anywhere) - .get('/example.com/redirectwithoutlocation') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/redirectwithoutlocation') - .expect('x-final-url', 'http://example.com/redirectwithoutlocation') - .expect('access-control-expose-headers', /x-final-url/) - .expect(302, 'maybe found', done); - }); - - it('GET with 302 redirect to an invalid Location should not be followed', function(done) { - // There is nothing to follow, so let the browser decide what to do with it. - request(cors_anywhere) - .get('/example.com/redirectinvalidlocation') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/redirectinvalidlocation') - .expect('x-final-url', 'http://example.com/redirectinvalidlocation') - .expect('access-control-expose-headers', /x-final-url/) - .expect('Location', 'http:///') - .expect(302, 'redirecting to junk...', done); - }); - - it('POST with 307 redirect should not be handled', function(done) { - // Because of implementation difficulties (having to keep the request body - // in memory), handling HTTP 307/308 redirects is deferred to the requestor. - request(cors_anywhere) - .post('/example.com/redirect307') - .redirects(0) - .expect('Access-Control-Allow-Origin', '*') - .expect('x-request-url', 'http://example.com/redirect307') - .expect('Location', /^http:\/\/127.0.0.1:\d+\/http:\/\/example.com\/redirectposttarget$/) - .expect('x-final-url', 'http://example.com/redirect307') - .expect('access-control-expose-headers', /x-final-url/) - .expect(307, 'redirecting...', done); - }); - - it('OPTIONS /', function(done) { - request(cors_anywhere) - .options('/') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, '', done); - }); - - it('OPTIONS / with Access-Control-Request-Method / -Headers', function(done) { - request(cors_anywhere) - .options('/') - .set('Access-Control-Request-Method', 'DELETE') - .set('Access-Control-Request-Headers', 'X-Tralala') - .expect('Access-Control-Allow-Origin', '*') - .expect('Access-Control-Allow-Methods', 'DELETE') - .expect('Access-Control-Allow-Headers', 'X-Tralala') - .expect(200, '', done); - }); - - it('OPTIONS //bogus', function(done) { - // The preflight request always succeeds, regardless of whether the request - // is valid. - request(cors_anywhere) - .options('//bogus') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, '', done); - }); - - it('X-Forwarded-* headers', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'http', - }, done); - }); - - it('X-Forwarded-* headers (non-standard port)', function(done) { - request(cors_anywhere) - .get('/example.com:1337/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com:1337', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'http', - }, done); - }); - - it('X-Forwarded-* headers (https)', function(done) { - request(cors_anywhere) - .get('/https://example.com/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'http', - }, done); - }); - - it('Ignore cookies', function(done) { - request(cors_anywhere) - .get('/example.com/setcookie') - .expect('Access-Control-Allow-Origin', '*') - .expect('Set-Cookie3', 'z') - .expectNoHeader('set-cookie') - .expectNoHeader('set-cookie2', done); - }); -}); - -describe('Proxy errors', function() { - before(function() { - cors_anywhere = createServer(); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - var bad_http_server; - var bad_http_server_url; - before(function() { - bad_http_server = http.createServer(function(req, res) { - res.writeHead(418, { - 'Content-Length': 'Not a digit', - }); - res.end('This response has an invalid Content-Length header.'); - }); - bad_http_server_url = 'http://127.0.0.1:' + bad_http_server.listen(0).address().port; - }); - after(function(done) { - bad_http_server.close(function() { - done(); - }); - }); - - var bad_status_http_server; - var bad_status_http_server_url; - before(function() { - bad_status_http_server = require('net').createServer(function(socket) { - socket.setEncoding('utf-8'); - socket.on('data', function(data) { - if (data.indexOf('\r\n') >= 0) { - // Assume end of headers. - socket.write('HTTP/1.0 0\r\n'); - socket.write('Content-Length: 0\r\n'); - socket.end('\r\n'); - } - }); - }); - bad_status_http_server_url = 'http://127.0.0.1:' + bad_status_http_server.listen(0).address().port; - }); - after(function(done) { - bad_status_http_server.close(function() { - done(); - }); - }); - - var bad_tcp_server; - var bad_tcp_server_url; - before(function() { - bad_tcp_server = require('net').createServer(function(socket) { - socket.setEncoding('utf-8'); - socket.on('data', function(data) { - if (data.indexOf('\r\n') >= 0) { - // Assume end of headers. - socket.write('HTTP/1.1 418 OK\r\n'); - socket.write('Transfer-Encoding: chunked\r\n'); - socket.write('\r\n'); - socket.end('JK I lied, this is NOT a chunked response!'); - } - }); - }); - bad_tcp_server_url = 'http://127.0.0.1:' + bad_tcp_server.listen(0).address().port; - }); - after(function(done) { - bad_tcp_server.close(function() { - done(); - }); - }); - - it('Proxy error', function(done) { - request(cors_anywhere) - .get('/example.com/proxyerror') - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Not found because of proxy error: Error: throw node', done); - }); - - it('Content-Length mismatch', function(done) { - var errorMessage = 'Error: Parse Error: Invalid character in Content-Length'; - // <13.0.0: https://github.com/nodejs/node/commit/ba565a37349e81c9d2402b0c8ef05ab39dca8968 - // <12.7.0: https://github.com/nodejs/node/pull/28817 - var nodev = process.versions.node.split('.').map(function(v) { return parseInt(v); }); - if (nodev[0] < 12 || - nodev[0] === 12 && nodev[1] < 7) { - errorMessage = 'Error: Parse Error'; - } - request(cors_anywhere) - .get('/' + bad_http_server_url) - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Not found because of proxy error: ' + errorMessage, done); - }); - - it('Invalid HTTP status code', function(done) { - // Strict HTTP status validation was introduced in Node 4.5.5+, 5.11.0+. - // https://github.com/nodejs/node/pull/6291 - var nodev = process.versions.node.split('.').map(function(v) { return parseInt(v); }); - if (nodev[0] < 4 || - nodev[0] === 4 && nodev[1] < 5 || - nodev[0] === 4 && nodev[1] === 5 && nodev[2] < 5 || - nodev[0] === 5 && nodev[1] < 11) { - this.skip(); - } - - var errorMessage = 'RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: 0'; - if (parseInt(process.versions.node, 10) < 9) { - errorMessage = 'RangeError: Invalid status code: 0'; - } - request(cors_anywhere) - .get('/' + bad_status_http_server_url) - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Not found because of proxy error: ' + errorMessage, done); - }); - - it('Content-Encoding invalid body', function(done) { - // The HTTP status can't be changed because the headers have already been - // sent. - request(cors_anywhere) - .get('/' + bad_tcp_server_url) - .expect('Access-Control-Allow-Origin', '*') - .expect(418, '', done); - }); - - it('Invalid header values', function(done) { - if (parseInt(process.versions.node, 10) < 6) { - // >=6.0.0: https://github.com/nodejs/node/commit/7bef1b790727430cb82bf8be80cfe058480de100 - this.skip(); - } - // >=9.0.0: https://github.com/nodejs/node/commit/11a2ca29babcb35132e7d93244b69c544d52dfe4 - var errorMessage = 'TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["headername"]'; - if (parseInt(process.versions.node, 10) < 9) { - // >=6.0.0, <9.0.0: https://github.com/nodejs/node/commit/7bef1b790727430cb82bf8be80cfe058480de100 - errorMessage = 'TypeError: The header content contains invalid characters'; - } - stopServer(function() { - cors_anywhere = createServer({ - // Setting an invalid header below in request(...).set(...) would trigger - // a header validation error in superagent. So we use setHeaders to test - // the attempt to proxy a request with invalid request headers. - setHeaders: {headername: 'invalid\x01value'}, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/' + bad_tcp_server_url) // Any URL that isn't intercepted by Nock would do. - .expect('Access-Control-Allow-Origin', '*') - .expect(404, 'Not found because of proxy error: ' + errorMessage, done); - }); - }); -}); - -describe('server on https', function() { - var NODE_TLS_REJECT_UNAUTHORIZED; - before(function() { - cors_anywhere = createServer({ - httpsOptions: { - key: fs.readFileSync(path.join(__dirname, 'key.pem')), - cert: fs.readFileSync(path.join(__dirname, 'cert.pem')), - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - // Disable certificate validation in case the certificate expires. - NODE_TLS_REJECT_UNAUTHORIZED = process.env.NODE_TLS_REJECT_UNAUTHORIZED; - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - }); - after(function(done) { - if (NODE_TLS_REJECT_UNAUTHORIZED === undefined) { - delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; - } else { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = NODE_TLS_REJECT_UNAUTHORIZED; - } - stopServer(done); - }); - - it('X-Forwarded-* headers (http)', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'https', - }, done); - }); - - it('X-Forwarded-* headers (https)', function(done) { - request(cors_anywhere) - .get('/https://example.com/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'https', - }, done); - }); - - it('X-Forwarded-* headers (https, non-standard port)', function(done) { - request(cors_anywhere) - .get('/https://example.com:1337/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com:1337', - 'x-forwarded-port': String(cors_anywhere_port), - 'x-forwarded-proto': 'https', - }, done); - }); -}); - -describe('NODE_TLS_REJECT_UNAUTHORIZED', function() { - var NODE_TLS_REJECT_UNAUTHORIZED; - var bad_https_server; - var bad_https_server_port; - - var certErrorMessage = 'Error: certificate has expired'; - // <0.11.11: https://github.com/nodejs/node/commit/262a752c2943842df7babdf55a034beca68794cd - if (/^0\.(?!11\.1[1-4]|12\.)/.test(process.versions.node)) { - certErrorMessage = 'Error: CERT_HAS_EXPIRED'; - } - - before(function() { - cors_anywhere = createServer({}); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(function(done) { - stopServer(done); - }); - - before(function() { - bad_https_server = https.createServer({ - // rejectUnauthorized: false, - key: fs.readFileSync(path.join(__dirname, 'key.pem')), - cert: fs.readFileSync(path.join(__dirname, 'cert.pem')), - }, function(req, res) { - res.end('Response from server with expired cert'); - }); - bad_https_server_port = bad_https_server.listen(0).address().port; - - NODE_TLS_REJECT_UNAUTHORIZED = process.env.NODE_TLS_REJECT_UNAUTHORIZED; - }); - after(function(done) { - if (NODE_TLS_REJECT_UNAUTHORIZED === undefined) { - delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; - } else { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = NODE_TLS_REJECT_UNAUTHORIZED; - } - bad_https_server.close(function() { - done(); - }); - }); - - it('respects certificate errors by default', function(done) { - // Test is expected to run without NODE_TLS_REJECT_UNAUTHORIZED=0 - request(cors_anywhere) - .get('/https://127.0.0.1:' + bad_https_server_port) - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expect('Not found because of proxy error: ' + certErrorMessage, done); - }); - - it('ignore certificate errors via NODE_TLS_REJECT_UNAUTHORIZED=0', function(done) { - stopServer(function() { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - cors_anywhere = createServer({}); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/https://127.0.0.1:' + bad_https_server_port) - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expect('Response from server with expired cert', done); - }); - }); - - it('respects certificate errors when httpProxyOptions.secure=true', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - httpProxyOptions: { - secure: true, - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/https://127.0.0.1:' + bad_https_server_port) - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expect('Not found because of proxy error: ' + certErrorMessage, done); - }); - }); -}); - -describe('originBlacklist', function() { - before(function() { - cors_anywhere = createServer({ - originBlacklist: ['http://denied.origin.test'], - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com with denied origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'http://denied.origin.test') - .expect('Access-Control-Allow-Origin', '*') - .expect(403, done); - }); - - it('GET /example.com without denied origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'https://denied.origin.test') // Note: different scheme! - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); - - it('GET /example.com without origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); -}); - -describe('originWhitelist', function() { - before(function() { - cors_anywhere = createServer({ - originWhitelist: ['https://permitted.origin.test'], - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com with permitted origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'https://permitted.origin.test') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); - - it('GET /example.com without permitted origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'http://permitted.origin.test') // Note: different scheme! - .expect('Access-Control-Allow-Origin', '*') - .expect(403, done); - }); - - it('GET /example.com without origin', function(done) { - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(403, done); - }); -}); - -describe('handleInitialRequest', function() { - afterEach(stopServer); - - it('GET / with handleInitialRequest', function(done) { - cors_anywhere = createServer({ - handleInitialRequest: function(req, res, location) { - res.writeHead(419); - res.end('res:' + (location && location.href)); - return true; - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/') - .expect(419, 'res:null', done); - }); - - it('GET /dummy with handleInitialRequest', function(done) { - cors_anywhere = createServer({ - handleInitialRequest: function(req, res, location) { - res.writeHead(419); - res.end('res:' + (location && location.href)); - return true; - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/dummy') - .expect(419, 'res:http://dummy/', done); - }); - - it('GET /example.com with handleInitialRequest', function(done) { - cors_anywhere = createServer({ - handleInitialRequest: function(req, res, location) { - res.setHeader('X-Extra-Header', 'hello ' + location.href); - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com') - .set('Origin', 'null') - .expect('Access-Control-Allow-Origin', '*') - .expect('X-Extra-Header', 'hello http://example.com/') - .expect(200, 'Response from example.com', done); - }); -}); - -describe('checkRateLimit', function() { - afterEach(stopServer); - - it('GET /example.com without rate-limit', function(done) { - cors_anywhere = createServer({ - checkRateLimit: function() {}, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); - - it('GET /example.com with rate-limit', function(done) { - cors_anywhere = createServer({ - checkRateLimit: function(origin) { - // Non-empty value. Let's return the origin parameter so that we also verify that the - // the parameter is really the origin. - return '[' + origin + ']'; - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'http://example.net:1234') - .expect('Access-Control-Allow-Origin', '*') - .expect(429, done, - 'The origin "http://example.net" has sent too many requests.\n[http://example.com:1234]'); - }); -}); - -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({ - requireHeader: ['origin', 'x-requested-with'], - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com without header', function(done) { - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(400, 'Missing required request header. Must specify one of: origin,x-requested-with', done); - }); - - it('GET /example.com with X-Requested-With header', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('X-Requested-With', '') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); - - it('GET /example.com with Origin header', function(done) { - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'null') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, done); - }); - - it('GET /example.com without header (requireHeader as string)', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - requireHeader: 'origin', - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(400, 'Missing required request header. Must specify one of: origin', done); - }); - }); - - it('GET /example.com with header (requireHeader as string)', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - requireHeader: 'origin', - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'null') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from example.com', done); - }); - }); - - it('GET /example.com without header (requireHeader as string, uppercase)', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - requireHeader: 'ORIGIN', - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(400, 'Missing required request header. Must specify one of: origin', done); - }); - }); - - it('GET /example.com with header (requireHeader as string, uppercase)', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - requireHeader: 'ORIGIN', - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .set('Origin', 'null') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from example.com', done); - }); - }); - - it('GET /example.com (requireHeader is an empty array)', function(done) { - stopServer(function() { - cors_anywhere = createServer({ - requireHeader: [], - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - request(cors_anywhere) - .get('/example.com/') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from example.com', done); - }); - }); -}); - -describe('removeHeaders', function() { - before(function() { - cors_anywhere = createServer({ - removeHeaders: ['cookie', 'cookie2'], - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com with request cookie', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('cookie', 'a') - .set('cookie2', 'b') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - }, done); - }); - - it('GET /example.com with unknown header', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('cookie', 'a') - .set('cookie2', 'b') - .set('cookie3', 'c') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - cookie3: 'c', - }, done); - }); -}); - -describe('setHeaders', function() { - before(function() { - cors_anywhere = createServer({ - setHeaders: {'x-powered-by': 'CORS Anywhere'}, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-powered-by': 'CORS Anywhere', - }, done); - }); - - it('GET /example.com should replace header', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('x-powered-by', 'should be replaced') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-powered-by': 'CORS Anywhere', - }, done); - }); -}); - -describe('setHeaders + removeHeaders', function() { - before(function() { - // setHeaders takes precedence over removeHeaders - cors_anywhere = createServer({ - removeHeaders: ['x-powered-by'], - setHeaders: {'x-powered-by': 'CORS Anywhere'}, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('GET /example.com', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-powered-by': 'CORS Anywhere', - }, done); - }); - - it('GET /example.com should replace header', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('x-powered-by', 'should be replaced') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - 'x-powered-by': 'CORS Anywhere', - }, done); - }); -}); - -describe('Access-Control-Max-Age set', function() { - before(function() { - cors_anywhere = createServer({ - corsMaxAge: 600, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('OPTIONS /', function(done) { - request(cors_anywhere) - .options('/') - .expect('Access-Control-Allow-Origin', '*') - .expect('Access-Control-Max-Age', '600') - .expect(200, '', done); - }); - - it('OPTIONS /example.com', function(done) { - request(cors_anywhere) - .options('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect('Access-Control-Max-Age', '600') - .expect(200, '', done); - }); - - it('GET / no Access-Control-Max-Age on GET', function(done) { - request(cors_anywhere) - .get('/') - .type('text/plain') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, helpText, done); - }); - - it('GET /example.com no Access-Control-Max-Age on GET', function(done) { - request(cors_anywhere) - .get('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, 'Response from example.com', done); - }); -}); - -describe('Access-Control-Max-Age not set', function() { - before(function() { - cors_anywhere = createServer(); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('OPTIONS / corsMaxAge disabled', function(done) { - request(cors_anywhere) - .options('/') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, '', done); - }); - - it('OPTIONS /example.com corsMaxAge disabled', function(done) { - request(cors_anywhere) - .options('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, '', done); - }); - - it('GET /', function(done) { - request(cors_anywhere) - .get('/') - .type('text/plain') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, helpText, done); - }); - - it('GET /example.com', function(done) { - request(cors_anywhere) - .get('/example.com') - .expect('Access-Control-Allow-Origin', '*') - .expectNoHeader('Access-Control-Max-Age') - .expect(200, 'Response from example.com', done); - }); -}); - -describe('httpProxyOptions.xfwd=false', function() { - before(function() { - cors_anywhere = createServer({ - httpProxyOptions: { - xfwd: false, - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - after(stopServer); - - it('X-Forwarded-* headers should not be set', function(done) { - request(cors_anywhere) - .get('/example.com/echoheaders') - .set('test-include-xfwd', '') - .expect('Access-Control-Allow-Origin', '*') - .expectJSON({ - host: 'example.com', - }, done); - }); -}); - -describe('httpProxyOptions.getProxyForUrl', function() { - var proxy_server; - var proxy_url; - before(function() { - // Using a real server instead of a mock because Nock doesn't can't mock proxies. - proxy_server = http.createServer(function(req, res) { - res.end(req.method + ' ' + req.url + ' Host=' + req.headers.host); - }); - proxy_url = 'http://127.0.0.1:' + proxy_server.listen(0).address().port; - - cors_anywhere = createServer({ - httpProxyOptions: { - xfwd: false, - }, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - }); - afterEach(function() { - // Assuming that they were not set before. - delete process.env.https_proxy; - delete process.env.http_proxy; - delete process.env.no_proxy; - }); - after(function(done) { - proxy_server.close(function() { - done(); - }); - }); - after(stopServer); - - it('http_proxy should be respected for matching domains', function(done) { - process.env.http_proxy = proxy_url; - - request(cors_anywhere) - .get('/http://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'GET http://example.com/ Host=example.com', done); - }); - - it('http_proxy should be ignored for http URLs', function(done) { - process.env.http_proxy = proxy_url; - request(cors_anywhere) - .get('/https://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from https://example.com', done); - }); - - it('https_proxy should be respected for matching domains', function(done) { - process.env.https_proxy = proxy_url; - - request(cors_anywhere) - .get('/https://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'GET https://example.com/ Host=example.com', done); - }); - - it('https_proxy should be ignored for http URLs', function(done) { - process.env.https_proxy = proxy_url; - request(cors_anywhere) - .get('/http://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from example.com', done); - }); - - it('https_proxy + no_proxy should not intercept requests in no_proxy', function(done) { - process.env.https_proxy = proxy_url; - process.env.no_proxy = 'example.com:443'; - request(cors_anywhere) - .get('/https://example.com') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, 'Response from https://example.com', done); - }); -}); - -describe('helpFile', function() { - - afterEach(stopServer); - - it('GET / with custom text helpFile', function(done) { - var customHelpTextPath = path.join(__dirname, './customHelp.txt'); - var customHelpText = fs.readFileSync(customHelpTextPath, {encoding: 'utf8'}); - - cors_anywhere = createServer({ - helpFile: customHelpTextPath, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - - request(cors_anywhere) - .get('/') - .type('text/plain') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, customHelpText, done); - }); - - it('GET / with custom HTML helpFile', function(done) { - var customHelpTextPath = path.join(__dirname, './customHelp.html'); - var customHelpText = fs.readFileSync(customHelpTextPath, {encoding: 'utf8'}); - - cors_anywhere = createServer({ - helpFile: customHelpTextPath, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - - request(cors_anywhere) - .get('/') - .type('text/html') - .expect('Access-Control-Allow-Origin', '*') - .expect(200, customHelpText, done); - }); - - it('GET / with non-existent help file', function(done) { - var customHelpTextPath = path.join(__dirname, 'Some non-existing file.'); - - cors_anywhere = createServer({ - helpFile: customHelpTextPath, - }); - cors_anywhere_port = cors_anywhere.listen(0).address().port; - - request(cors_anywhere) - .get('/') - .type('text/plain') - .expect('Access-Control-Allow-Origin', '*') - .expect(500, '', done); - }); -});