From 9d681110568a6f2cb9b01e4f64ba2fd5ea89f185 Mon Sep 17 00:00:00 2001 From: cjk7989 <929970065@qq.com> Date: Fri, 7 Jul 2023 11:09:29 +0800 Subject: [PATCH] fix: support dev server on node 18 --- src/core/utils/net.ts | 4 +- src/msha/middlewares/request.middleware.ts | 48 +++++++++++++++++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/core/utils/net.ts b/src/core/utils/net.ts index 8dfe694d..1af48591 100644 --- a/src/core/utils/net.ts +++ b/src/core/utils/net.ts @@ -103,7 +103,7 @@ export async function validateDevServerConfig(url: string | undefined, timeout: const resolvedPortNumber = await isAcceptingTcpConnections({ port, host: hostname }); if (resolvedPortNumber !== 0) { const spinner = ora(); - const waitOnOneOfResources = hostname === "localhost" ? [`tcp:127.0.0.1:${port}`, `tcp:[::1]:${port}`] : [`tcp:${hostname}:${port}`]; + let waitOnOneOfResources = hostname === "localhost" ? [`tcp:127.0.0.1:${port}`, `tcp:localhost:${port}`] : [`tcp:${hostname}:${port}`]; spinner.start(`Waiting for ${chalk.green(url)} to be ready`); let promises = waitOnOneOfResources.map((resource) => { @@ -130,7 +130,7 @@ export async function validateDevServerConfig(url: string | undefined, timeout: try { await Promise.any(promises); - spinner.succeed(`Connected to ${url} successfully`); + spinner.succeed(`${url} validated successfully`); spinner.clear(); } catch { spinner.fail(); diff --git a/src/msha/middlewares/request.middleware.ts b/src/msha/middlewares/request.middleware.ts index 8fa960d4..cf4b6bd4 100644 --- a/src/msha/middlewares/request.middleware.ts +++ b/src/msha/middlewares/request.middleware.ts @@ -9,6 +9,8 @@ import serveStatic from "serve-static"; import { DEFAULT_CONFIG } from "../../config"; import { findSWAConfigFile, logger, logRequest } from "../../core"; import { AUTH_STATUS, CUSTOM_URL_SCHEME, IS_APP_DEV_SERVER, SWA_PUBLIC_DIR } from "../../core/constants"; +import { parseUrl } from "../../core/utils/net"; +import waitOn from "wait-on"; import { getAuthBlockResponse, handleAuthRequest, isAuthRequest, isLoginRequest, isLogoutRequest } from "../handlers/auth.handler"; import { isDataApiRequest } from "../handlers/dab.handler"; import { handleErrorPage } from "../handlers/error-page.handler"; @@ -67,7 +69,7 @@ export async function handleUserConfig(appLocation: string | undefined): Promise * @param proxyApp An `http-proxy` instance. * @param target The root folder of the static app (ie. `output_location`). Or, the HTTP host target, if connecting to a dev server, or */ -function serveStaticOrProxyResponse(req: http.IncomingMessage, res: http.ServerResponse, proxyApp: httpProxy, target: string | undefined) { +async function serveStaticOrProxyResponse(req: http.IncomingMessage, res: http.ServerResponse, proxyApp: httpProxy, target: string | undefined) { if ([301, 302].includes(res.statusCode)) { res.end(); return; @@ -100,6 +102,42 @@ function serveStaticOrProxyResponse(req: http.IncomingMessage, res: http.ServerR target = DEFAULT_CONFIG.outputLocation; logRequest(req, target); + let { protocol, hostname, port } = parseUrl(target); + + if (hostname === "localhost") { + let waitOnOneOfResources = [`tcp:127.0.0.1:${port}`, `tcp:localhost:${port}`]; + let promises = waitOnOneOfResources.map((resource) => { + return waitOn({ + resources: [resource], + delay: 1000, // initial delay in ms, default 0 + interval: 100, // poll interval in ms, default 250ms + simultaneous: 1, // limit to 1 connection per resource at a time + timeout: 60000, // timeout in ms, default Infinity + tcpTimeout: 1000, // tcp timeout in ms, default 300ms + window: 1000, // stabilization time in ms, default 750ms + strictSSL: false, + verbose: false, // force disable verbose logs even if SWA_CLI_DEBUG is enabled + }) + .then(() => { + logger.silly(`Connected to ${resource} successfully`); + return resource; + }) + .catch((err) => { + logger.silly(`Could not connect to ${resource}`); + logger.warn(err.message); + throw err; + }); + }); + + try { + const availableUrl = await Promise.any(promises); + logger.silly(`${target} validated successfully`); + target = protocol + "//" + availableUrl.slice(4); + } catch { + logger.error(`Could not connect to "${target}". Is the server up and running?`); + } + } + proxyApp.web( req, res, @@ -209,7 +247,7 @@ export async function requestMiddleware( if (isWebsocketRequest(req)) { logger.silly(`websocket request detected`); - return serveStaticOrProxyResponse(req, res, proxyApp, DEFAULT_CONFIG.outputLocation); + return await serveStaticOrProxyResponse(req, res, proxyApp, DEFAULT_CONFIG.outputLocation); } let target = DEFAULT_CONFIG.outputLocation; @@ -224,7 +262,7 @@ export async function requestMiddleware( logger.silly(` - ${statusCodeToServe} code detected. Exit`); handleErrorPage(req, res, statusCodeToServe, userConfig?.responseOverrides); - return serveStaticOrProxyResponse(req, res, proxyApp, target); + return await serveStaticOrProxyResponse(req, res, proxyApp, target); } } @@ -280,7 +318,7 @@ export async function requestMiddleware( if (!isRouteRequiringUserRolesCheck(req, matchingRouteRule, isFunctionReq, authStatus)) { handleErrorPage(req, res, 401, userConfig?.responseOverrides); - return serveStaticOrProxyResponse(req, res, proxyApp, target); + return await serveStaticOrProxyResponse(req, res, proxyApp, target); } if (authStatus != AUTH_STATUS.NoAuth && (authStatus != AUTH_STATUS.HostNameAuthLogin || !urlPathnameWithQueryParams)) { @@ -295,6 +333,6 @@ export async function requestMiddleware( logger.silly(` - url: ${chalk.yellow(req.url)}`); logger.silly(` - target: ${chalk.yellow(target)}`); - serveStaticOrProxyResponse(req, res, proxyApp, target); + await serveStaticOrProxyResponse(req, res, proxyApp, target); } }