78 lines
2.8 KiB
JavaScript
78 lines
2.8 KiB
JavaScript
// DSH LAN proxy (Host/Origin rewrite mode)
|
|
// Forwards 0.0.0.0:3081 -> 127.0.0.1:3080 (HTTP + WebSocket upgrade)
|
|
// Rewrites Host and Origin to the loopback authority so requests pass the
|
|
// DSH API trust fence (Host must be loopback or a trustedHosts entry).
|
|
// Cookie authority stays consistent because it is derived from the request
|
|
// Host the server sees, which is always 127.0.0.1:3080 through this proxy.
|
|
import http from 'node:http';
|
|
|
|
const LISTEN_PORT = 3081;
|
|
const LISTEN_HOST = '0.0.0.0';
|
|
const UPSTREAM_HOST = '127.0.0.1';
|
|
const UPSTREAM_PORT = 3080;
|
|
const UPSTREAM_AUTHORITY = `${UPSTREAM_HOST}:${UPSTREAM_PORT}`;
|
|
const UPSTREAM_ORIGIN = `http://${UPSTREAM_AUTHORITY}`;
|
|
|
|
function rewriteHeaders(req) {
|
|
const h = { ...req.headers };
|
|
h['host'] = UPSTREAM_AUTHORITY;
|
|
if (h['origin'] !== undefined) h['origin'] = UPSTREAM_ORIGIN;
|
|
h['x-forwarded-for'] = req.socket.remoteAddress || '';
|
|
h['x-forwarded-host'] = req.headers.host || '';
|
|
h['x-forwarded-proto'] = 'http';
|
|
return h;
|
|
}
|
|
|
|
const server = http.createServer((req, res) => {
|
|
const proxyReq = http.request(
|
|
{
|
|
host: UPSTREAM_HOST,
|
|
port: UPSTREAM_PORT,
|
|
path: req.url,
|
|
method: req.method,
|
|
headers: rewriteHeaders(req),
|
|
},
|
|
(proxyRes) => {
|
|
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
|
|
proxyRes.pipe(res);
|
|
}
|
|
);
|
|
proxyReq.on('error', (err) => {
|
|
if (!res.headersSent) res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
res.end(`DSH LAN proxy: upstream(${UPSTREAM_AUTHORITY}) error: ${err.message}`);
|
|
});
|
|
req.pipe(proxyReq);
|
|
});
|
|
|
|
server.on('upgrade', (req, socket, head) => {
|
|
const proxyReq = http.request({
|
|
host: UPSTREAM_HOST,
|
|
port: UPSTREAM_PORT,
|
|
path: req.url,
|
|
method: req.method,
|
|
headers: rewriteHeaders(req),
|
|
});
|
|
proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => {
|
|
const lines = ['HTTP/1.1 101 Switching Protocols'];
|
|
for (const [k, v] of Object.entries(proxyRes.headers)) lines.push(`${k}: ${v}`);
|
|
socket.write(lines.join('\r\n') + '\r\n\r\n');
|
|
if (proxyHead && proxyHead.length) socket.write(proxyHead);
|
|
proxySocket.pipe(socket);
|
|
socket.pipe(proxySocket);
|
|
proxySocket.on('error', () => socket.destroy());
|
|
socket.on('error', () => proxySocket.destroy());
|
|
});
|
|
proxyReq.on('response', (proxyRes) => {
|
|
const lines = [`HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage || ''}`];
|
|
for (const [k, v] of Object.entries(proxyRes.headers)) lines.push(`${k}: ${v}`);
|
|
socket.end(lines.join('\r\n') + '\r\n\r\n');
|
|
proxyRes.pipe(socket);
|
|
});
|
|
proxyReq.on('error', () => socket.destroy());
|
|
proxyReq.end(head && head.length ? head : undefined);
|
|
});
|
|
|
|
server.listen(LISTEN_PORT, LISTEN_HOST, () => {
|
|
console.log(`[dsh-lan-proxy] ${LISTEN_HOST}:${LISTEN_PORT} -> ${UPSTREAM_AUTHORITY} (Host/Origin rewritten)`);
|
|
});
|