Update ReverseProxy.js
Centralize request and response post processing
This commit is contained in:
@@ -21,7 +21,6 @@ class ReverseProxy {
|
||||
console.log(`Database opened with '${routeCount}' routes`);
|
||||
|
||||
// Bind to ports
|
||||
const bindedProcessReq = this.#ProcessRequest.bind(this);
|
||||
const httpPort = 80, httpsPort = 443;
|
||||
try {
|
||||
this.HttpEndpoint = await this.#CreateHttpEndpoint(httpPort);
|
||||
@@ -40,22 +39,7 @@ class ReverseProxy {
|
||||
this.HttpEndpoint.on("upgrade", this.#HandleWebSocketUpgrade.bind(this));
|
||||
this.HttpsEndpoint.on("upgrade", this.#HandleWebSocketUpgrade.bind(this));
|
||||
// This is for logging proxy results
|
||||
this.ProxyService.on("proxyRes", (proxyResponse, request, response) => {
|
||||
// Check if there is an auth/access error
|
||||
let internalStatus = response.statusCode;
|
||||
if (response.statusCode == 511) {
|
||||
if (IsNullOrUndefined(request.bailHandler)) {
|
||||
response.writeHead(404, { "Content-Type": "text/plain" });
|
||||
response.end(`You do not have the permissions to access '${host}', it's endpoint does not exist or the origin may be down`);
|
||||
console.log(`[${internalStatus} as 404] ${request.method} ${hostname ?? request.headers?.host}${request.url}`);
|
||||
proxyResponse.destroy();
|
||||
return;
|
||||
}
|
||||
return request.bailHandler(internalStatus);
|
||||
}
|
||||
// Else let the response go through
|
||||
console.log(`[${proxyResponse.statusCode}] ${request.method} '${request.headers?.host ?? ''}${request.url}'`);
|
||||
});
|
||||
this.ProxyService.on("proxyRes", this.#PostProcessResponse.bind(this));
|
||||
|
||||
// Start listening
|
||||
// except we already started listening when initializing the http/s endpoints
|
||||
@@ -63,7 +47,7 @@ class ReverseProxy {
|
||||
}
|
||||
|
||||
async #CreateHttpEndpoint(port = 80, { cancellationToken } = {}) {
|
||||
let endpoint = CreateHttpListener(this.#ProcessRequest.bind(this));
|
||||
let endpoint = CreateHttpListener(this.#HandleRequest.bind(this));
|
||||
const raceConditions = [
|
||||
once(endpoint, "listening"),
|
||||
once(endpoint, "error").then(([err]) => Promise.reject(err)),
|
||||
@@ -95,7 +79,7 @@ class ReverseProxy {
|
||||
let endpoint = CreateHttpsListener({
|
||||
key: readFileSync(keyPath),
|
||||
cert: readFileSync(certPath)
|
||||
}, this.#ProcessRequest.bind(this));
|
||||
}, this.#HandleRequest.bind(this));
|
||||
|
||||
// Subscribing to these events once before calling .listen
|
||||
// to make sure they get called, even though they should be
|
||||
@@ -147,7 +131,7 @@ class ReverseProxy {
|
||||
return urlStr.substring(pathStartIndex, queryStartIndex);
|
||||
}
|
||||
|
||||
#ProcessRequest(request, response) {
|
||||
#HandleRequest(request, response) {
|
||||
if (ReverseProxy.IsInternalAddress(request.socket.remoteAddress)) {
|
||||
switch (ReverseProxy.GetPath(request.url)) {
|
||||
case "/ready":
|
||||
@@ -160,42 +144,78 @@ class ReverseProxy {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Regular
|
||||
const { host } = request.headers;
|
||||
const resolvedHost = this.TryResolveInternalHost(host);
|
||||
const { hostname } = new URL(resolvedHost);
|
||||
|
||||
const _bail = (internalStatus = 404) => {
|
||||
response.writeHead(404, { "Content-Type": "text/plain" });
|
||||
response.end(`You do not have the permissions to access '${host}', it's endpoint does not exist or the origin may be down`);
|
||||
console.log(`[${internalStatus} as 404] ${request.method} ${hostname ?? request.headers?.host}${request.url}`);
|
||||
response.end(`You do not have the permissions to access '${request.originalHost}', it's endpoint does not exist or the origin may be down`);
|
||||
console.log(`[${internalStatus} as 404] ${request.method} ${request.headers?.host ?? ''}${request.url}`);
|
||||
};
|
||||
if (IsNullOrUndefined(resolvedHost))
|
||||
return _bail(404);
|
||||
try {
|
||||
request.headers.host = hostname;
|
||||
this.ProxyService.web(request, response, { target: resolvedHost, xfwd: true }, proxyException => {
|
||||
request = this.#PostProcessRequest(request, _bail);
|
||||
if (IsNullOrUndefined(request)) return;
|
||||
|
||||
this.ProxyService.web(request, response, { target: request.resolvedHost, xfwd: true }, proxyException => {
|
||||
return _bail(502); // Bad Gateway: Error related to proxying
|
||||
});
|
||||
} catch (proxyError) { return _bail(500); }
|
||||
}
|
||||
|
||||
#HandleWebSocketUpgrade(request, socket, head) {
|
||||
const { host } = request.headers;
|
||||
const resolvedHost = this.TryResolveInternalHost(host);
|
||||
const { hostname } = new URL(resolvedHost);
|
||||
const _bail = (internalStatus = 404) => {
|
||||
console.log(`[${internalStatus} as 404] ${request.method}=>WS ${req.headers?.host ?? ''}${req.url}`);
|
||||
socket.write(`HTTP/1.1 404 Not Found\r\nYou do not have the permissions to access '${host}', it's endpoint does not exist or the origin may be down\r\n`);
|
||||
console.log(`[${internalStatus} as 404] ${request.method}=>WS ${request.headers?.host ?? ''}${req.url}`);
|
||||
socket.write(`HTTP/1.1 404 Not Found\r\nYou do not have the permissions to access '${request.originalHost}', it's endpoint does not exist or the origin may be down\r\n`);
|
||||
socket.destroy();
|
||||
};
|
||||
if (IsNullOrUndefined(resolvedHost))
|
||||
return _bail(404);
|
||||
try {
|
||||
request.headers.host = hostname;
|
||||
this.ProxyService.ws(Request, socket, head, { target: resolvedHost, xfwd: true }, proxyException => {
|
||||
return _bail(502);
|
||||
request = this.#PostProcessRequest(request, _bail);
|
||||
if (IsNullOrUndefined(request)) return;
|
||||
|
||||
this.ProxyService.ws(request, socket, head, { target: request.resolvedHost, xfwd: true }, proxyException => {
|
||||
return _bail(502); // Bad Gateway: Error related to proxying
|
||||
});
|
||||
} catch (proxyError) { return _bail(500); }
|
||||
}
|
||||
|
||||
#PostProcessRequest(request, bailHandler = undefined) {
|
||||
const host = request.headers.host.trim();
|
||||
if (IsNullOrUndefined(host) || host.length == 0) return bailHandler(412); // Preconditions Failed: Missing 'Host' header
|
||||
const resolvedHost = this.TryResolveInternalHost(host);
|
||||
if (IsNullOrUndefined(resolvedHost)) return bailHandler(404); // Not found: No server to proxy/forward to was found
|
||||
let resolvedHostname;
|
||||
try {
|
||||
// In the future I could make a path differ, so that I can forward
|
||||
// https://domain.com[/my/path] => http://internalHostname/sub[/my/path]
|
||||
resolvedHostname = new URL(resolvedHost).hostname.trim();
|
||||
|
||||
// Appropriate to add here, since throwing will cause a 500,
|
||||
// since its the source of our data that caused the issue
|
||||
if (IsNullOrUndefined(resolvedHostname)
|
||||
|| resolvedHostname.length == 0
|
||||
) throw new TypeError(`Malformed internal hostname for '${host}'`);
|
||||
} catch { return bailHandler(500); } // Internal Server Error: Because data on server-side is messed up
|
||||
|
||||
// Lets do some forgery!! We want the internal server to know the hostname that it was accessed with,
|
||||
// port is irrelevent in this case, so don't include it
|
||||
request.originalHost = host;
|
||||
request.resolvedHost = resolvedHost;
|
||||
request.headers.host = resolvedHostname;
|
||||
// Attach the bailHandler to the request, so proxyRes can call it in case it needs to
|
||||
request.bailHandler = bailHandler;
|
||||
// Request is ready to proxy, send it out
|
||||
return request;
|
||||
}
|
||||
|
||||
#PostProcessResponse(proxyResponse, request, response) {
|
||||
// Check if there is an auth/access error
|
||||
let internalStatus = response.statusCode;
|
||||
if (response.statusCode == 511) {
|
||||
if (IsNullOrUndefined(request.bailHandler)) {
|
||||
response.writeHead(404, { "Content-Type": "text/plain" });
|
||||
response.end(`You do not have the permissions to access '${host}', it's endpoint does not exist or the origin may be down`);
|
||||
console.log(`[${internalStatus} as 404] ${request.method} ${hostname ?? request.headers?.host}${request.url}`);
|
||||
proxyResponse.destroy();
|
||||
return;
|
||||
}
|
||||
return request.bailHandler(internalStatus);
|
||||
}
|
||||
// Else let the response go through
|
||||
console.log(`[${proxyResponse.statusCode}] ${request.method} '${request.headers?.host ?? ''}${request.url}'`);
|
||||
}
|
||||
|
||||
TryResolveInternalHost(publicHost = "") {
|
||||
|
||||
Reference in New Issue
Block a user