| |
| |
| |
| '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); |
| }); |