Spaces:
Runtime error
Runtime error
File size: 3,751 Bytes
fb38ec5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import http, { IncomingHttpHeaders } from "node:http";
import { PrepareRequestFunctionOpts, PrepareRequestFunctionResult } from "proxy-chain";
// Headers that are hop-by-hop and should not be forwarded RFC 2616, Section 13.5.1
export const hopByHopHeaders = new Set([
"connection",
"proxy-authenticate",
"proxy-authorization",
"keep-alive",
"te",
"trailers",
"transfer-encoding",
"upgrade",
]);
/**
* Creates a simple http proxy which reverse proxies the original request to the original host.
* There's an issue with proxy-chain's implementation causing corruption in our internals requests
*/
export const PassthroughServer = http.createServer((clientReq, clientRes) => {
const targetUrl = new URL(clientReq.url ?? "/", `http://${clientReq.headers.host}`);
const proxyHeaders: IncomingHttpHeaders = {};
for (const [key, value] of Object.entries(clientReq.headers)) {
const lowerKey = key.toLowerCase();
if (!hopByHopHeaders.has(lowerKey)) {
proxyHeaders[lowerKey] = value;
}
}
proxyHeaders["host"] = targetUrl.host;
proxyHeaders["x-forwarded-for"] = clientReq.socket.remoteAddress || "";
proxyHeaders["x-forwarded-proto"] = "http";
proxyHeaders["x-forwarded-host"] = clientReq.headers.host || "";
const proxyReq = http.request(
{
method: clientReq.method,
headers: proxyHeaders,
hostname: targetUrl.hostname,
port: targetUrl.port,
path: targetUrl.pathname + targetUrl.search,
},
(proxyRes) => {
const clientResHeaders = {};
for (const [key, value] of Object.entries(proxyRes.headers)) {
if (!hopByHopHeaders.has(key.toLowerCase())) {
clientResHeaders[key] = value;
}
}
clientRes.writeHead(proxyRes.statusCode ?? 500, clientResHeaders);
proxyRes.pipe(clientRes);
},
);
proxyReq.on("error", (err) => {
console.error(`Proxy error for ${targetUrl}:`, err);
if (!clientRes.headersSent) {
clientRes.writeHead(502);
}
clientRes.end("Proxy error: Could not connect to the target service.");
});
clientReq.on("error", (err) => {
console.error(`Client request error:`, err);
proxyReq.destroy();
});
clientReq.pipe(proxyReq);
});
type Result<T> = [err: Error, result: null] | [err: null, result: T];
/**
* There's an issue with proxy-chain's handling of chunked requests when doing a direct passthrough.
* This workaround forwards the requests manually and returns the response
*/
export const makePassthrough = function ({
request,
hostname,
port,
}: PrepareRequestFunctionOpts): NonNullable<
PrepareRequestFunctionResult["customResponseFunction"]
> {
return async () => {
const [err, proxyRes]: Result<http.IncomingMessage> = await new Promise((resolve) => {
const forward = http.request(
{
hostname,
port,
method: request.method,
path: request.url,
headers: request.headers,
},
(res) => resolve([null, res]),
);
forward.on("error", (err) => resolve([err, null]));
request.pipe(forward);
});
if (err) {
console.error(`Request failed "${err.name}": ${err.message}`);
throw err;
}
const chunks: Buffer[] = [];
for await (const chunk of proxyRes) chunks.push(chunk);
const body = Buffer.concat(chunks);
const headers: IncomingHttpHeaders = {};
for (const [k, v] of Object.entries(proxyRes.headers)) {
if (!hopByHopHeaders.has(k.toLowerCase()) && v !== undefined) {
headers[k] = Array.isArray(v) ? v.join(",") : v;
}
}
return {
statusCode: proxyRes.statusCode ?? 500,
headers: proxyRes.headers as Record<string, string>,
body,
};
};
};
|