| | |
| | |
| | |
| | 'use strict'; |
| |
|
| | var httpProxy = require('http-proxy'); |
| | var net = require('net'); |
| | var url = require('url'); |
| | var getProxyForUrl = require('proxy-from-env').getProxyForUrl; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | function isValidHostName(hostname) { |
| | return !!( |
| | hostname.indexOf('.') > 0 || |
| | net.isIPv4(hostname) || |
| | net.isIPv6(hostname) |
| | ); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | function withCORS(headers, request) { |
| | headers['access-control-allow-origin'] = '*'; |
| | var corsMaxAge = request.corsAnywhereRequestState.corsMaxAge; |
| | if (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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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, |
| | }, |
| | |
| | |
| | buffer: { |
| | pipe: function (proxyReq) { |
| | var proxyReqOn = proxyReq.on; |
| | |
| | |
| | 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) { |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | proxyReq.emit('error', err); |
| | } |
| | } |
| | }); |
| | }; |
| | return req.pipe(proxyReq); |
| | }, |
| | }, |
| | }; |
| |
|
| | var proxyThroughUrl = req.corsAnywhereRequestState.getProxyForUrl(location.href); |
| | if (proxyThroughUrl) { |
| | proxyOptions.target = proxyThroughUrl; |
| | proxyOptions.toProxy = true; |
| | |
| | |
| | req.url = location.href; |
| | } |
| |
|
| | |
| | try { |
| | proxy.web(req, res, proxyOptions); |
| | } catch (err) { |
| | proxy.emit('error', err, req, 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); |
| | } |
| | |
| | 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) { |
| | |
| | requestState.redirectCount_ = requestState.redirectCount_ + 1 || 1; |
| | if (requestState.redirectCount_ <= requestState.maxRedirects) { |
| | |
| | |
| | |
| | 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; |
| |
|
| | |
| | req.removeAllListeners(); |
| |
|
| | |
| | |
| | |
| | proxyReq.removeAllListeners('error'); |
| | proxyReq.once('error', function catchAndIgnoreError() { }); |
| | proxyReq.abort(); |
| |
|
| | |
| | proxyRequest(req, res, proxy); |
| | return false; |
| | } |
| | } |
| | proxyRes.headers.location = requestState.proxyBaseUrl + '/' + locationHeader; |
| | } |
| | } |
| |
|
| | |
| | delete proxyRes.headers['set-cookie']; |
| | delete proxyRes.headers['set-cookie2']; |
| |
|
| | proxyRes.headers['x-final-url'] = requestState.location.href; |
| | withCORS(proxyRes.headers, req); |
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | function parseURL(req_url) { |
| |
|
| | var host = req_url.slice(0, 8); |
| |
|
| | if (host.startsWith('/')){ |
| | return null; |
| | } else if (host.includes("://")) { |
| |
|
| | } else if (host.includes(':/')) { |
| | req_url = new URL(req_url).href; |
| | } else { |
| | req_url = "http://" + req_url; |
| | } |
| |
|
| | return url.parse(req_url); |
| | } |
| |
|
| | |
| | function getHandler(options, proxy) { |
| | var corsAnywhere = { |
| | getProxyForUrl: getProxyForUrl, |
| | maxRedirects: 5, |
| | keywordBlacklist: [], |
| | originBlacklist: [], |
| | originWhitelist: [], |
| | checkRateLimit: null, |
| | redirectSameOrigin: false, |
| | requireHeader: null, |
| | removeHeaders: [], |
| | setHeaders: {}, |
| | corsMaxAge: 0 |
| | }; |
| |
|
| | Object.keys(corsAnywhere).forEach(function (option) { |
| | if (Object.prototype.hasOwnProperty.call(options, option)) { |
| | corsAnywhere[option] = options[option]; |
| | } |
| | }); |
| |
|
| | |
| | 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, |
| | }; |
| |
|
| | let clientip = req.headers['x-forwarded-for'] || |
| | req.connection.remoteAddress || |
| | req.socket.remoteAddress || |
| | req.connection.socket.remoteAddress; |
| |
|
| | console.log(JSON.stringify([ |
| | new Date().toISOString(), |
| | req.method, |
| | req.url, |
| | clientip, |
| | req.headers["user-agent"] |
| | ])); |
| |
|
| | var cors_headers = withCORS({}, req); |
| | if (req.method === 'OPTIONS') { |
| | |
| | res.writeHead(200, cors_headers); |
| | res.end(); |
| | return; |
| | } |
| |
|
| | |
| | try { |
| | var raw = req.url; |
| | var idx = raw.indexOf('/', 1); |
| | var url = raw.slice(idx + 1); |
| | var location = parseURL(url); |
| | }catch(err){ |
| | console.log(err.message); |
| | } |
| |
|
| | if (!location) { |
| | |
| | res.writeHead(200, { 'Content-Type': 'text/plain' }); |
| | res.end('404'); |
| | return; |
| | } |
| |
|
| | if (location.host === 'iscorsneeded') { |
| | |
| | |
| | |
| | res.writeHead(200, { 'Content-Type': 'text/plain' }); |
| | res.end('no'); |
| | return; |
| | } |
| |
|
| | if (location.port > 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)) { |
| | |
| | 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; |
| | } |
| |
|
| | if (corsAnywhere.keywordBlacklist.filter(x => location.href.indexOf(x) >= 0).length > 0) { |
| | res.writeHead(403, 'Forbidden', cors_headers); |
| | res.end('The keyword "' + corsAnywhere.keywordBlacklist.join(" ") + '" was blacklisted by the operator of this proxy.'); |
| | return; |
| | } |
| |
|
| | if (corsAnywhere.originBlacklist.indexOf(location.hostname) >= 0) { |
| | res.writeHead(403, 'Forbidden', cors_headers); |
| | res.end('The origin "' + location.hostname + '" was blacklisted by the operator of this proxy.'); |
| | return; |
| | } |
| |
|
| | if (corsAnywhere.originWhitelist.length && corsAnywhere.originWhitelist.indexOf(location.hostname) === -1) { |
| | res.writeHead(403, 'Forbidden', cors_headers); |
| | res.end('The origin "' + location.hostname + '" was not whitelisted by the operator of this proxy.'); |
| | return; |
| | } |
| |
|
| | var origin = req.headers.origin || ''; |
| |
|
| | if (corsAnywhere.redirectSameOrigin && origin && location.href[origin.length] === '/' && |
| | location.href.lastIndexOf(origin, 0) === 0) { |
| | |
| | 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]; |
| | }); |
| |
|
| | var host = raw.slice(1, idx); |
| | var referer = host.indexOf('.') != -1 ? 'https://' + host : location.href; |
| | console.log('网址' + location.href + ' 来路' + referer) |
| | req.headers.referer = referer; |
| |
|
| | req.corsAnywhereRequestState.location = location; |
| | req.corsAnywhereRequestState.proxyBaseUrl = proxyBaseUrl; |
| |
|
| | proxyRequest(req, res, proxy); |
| | }; |
| | } |
| |
|
| | |
| | |
| | function createServer(options) { |
| | options = options || {}; |
| |
|
| | |
| | var httpProxyOptions = { |
| | xfwd: true, |
| | }; |
| | |
| | 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); |
| | } |
| |
|
| | |
| | proxy.on('error', function (err, req, res) { |
| | if (res.headersSent) { |
| | |
| | |
| | |
| | |
| | |
| | if (res.writableEnded === false) { |
| | res.end(); |
| | } |
| | return; |
| | } |
| |
|
| | |
| | |
| | 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; |
| | }; |
| |
|
| | process.on('uncaughtException', function (exception) { |
| | console.log(exception); |
| | }); |
| |
|
| | |
| |
|
| | |
| | var host = process.env.HOST || '0.0.0.0'; |
| | |
| | var port = process.env.PORT || 3000; |
| |
|
| | createServer({ |
| | |
| | removeHeaders: [ |
| | 'x-heroku-queue-wait-time', |
| | 'x-heroku-queue-depth', |
| | 'x-heroku-dynos-in-use', |
| | 'x-request-start', |
| | ], |
| | keywordBlacklist: [".mpd", ".m4v"], |
| | originBlacklist: [], |
| | originWhitelist: [], |
| | redirectSameOrigin: true, |
| | |
| | httpProxyOptions: { |
| | xfwd: false, |
| | secure: false |
| | }, |
| | }).listen(port, host, function () { |
| | console.log(new Date().toISOString() + ' Running CORS Anywhere on ' + host + ':' + port); |
| | }); |