diff --git a/packages/vite/src/node/__tests__/http.spec.ts b/packages/vite/src/node/__tests__/http.spec.ts index ec9d8d29245a7e..3c10fae328a081 100644 --- a/packages/vite/src/node/__tests__/http.spec.ts +++ b/packages/vite/src/node/__tests__/http.spec.ts @@ -213,4 +213,42 @@ describe('port detection', () => { `Port ${BASE_PORT} is already in use`, ) }) + + test('allows binding to specific host with strictPort when wildcard port is in use', async () => { + await using _wildcardServer = await createSimpleServer(BASE_PORT, '0.0.0.0') + + const warnMessages: string[] = [] + viteServer = await createServer({ + root: import.meta.dirname, + customLogger: { + info: () => {}, + warn: (msg) => warnMessages.push(msg), + warnOnce: () => {}, + error: () => {}, + clearScreen: () => {}, + hasErrorLogged: () => false, + hasWarned: false, + }, + server: { + port: BASE_PORT, + host: '127.0.0.1', + strictPort: true, + ws: false, + }, + }) + + try { + await viteServer.listen() + } catch (e) { + // it may not be allowed to bind to specific host when wildcard port is in use + expect(() => { + throw e + }).toThrow(`Port ${BASE_PORT} is already in use`) + return + } + + const address = viteServer.httpServer!.address() + expect(address).toStrictEqual(expect.objectContaining({ port: BASE_PORT })) + expect(warnMessages).toContainEqual(expect.stringContaining('wildcard')) + }) }) diff --git a/packages/vite/src/node/http.ts b/packages/vite/src/node/http.ts index 78ef9060ababcd..ecca383e3d6e7a 100644 --- a/packages/vite/src/node/http.ts +++ b/packages/vite/src/node/http.ts @@ -229,20 +229,38 @@ export async function httpServerStart( for (let port = startPort; port <= MAX_PORT; port++) { // Pre-check port availability on wildcard addresses (0.0.0.0, ::) // so that we avoid conflicts with other servers listening on all interfaces - if (await isPortAvailable(port)) { + const portAvailableOnWildcard = await isPortAvailable(port) + + // If port is not available on a wildcard address but strictPort is set, + // we still try binding directly before giving up. + if (strictPort) { const result = await tryBindServer(httpServer, port, host) if (result.success) { + if (!portAvailableOnWildcard) { + logger.warn( + colors.yellow( + `Port ${port} is in use on a wildcard address, but ${host ?? 'localhost'}:${port} is available. ` + + `There may be another server running on a wildcard IP on port ${port}.`, + ), + ) + } return port } if (result.error.code !== 'EADDRINUSE') { throw result.error } - } - - if (strictPort) { throw new Error(`Port ${port} is already in use`) } + if (portAvailableOnWildcard) { + const result = await tryBindServer(httpServer, port, host) + if (result.success) { + return port + } + if (result.error.code !== 'EADDRINUSE') { + throw result.error + } + } logger.info(`Port ${port} is in use, trying another one...`) } throw new Error(