Skip to content

Commit dfc8aa5

Browse files
Copilotsapphi-red
andauthored
fix: allow binding when strictPort is set but wildcard port is in use (#22150)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com>
1 parent 9163412 commit dfc8aa5

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

packages/vite/src/node/__tests__/http.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,42 @@ describe('port detection', () => {
213213
`Port ${BASE_PORT} is already in use`,
214214
)
215215
})
216+
217+
test('allows binding to specific host with strictPort when wildcard port is in use', async () => {
218+
await using _wildcardServer = await createSimpleServer(BASE_PORT, '0.0.0.0')
219+
220+
const warnMessages: string[] = []
221+
viteServer = await createServer({
222+
root: import.meta.dirname,
223+
customLogger: {
224+
info: () => {},
225+
warn: (msg) => warnMessages.push(msg),
226+
warnOnce: () => {},
227+
error: () => {},
228+
clearScreen: () => {},
229+
hasErrorLogged: () => false,
230+
hasWarned: false,
231+
},
232+
server: {
233+
port: BASE_PORT,
234+
host: '127.0.0.1',
235+
strictPort: true,
236+
ws: false,
237+
},
238+
})
239+
240+
try {
241+
await viteServer.listen()
242+
} catch (e) {
243+
// it may not be allowed to bind to specific host when wildcard port is in use
244+
expect(() => {
245+
throw e
246+
}).toThrow(`Port ${BASE_PORT} is already in use`)
247+
return
248+
}
249+
250+
const address = viteServer.httpServer!.address()
251+
expect(address).toStrictEqual(expect.objectContaining({ port: BASE_PORT }))
252+
expect(warnMessages).toContainEqual(expect.stringContaining('wildcard'))
253+
})
216254
})

packages/vite/src/node/http.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,20 +229,38 @@ export async function httpServerStart(
229229
for (let port = startPort; port <= MAX_PORT; port++) {
230230
// Pre-check port availability on wildcard addresses (0.0.0.0, ::)
231231
// so that we avoid conflicts with other servers listening on all interfaces
232-
if (await isPortAvailable(port)) {
232+
const portAvailableOnWildcard = await isPortAvailable(port)
233+
234+
// If port is not available on a wildcard address but strictPort is set,
235+
// we still try binding directly before giving up.
236+
if (strictPort) {
233237
const result = await tryBindServer(httpServer, port, host)
234238
if (result.success) {
239+
if (!portAvailableOnWildcard) {
240+
logger.warn(
241+
colors.yellow(
242+
`Port ${port} is in use on a wildcard address, but ${host ?? 'localhost'}:${port} is available. ` +
243+
`There may be another server running on a wildcard IP on port ${port}.`,
244+
),
245+
)
246+
}
235247
return port
236248
}
237249
if (result.error.code !== 'EADDRINUSE') {
238250
throw result.error
239251
}
240-
}
241-
242-
if (strictPort) {
243252
throw new Error(`Port ${port} is already in use`)
244253
}
245254

255+
if (portAvailableOnWildcard) {
256+
const result = await tryBindServer(httpServer, port, host)
257+
if (result.success) {
258+
return port
259+
}
260+
if (result.error.code !== 'EADDRINUSE') {
261+
throw result.error
262+
}
263+
}
246264
logger.info(`Port ${port} is in use, trying another one...`)
247265
}
248266
throw new Error(

0 commit comments

Comments
 (0)