Skip to content
38 changes: 38 additions & 0 deletions packages/vite/src/node/__tests__/http.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
})
})
26 changes: 22 additions & 4 deletions packages/vite/src/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading