From 39bf1646e45153fd66a1aad7e36e10a6645b904e Mon Sep 17 00:00:00 2001 From: winixt Date: Wed, 6 May 2026 19:41:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=83=AD=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=97=AE=E9=A2=98=20#AI=20commit#?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builder-vite/src/commands/dev/index.ts | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/packages/builder-vite/src/commands/dev/index.ts b/packages/builder-vite/src/commands/dev/index.ts index ade6ae01..4b91476c 100644 --- a/packages/builder-vite/src/commands/dev/index.ts +++ b/packages/builder-vite/src/commands/dev/index.ts @@ -1,8 +1,10 @@ import type { IPluginAPI } from '@fesjs/shared'; +import type { Server as HttpsServer } from 'node:https'; import type { ViteDevServer } from 'vite'; +import os from 'node:os'; import process from 'node:process'; -import { createServer } from 'vite'; import pc from 'picocolors'; +import { createServer as createViteServer } from 'vite'; import getDevConfig from './getDevConfig'; interface Args { @@ -10,6 +12,52 @@ interface Args { rawArgv?: Record; options?: Record; program?: any; + https?: boolean; +} + +// Vite 8 uses http2.createSecureServer when HTTPS is enabled, which creates +// Http2ServerRequest/Http2ServerResponse objects that are incompatible with +// Connect middleware (parseurl, finalhandler, etc.). +// This function replaces the HTTP/2 server with a standard HTTPS server +// that produces compatible IncomingMessage/ServerResponse objects. +async function replaceHttp2WithHttps(server: ViteDevServer): Promise { + const httpServer = server.httpServer; + if (!httpServer || (httpServer as any).constructor.name !== 'Http2SecureServer') { + return undefined; + } + + // Extract the TLS credentials from the HTTP/2 server + const secureContext = (httpServer as any)._sharedCreds; + if (!secureContext?.context) { + return undefined; + } + + // Get the resolved HTTPS options from Vite config + const httpsOptions = (server.config as any).server?.https; + if (!httpsOptions) { + return undefined; + } + + // Migrate upgrade listeners (for WebSocket/HMR) from the old server + // before closing it, so they can be re-attached to the new server. + const upgradeListeners = (httpServer as any).listeners('upgrade').slice(); + + // Create a standard HTTPS server + const https = await import('node:https'); + const newServer = https.createServer(httpsOptions, server.middlewares); + + // Re-attach upgrade listeners to the new server + for (const listener of upgradeListeners) { + newServer.on('upgrade', listener); + } + + // Close the HTTP/2 server (it hasn't started listening yet) + httpServer.close(); + + // Replace the httpServer reference on the Vite server object + (server as any).httpServer = newServer; + + return newServer; } export default (api: IPluginAPI) => { @@ -19,8 +67,10 @@ export default (api: IPluginAPI) => { } = api; let server: ViteDevServer | undefined; + let customHttpsServer: HttpsServer | undefined; function destroy() { + customHttpsServer?.close(); if (server) { server.close().catch(() => {}); } @@ -49,8 +99,45 @@ export default (api: IPluginAPI) => { await api.startWatch(); - server = await createServer(await getDevConfig(api, args)); - await server.listen(); + server = await createViteServer(await getDevConfig(api, args)); + + // Replace HTTP/2 server with standard HTTPS if applicable + customHttpsServer = await replaceHttp2WithHttps(server); + + if (customHttpsServer) { + // For custom HTTPS server, we need to listen manually + // because Vite's internal listen() uses the old httpServer closure + const port = server.config.server.port; + const host = server.config.server.host; + await new Promise((resolve, reject) => { + customHttpsServer!.listen( + port, + host === true ? undefined : (host as string) || undefined, + () => resolve(), + ); + customHttpsServer!.on('error', reject); + }); + + // Set resolvedUrls for printUrls() + const resolvedPort = (customHttpsServer.address() as any).port; + const base = server.config.base === './' || server.config.base === '' ? '/' : server.config.base; + const networkInterfaces = os.networkInterfaces(); + const networkUrls: string[] = []; + for (const nInterface of Object.values(networkInterfaces)) { + for (const detail of nInterface ?? []) { + if (detail.family === 'IPv4' && detail.address && !detail.address.includes('127.0.0.1')) { + networkUrls.push(`https://${detail.address}:${resolvedPort}${base}`); + } + } + } + (server as any).resolvedUrls = { + local: [`https://localhost:${resolvedPort}${base}`], + network: networkUrls, + }; + } + else { + await server.listen(); + } server.printUrls();