mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Simplify handleLensRequest by factoring out sub functions
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
2d8ecbff43
commit
bd6f204828
14
src/common/utils/box.ts
Normal file
14
src/common/utils/box.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Box<T> {
|
||||||
|
constructor(private value: T) {}
|
||||||
|
|
||||||
|
get = () => this.value;
|
||||||
|
|
||||||
|
set = (newValue: T) => {
|
||||||
|
this.value = newValue;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -5,14 +5,19 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import type { IncomingMessage, ServerResponse } from "http";
|
import type { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import type { ErrorCallback, ProxyResCallback } from "http-proxy";
|
||||||
import HttpProxyServer from "http-proxy";
|
import HttpProxyServer from "http-proxy";
|
||||||
import { Socket } from "net";
|
import { Socket } from "net";
|
||||||
|
import type { Logger } from "../../common/logger";
|
||||||
import loggerInjectable from "../../common/logger.injectable";
|
import loggerInjectable from "../../common/logger.injectable";
|
||||||
|
import { Box } from "../../common/utils/box";
|
||||||
import { apiKubePrefix } from "../../common/vars";
|
import { apiKubePrefix } from "../../common/vars";
|
||||||
import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable";
|
import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable";
|
||||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||||
|
import type { RouteRequest } from "../router/route-request.injectable";
|
||||||
import routeRequestInjectable from "../router/route-request.injectable";
|
import routeRequestInjectable from "../router/route-request.injectable";
|
||||||
import { getBoolean } from "../utils/parse-query";
|
import { getBoolean } from "../utils/parse-query";
|
||||||
|
import type { GetClusterForRequest } from "./get-cluster-for-request.injectable";
|
||||||
import getClusterForRequestInjectable from "./get-cluster-for-request.injectable";
|
import getClusterForRequestInjectable from "./get-cluster-for-request.injectable";
|
||||||
|
|
||||||
const getRequestId = (req: IncomingMessage) => {
|
const getRequestId = (req: IncomingMessage) => {
|
||||||
@ -37,6 +42,111 @@ export interface HandleLensRequest {
|
|||||||
stopHandling: () => void;
|
stopHandling: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getProxyTarget = async (req: IncomingMessage, contextHandler: ClusterContextHandler) => {
|
||||||
|
if (req.url?.startsWith(apiKubePrefix)) {
|
||||||
|
delete req.headers.authorization;
|
||||||
|
req.url = req.url.replace(apiKubePrefix, "");
|
||||||
|
|
||||||
|
return contextHandler.getApiTarget(isLongRunningRequest(req.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onProxyResWith = (retryCounters: Map<string, number>): ProxyResCallback => (proxyRes, req, res) => {
|
||||||
|
retryCounters.delete(getRequestId(req));
|
||||||
|
|
||||||
|
proxyRes.on("aborted", () => { // happens when proxy target aborts connection
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface OnErrorWithDeps {
|
||||||
|
closed: Box<boolean>;
|
||||||
|
logger: Logger;
|
||||||
|
retryCounters: Map<string, number>;
|
||||||
|
handleRequest: HandleRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onErrorWith = (deps: OnErrorWithDeps): ErrorCallback => {
|
||||||
|
const {
|
||||||
|
closed,
|
||||||
|
logger,
|
||||||
|
retryCounters,
|
||||||
|
handleRequest,
|
||||||
|
} = deps;
|
||||||
|
|
||||||
|
return (error, req, res, target) => {
|
||||||
|
if (closed.get() || res instanceof Socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error(`[LENS-PROXY]: http proxy errored for cluster: ${error}`, { url: req.url });
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`);
|
||||||
|
|
||||||
|
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
|
||||||
|
const reqId = getRequestId(req);
|
||||||
|
const retryCount = retryCounters.get(reqId) || 0;
|
||||||
|
const timeoutMs = retryCount * 250;
|
||||||
|
|
||||||
|
if (retryCount < 20) {
|
||||||
|
logger.debug(`Retrying proxy request to url: ${reqId}`);
|
||||||
|
setTimeout(() => {
|
||||||
|
retryCounters.set(reqId, retryCount + 1);
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await handleRequest(req, res);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[LENS-PROXY]: failed to handle request on proxy error: ${error}`);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, timeoutMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.writeHead(500).end(`Oops, something went wrong.\n${error}`);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`[LENS-PROXY]: Failed to write headers: `, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface HandleRequestWithDeps {
|
||||||
|
getClusterForRequest: GetClusterForRequest;
|
||||||
|
proxy: HttpProxyServer;
|
||||||
|
contentSecurityPolicy: string;
|
||||||
|
routeRequest: RouteRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRequestWith = (deps: HandleRequestWithDeps): HandleRequest => {
|
||||||
|
const {
|
||||||
|
getClusterForRequest,
|
||||||
|
proxy,
|
||||||
|
contentSecurityPolicy,
|
||||||
|
routeRequest,
|
||||||
|
} = deps;
|
||||||
|
|
||||||
|
return async (req, res) => {
|
||||||
|
const cluster = getClusterForRequest(req);
|
||||||
|
|
||||||
|
if (cluster) {
|
||||||
|
const proxyTarget = await getProxyTarget(req, cluster.contextHandler);
|
||||||
|
|
||||||
|
if (proxyTarget) {
|
||||||
|
return proxy.web(req, res, proxyTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader("Content-Security-Policy", contentSecurityPolicy);
|
||||||
|
await routeRequest(cluster, req, res);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const handleLensRequestInjectable = getInjectable({
|
const handleLensRequestInjectable = getInjectable({
|
||||||
id: "handle-lens-request",
|
id: "handle-lens-request",
|
||||||
instantiate: (di): HandleLensRequest => {
|
instantiate: (di): HandleLensRequest => {
|
||||||
@ -46,84 +156,27 @@ const handleLensRequestInjectable = getInjectable({
|
|||||||
const routeRequest = di.inject(routeRequestInjectable);
|
const routeRequest = di.inject(routeRequestInjectable);
|
||||||
|
|
||||||
const retryCounters = new Map<string, number>();
|
const retryCounters = new Map<string, number>();
|
||||||
let closed = false;
|
const closed = new Box(false);
|
||||||
|
|
||||||
const getProxyTarget = async (req: IncomingMessage, contextHandler: ClusterContextHandler) => {
|
const proxy = HttpProxyServer.createProxy();
|
||||||
if (req.url?.startsWith(apiKubePrefix)) {
|
const handleRequest = handleRequestWith({
|
||||||
delete req.headers.authorization;
|
contentSecurityPolicy,
|
||||||
req.url = req.url.replace(apiKubePrefix, "");
|
getClusterForRequest,
|
||||||
|
proxy,
|
||||||
|
routeRequest,
|
||||||
|
});
|
||||||
|
|
||||||
return contextHandler.getApiTarget(isLongRunningRequest(req.url));
|
proxy.on("proxyRes", onProxyResWith(retryCounters));
|
||||||
}
|
proxy.on("error", onErrorWith({
|
||||||
|
closed,
|
||||||
return undefined;
|
handleRequest,
|
||||||
};
|
logger,
|
||||||
|
retryCounters,
|
||||||
const proxy = HttpProxyServer.createProxy()
|
}));
|
||||||
.on("proxyRes", (proxyRes, req, res) => {
|
|
||||||
retryCounters.delete(getRequestId(req));
|
|
||||||
|
|
||||||
proxyRes.on("aborted", () => { // happens when proxy target aborts connection
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on("error", (error, req, res, target) => {
|
|
||||||
if (closed || res instanceof Socket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.error(`[LENS-PROXY]: http proxy errored for cluster: ${error}`, { url: req.url });
|
|
||||||
|
|
||||||
if (target) {
|
|
||||||
logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`);
|
|
||||||
|
|
||||||
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
|
|
||||||
const reqId = getRequestId(req);
|
|
||||||
const retryCount = retryCounters.get(reqId) || 0;
|
|
||||||
const timeoutMs = retryCount * 250;
|
|
||||||
|
|
||||||
if (retryCount < 20) {
|
|
||||||
logger.debug(`Retrying proxy request to url: ${reqId}`);
|
|
||||||
setTimeout(() => {
|
|
||||||
retryCounters.set(reqId, retryCount + 1);
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await handleRequest(req, res);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`[LENS-PROXY]: failed to handle request on proxy error: ${error}`);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, timeoutMs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
res.writeHead(500).end(`Oops, something went wrong.\n${error}`);
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(`[LENS-PROXY]: Failed to write headers: `, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleRequest: HandleRequest = async (req, res) => {
|
|
||||||
const cluster = getClusterForRequest(req);
|
|
||||||
|
|
||||||
if (cluster) {
|
|
||||||
const proxyTarget = await getProxyTarget(req, cluster.contextHandler);
|
|
||||||
|
|
||||||
if (proxyTarget) {
|
|
||||||
return proxy.web(req, res, proxyTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.setHeader("Content-Security-Policy", contentSecurityPolicy);
|
|
||||||
await routeRequest(cluster, req, res);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handle: handleRequest,
|
handle: handleRequest,
|
||||||
stopHandling: () => closed = true,
|
stopHandling: () => closed.set(true),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user