diff --git a/packages/core/src/main/lens-proxy/lens-proxy.injectable.ts b/packages/core/src/main/lens-proxy/lens-proxy.injectable.ts deleted file mode 100644 index b182d24723..0000000000 --- a/packages/core/src/main/lens-proxy/lens-proxy.injectable.ts +++ /dev/null @@ -1,23 +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 { LensProxy } from "./lens-proxy"; -import loggerInjectable from "../../common/logger.injectable"; -import lensProxyHttpsServerInjectable from "./https-proxy/server.injectable"; -import proxyRetryInjectable from "./proxy/retry.injectable"; -import attemptToListenInjectable from "./attempt-to-listen.injectable"; - -const lensProxyInjectable = getInjectable({ - id: "lens-proxy", - - instantiate: (di) => new LensProxy({ - proxyServer: di.inject(lensProxyHttpsServerInjectable), - logger: di.inject(loggerInjectable), - proxyRetry: di.inject(proxyRetryInjectable), - attemptToListen: di.inject(attemptToListenInjectable), - }), -}); - -export default lensProxyInjectable; diff --git a/packages/core/src/main/lens-proxy/lens-proxy.ts b/packages/core/src/main/lens-proxy/lens-proxy.ts deleted file mode 100644 index 57435fc26f..0000000000 --- a/packages/core/src/main/lens-proxy/lens-proxy.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type https from "https"; -import type http from "http"; -import type { Cluster } from "../../common/cluster/cluster"; -import type { ProxyApiRequestArgs } from "./proxy-functions"; -import type { SetRequired } from "type-fest"; -import type { Logger } from "../../common/logger"; -import { disallowedPorts } from "./disallowed-ports"; -import type { ProxyRetry } from "./proxy/retry.injectable"; -import type { AttemptToListen } from "./attempt-to-listen.injectable"; - -export type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; -export type ServerIncomingMessage = SetRequired; -export type LensProxyApiRequest = (args: ProxyApiRequestArgs) => void | Promise; - -interface Dependencies { - attemptToListen: AttemptToListen; - readonly logger: Logger; - readonly proxyServer: https.Server; - readonly proxyRetry: ProxyRetry; -} - -export class LensProxy { - constructor(private readonly dependencies: Dependencies) {} - - /** - * Starts the lens proxy. - * @resolves After the server is listening on a good port - * @rejects if there is an error before that happens - */ - async listen(): Promise { - const seenPorts = new Set(); - - while(true) { - this.dependencies.proxyServer?.close(); - const port = await this.dependencies.attemptToListen(); - - if (!disallowedPorts.has(port)) { - // We didn't get a port that would result in an ERR_UNSAFE_PORT error, use it - return; - } - - this.dependencies.logger.warn(`[LENS-PROXY]: Proxy server has with port known to be considered unsafe to connect to by chrome, restarting...`); - - if (seenPorts.has(port)) { - /** - * Assume that if we have seen the port before, then the OS has looped - * through all the ports possible and we will not be able to get a safe - * port. - */ - throw new Error("Failed to start LensProxy due to seeing too many unsafe ports. Please restart Lens."); - } else { - seenPorts.add(port); - } - } - } - - close() { - this.dependencies.logger.info("[LENS-PROXY]: Closing server"); - - this.dependencies.proxyServer.close(); - this.dependencies.proxyRetry.close(); - } -} diff --git a/packages/core/src/main/lens-proxy/start-listening.injectable.ts b/packages/core/src/main/lens-proxy/start-listening.injectable.ts new file mode 100644 index 0000000000..e33d2afd80 --- /dev/null +++ b/packages/core/src/main/lens-proxy/start-listening.injectable.ts @@ -0,0 +1,54 @@ +/** + * 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 loggerInjectable from "../../common/logger.injectable"; +import attemptToListenInjectable from "./attempt-to-listen.injectable"; +import { disallowedPorts } from "./disallowed-ports"; +import lensProxyHttpsServerInjectable from "./https-proxy/server.injectable"; + +/** + * Starts the lens proxy. + * @resolves After the server is listening on a good port + * @rejects if there is an error before that happens + */ +export type StartLensProxyListening = () => Promise; + +const startLensProxyListeningInjectable = getInjectable({ + id: "start-lens-proxy-listening", + instantiate: (di): StartLensProxyListening => { + const attemptToListen = di.inject(attemptToListenInjectable); + const logger = di.inject(loggerInjectable); + const proxyServer = di.inject(lensProxyHttpsServerInjectable); + + return async () => { + const seenPorts = new Set(); + + while(true) { + proxyServer.close(); + const port = await attemptToListen(); + + if (!disallowedPorts.has(port)) { + // We didn't get a port that would result in an ERR_UNSAFE_PORT error, use it + return; + } + + logger.warn(`[LENS-PROXY]: Proxy server has with port known to be considered unsafe to connect to by chrome, restarting...`); + + if (seenPorts.has(port)) { + /** + * Assume that if we have seen the port before, then the OS has looped + * through all the ports possible and we will not be able to get a safe + * port. + */ + throw new Error("Failed to start LensProxy due to seeing too many unsafe ports. Please restart Lens."); + } else { + seenPorts.add(port); + } + } + }; + }, +}); + +export default startLensProxyListeningInjectable; diff --git a/packages/core/src/main/lens-proxy/stop-listening.injectable.ts b/packages/core/src/main/lens-proxy/stop-listening.injectable.ts new file mode 100644 index 0000000000..9f7da85dd8 --- /dev/null +++ b/packages/core/src/main/lens-proxy/stop-listening.injectable.ts @@ -0,0 +1,25 @@ +/** + * 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 loggerInjectable from "../../common/logger.injectable"; +import lensProxyHttpsServerInjectable from "./https-proxy/server.injectable"; +import proxyRetryInjectable from "./proxy/retry.injectable"; + +const stopLensProxyListeningInjectable = getInjectable({ + id: "stop-lens-proxy-listening", + instantiate: (di) => { + const logger = di.inject(loggerInjectable); + const proxyServer = di.inject(lensProxyHttpsServerInjectable); + const proxyRetry = di.inject(proxyRetryInjectable); + + return () => { + logger.info("[LENS-PROXY]: Closing server"); + proxyServer.close(); + proxyRetry.close(); + }; + }, +}); + +export default stopLensProxyListeningInjectable; diff --git a/packages/core/src/main/lens-proxy/stopping.injectable.ts b/packages/core/src/main/lens-proxy/stopping.injectable.ts new file mode 100644 index 0000000000..43220d3276 --- /dev/null +++ b/packages/core/src/main/lens-proxy/stopping.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/phases"; +import stopLensProxyListeningInjectable from "./stop-listening.injectable"; + +const stopLensProxyOnQuitInjectable = getInjectable({ + id: "stop-lens-proxy-on-quit", + instantiate: (di) => ({ + run: () => { + const stopLensProxyListening = di.inject(stopLensProxyListeningInjectable); + + stopLensProxyListening(); + + return undefined; + }, + }), + injectionToken: beforeQuitOfBackEndInjectionToken, +}); + +export default stopLensProxyOnQuitInjectable; diff --git a/packages/core/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts b/packages/core/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts index c77a0470be..0ea4e5db1a 100644 --- a/packages/core/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts +++ b/packages/core/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts @@ -4,7 +4,6 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import exitAppInjectable from "../../electron-app/features/exit-app.injectable"; -import lensProxyInjectable from "../../lens-proxy/lens-proxy.injectable"; import loggerInjectable from "../../../common/logger.injectable"; import lensProxyPortInjectable from "../../lens-proxy/lens-proxy-port.injectable"; import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; @@ -15,13 +14,14 @@ import initializeBuildVersionInjectable from "../../vars/build-version/init.inje import lensProxyCertificateInjectable from "../../../common/certificate/lens-proxy-certificate.injectable"; import fetchInjectable from "../../../common/fetch/fetch.injectable"; import { Agent } from "https"; +import startLensProxyListeningInjectable from "../../lens-proxy/start-listening.injectable"; const setupLensProxyInjectable = getInjectable({ id: "setup-lens-proxy", instantiate: (di) => ({ run: async () => { - const lensProxy = di.inject(lensProxyInjectable); + const startLensProxyListening = di.inject(startLensProxyListeningInjectable); const exitApp = di.inject(exitAppInjectable); const logger = di.inject(loggerInjectable); const lensProxyPort = di.inject(lensProxyPortInjectable); @@ -33,7 +33,7 @@ const setupLensProxyInjectable = getInjectable({ try { logger.info("🔌 Starting LensProxy"); - await lensProxy.listen(); // lensProxy.port available + await startLensProxyListening(); } catch (error: any) { showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);