diff --git a/src/main/__test__/lens-proxy.test.ts b/src/main/__test__/lens-proxy.test.ts index 2269d2dd65..0e895a03d0 100644 --- a/src/main/__test__/lens-proxy.test.ts +++ b/src/main/__test__/lens-proxy.test.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { isLongRunningRequest } from "../lens-proxy/handle-request-for.injectable"; +import { isLongRunningRequest } from "../lens-proxy/handle-lens-request.injectable"; describe("isLongRunningRequest", () => { it("returns true on watches", () => { diff --git a/src/main/lens-proxy/handle-lens-request.injectable.ts b/src/main/lens-proxy/handle-lens-request.injectable.ts index 8e0e6ca5ec..6798283999 100644 --- a/src/main/lens-proxy/handle-lens-request.injectable.ts +++ b/src/main/lens-proxy/handle-lens-request.injectable.ts @@ -4,12 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; -import type { IncomingMessage } from "http"; +import type { IncomingMessage, ServerResponse } from "http"; import HttpProxyServer from "http-proxy"; import { Socket } from "net"; import loggerInjectable from "../../common/logger.injectable"; -import type { HandleRequest } from "./handle-request-for.injectable"; -import handleRequestForInjectable from "./handle-request-for.injectable"; +import { apiKubePrefix } from "../../common/vars"; +import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable"; +import type { ClusterContextHandler } from "../context-handler/context-handler"; +import routeRequestInjectable from "../router/route-request.injectable"; +import { getBoolean } from "../utils/parse-query"; +import getClusterForRequestInjectable from "./get-cluster-for-request.injectable"; const getRequestId = (req: IncomingMessage) => { assert(req.headers.host); @@ -17,6 +21,17 @@ const getRequestId = (req: IncomingMessage) => { return req.headers.host + req.url; }; +const watchParam = "watch"; +const followParam = "follow"; + +export const isLongRunningRequest = (reqUrl: string) => { + const url = new URL(reqUrl, "http://localhost"); + + return getBoolean(url.searchParams, watchParam) || getBoolean(url.searchParams, followParam); +}; + +export type HandleRequest = (req: IncomingMessage, res: ServerResponse) => Promise; + export interface HandleLensRequest { handle: HandleRequest; stopHandling: () => void; @@ -26,11 +41,24 @@ const handleLensRequestInjectable = getInjectable({ id: "handle-lens-request", instantiate: (di): HandleLensRequest => { const logger = di.inject(loggerInjectable); - const handleRequestFor = di.inject(handleRequestForInjectable); + const getClusterForRequest = di.inject(getClusterForRequestInjectable); + const contentSecurityPolicy = di.inject(contentSecurityPolicyInjectable); + const routeRequest = di.inject(routeRequestInjectable); const retryCounters = new Map(); let closed = false; + 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 proxy = HttpProxyServer.createProxy() .on("proxyRes", (proxyRes, req, res) => { retryCounters.delete(getRequestId(req)); @@ -78,7 +106,20 @@ const handleLensRequestInjectable = getInjectable({ } }); - const handleRequest = handleRequestFor(proxy); + 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 { handle: handleRequest, diff --git a/src/main/lens-proxy/handle-request-for.injectable.ts b/src/main/lens-proxy/handle-request-for.injectable.ts deleted file mode 100644 index 7382460141..0000000000 --- a/src/main/lens-proxy/handle-request-for.injectable.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { ServerResponse, IncomingMessage } from "http"; -import { apiKubePrefix } from "../../common/vars"; -import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable"; -import type { ClusterContextHandler } from "../context-handler/context-handler"; -import routeRequestInjectable from "../router/route-request.injectable"; -import { getBoolean } from "../utils/parse-query"; -import getClusterForRequestInjectable from "./get-cluster-for-request.injectable"; -import type HttpProxyServer from "http-proxy"; - -const watchParam = "watch"; -const followParam = "follow"; - -export const isLongRunningRequest = (reqUrl: string) => { - const url = new URL(reqUrl, "http://localhost"); - - return getBoolean(url.searchParams, watchParam) || getBoolean(url.searchParams, followParam); -}; - -export type HandleRequest = (req: IncomingMessage, res: ServerResponse) => Promise; -export type HandleRequestFor = (proxy: HttpProxyServer) => HandleRequest; - -const handleRequestForInjectable = getInjectable({ - id: "handle-request-for", - instantiate: (di): HandleRequestFor => { - const getClusterForRequest = di.inject(getClusterForRequestInjectable); - const contentSecurityPolicy = di.inject(contentSecurityPolicyInjectable); - const routeRequest = di.inject(routeRequestInjectable); - - 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; - }; - - return (proxy) => 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); - }; - }, -}); - -export default handleRequestForInjectable; diff --git a/src/main/router/route-request.injectable.ts b/src/main/router/route-request.injectable.ts index d99417c0ac..d7b71ed6cb 100644 --- a/src/main/router/route-request.injectable.ts +++ b/src/main/router/route-request.injectable.ts @@ -12,6 +12,7 @@ import type { Cluster } from "../../common/cluster/cluster"; import type { RouteHandler } from "./create-handler-for-route.injectable"; import type { IncomingMessage, ServerResponse } from "http"; import loggerInjectable from "../../common/logger.injectable"; +import { writeServerResponseFor } from "./write-server-response"; export const routeInjectionToken = getInjectionToken>({ id: "route-injection-token", @@ -51,6 +52,11 @@ const routeRequestInjectable = getInjectable({ if (matchingRoute instanceof Error) { logger.warn(`[ROUTE-REQUEST]: ${matchingRoute}`, { url: url.pathname }); + writeServerResponseFor(res)({ + statusCode: 404, + content: "Not found", + }); + return; } @@ -71,8 +77,6 @@ const routeRequestInjectable = getInjectable({ }; await matchingRoute.route(request, res); - - return; }; }, }); diff --git a/src/main/router/write-server-response.ts b/src/main/router/write-server-response.ts index 648b22fad0..8121a825b3 100644 --- a/src/main/router/write-server-response.ts +++ b/src/main/router/write-server-response.ts @@ -9,7 +9,7 @@ import { object } from "../../common/utils"; export interface LensServerResponse { statusCode: number; content: unknown; - headers: Partial>; + headers?: Partial>; } export const writeServerResponseFor = (serverResponse: ServerResponse) => ({