| import express from 'express'; |
| import fetch, { Headers as FetchHeaders } from 'node-fetch'; |
|
|
| const app = express(); |
| app.use(express.raw({ |
| type: '*/*', |
| limit: '100mb' |
| })); |
|
|
| app.options('*', (req, res) => { |
| const origin = req.headers.origin; |
|
|
| if (origin) { |
| res.setHeader('Access-Control-Allow-Origin', origin); |
| res.setHeader('Access-Control-Allow-Credentials', 'true'); |
| } else { |
| res.setHeader('Access-Control-Allow-Origin', '*'); |
| } |
|
|
| res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'); |
| |
| const requestedHeaders = req.headers['access-control-request-headers']; |
| if (requestedHeaders) { |
| res.setHeader('Access-Control-Allow-Headers', requestedHeaders); |
| } else { |
| res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, X-CSRF-Token, Accept, Origin'); |
| } |
| |
| res.setHeader('Access-Control-Max-Age', '86400'); |
|
|
| if (origin) { |
| res.setHeader('Vary', 'Origin'); |
| } |
|
|
| res.status(204).end(); |
| }); |
|
|
| app.get('/', (req, res) => { |
| const origin = req.headers.origin; |
| if (origin) { |
| res.setHeader('Access-Control-Allow-Origin', origin); |
| res.setHeader('Vary', 'Origin'); |
| if (req.method === 'OPTIONS') { |
| res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); |
| res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); |
| res.status(204).end(); |
| return; |
| } |
| } else { |
| res.setHeader('Access-Control-Allow-Origin', '*'); |
| } |
| res.send('Hello World'); |
| }); |
|
|
| app.all('*', async (req, res) => { |
| if (req.url === '/') { |
| return; |
| } |
|
|
| const clientRequestOrigin = req.headers.origin; |
|
|
| try { |
| let targetUrlString = req.url.substring(1); |
| |
| try { |
| targetUrlString = decodeURIComponent(targetUrlString); |
| } catch (e) {} |
|
|
| if (!targetUrlString) { |
| addCorsHeaders(res, clientRequestOrigin); |
| res.status(400).send('Target URL is missing in the path.'); |
| return; |
| } |
|
|
| let targetUrl; |
| try { |
| targetUrl = new URL(targetUrlString); |
| } catch (e) { |
| addCorsHeaders(res, clientRequestOrigin); |
| res.status(400).send(`Invalid target URL provided in path: ${targetUrlString}`); |
| return; |
| } |
| |
| const requestHeaders = {...req.headers}; |
| delete requestHeaders['host']; |
| delete requestHeaders['content-length']; |
|
|
| const response = await fetch(targetUrl.toString(), { |
| method: req.method, |
| headers: requestHeaders, |
| body: (req.method !== 'GET' && req.method !== 'HEAD') ? req.body : undefined, |
| redirect: 'manual', |
| compress: false |
| }); |
|
|
| response.headers.forEach((value, key) => { |
| const lowerKey = key.toLowerCase(); |
| if (!lowerKey.startsWith('access-control-') && |
| lowerKey !== 'strict-transport-security' && |
| lowerKey !== 'content-security-policy' && |
| lowerKey !== 'public-key-pins' && |
| lowerKey !== 'transfer-encoding' && |
| lowerKey !== 'connection' && |
| lowerKey !== 'keep-alive' && |
| lowerKey !== 'proxy-authenticate' && |
| lowerKey !== 'proxy-authorization' && |
| lowerKey !== 'te' && |
| lowerKey !== 'trailers' && |
| lowerKey !== 'upgrade' |
| ) { |
| res.setHeader(key, value); |
| } |
| }); |
| |
| addCorsHeaders(res, clientRequestOrigin); |
| const exposedHeaders = Array.from(response.headers.keys()).filter(key => |
| !key.toLowerCase().startsWith('access-control-') |
| ).join(', '); |
| if (exposedHeaders) { |
| res.setHeader('Access-Control-Expose-Headers', exposedHeaders || '*'); |
| } |
| |
| res.status(response.status); |
|
|
| if (response.body) { |
| response.body.pipe(res); |
| } else { |
| res.end(); |
| } |
|
|
| } catch (error) { |
| if (!res.headersSent) { |
| addCorsHeaders(res, clientRequestOrigin); |
| let statusCode = 500; |
| let message = 'Proxy error occurred.'; |
|
|
| if (error.code === 'ENOTFOUND') { |
| statusCode = 404; |
| message = `Target host not found: ${req.url.substring(1)}`; |
| } else if (error.message && error.message.includes('Invalid URL')) { |
| statusCode = 400; |
| message = `Invalid target URL in path: ${req.url.substring(1)}`; |
| } else if (error.code === 'ECONNREFUSED') { |
| statusCode = 502; |
| message = `Bad Gateway: Could not connect to target server at ${req.url.substring(1)}`; |
| } else if (error.code === 'ERR_INVALID_URL') { |
| statusCode = 400; |
| message = `Invalid target URL format in path: ${req.url.substring(1)}`; |
| } |
| res.status(statusCode).send(message); |
| } else { |
| res.end(); |
| } |
| } |
| }); |
|
|
| function addCorsHeaders(res, clientRequestOrigin) { |
| if (clientRequestOrigin) { |
| res.setHeader('Access-Control-Allow-Origin', clientRequestOrigin); |
| res.setHeader('Access-Control-Allow-Credentials', 'true'); |
| res.setHeader('Vary', 'Origin'); |
| } else { |
| res.setHeader('Access-Control-Allow-Origin', '*'); |
| } |
| } |
|
|
| const PORT = process.env.PORT || 7860; |
| const HOST = '0.0.0.0'; |
|
|
| app.listen(PORT, HOST); |