Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion packages/vite/src/node/__tests__/http.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import http from 'node:http'
import { afterEach, describe, expect, test } from 'vitest'
import net from 'node:net'
import { afterEach, describe, expect, test, vi } from 'vitest'
import { wildcardHosts } from '../constants'
import { createServer } from '..'
import type { ViteDevServer } from '..'

Expand Down Expand Up @@ -157,6 +159,44 @@ describe('port detection', () => {
})
})

test('non-EADDRINUSE errors on wildcard do not block port selection', async () => {
const originalCreateServer = net.createServer.bind(net)
using _ = vi.spyOn(net, 'createServer').mockImplementation(() => {
const server = originalCreateServer()
const originalListen = server.listen.bind(server)
// @ts-expect-error this is the overload used internally
server.listen = (
port: number,
host: string,
...args: unknown[]
): net.Server => {
if (wildcardHosts.has(host)) {
process.nextTick(() => {
const err: NodeJS.ErrnoException = new Error(
'listen EACCES: permission denied',
)
err.code = 'EACCES'
server.emit('error', err)
})
return server
}
// @ts-expect-error this is the overload used internally
return originalListen(port, host, ...args)
}
return server
})

viteServer = await createServer({
root: import.meta.dirname,
logLevel: 'silent',
server: { port: BASE_PORT, strictPort: false, ws: false },
})
await viteServer.listen()

const address = viteServer.httpServer!.address()
expect(address).toStrictEqual(expect.objectContaining({ port: BASE_PORT }))
})

test('throws error when port is blocked and strictPort is true', async () => {
await using _blockingServer = await createSimpleServer(
BASE_PORT,
Expand Down
9 changes: 4 additions & 5 deletions packages/vite/src/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,8 @@ async function isPortAvailable(port: number): Promise<boolean> {
function tryListen(port: number, host: string): Promise<boolean> {
return new Promise((resolve) => {
const server = net.createServer()
server.once('error', () => {
// Ensure server is closed even on error to prevent resource leaks
server.close(() => resolve(false))
server.once('error', (e: NodeJS.ErrnoException) => {
server.close(() => resolve(e.code !== 'EADDRINUSE'))
})
server.once('listening', () => {
server.close(() => resolve(true))
Expand All @@ -193,10 +192,10 @@ async function tryBindServer(
port: number,
host: string | undefined,
): Promise<
{ success: true } | { success: false; error: Error & { code?: string } }
{ success: true } | { success: false; error: NodeJS.ErrnoException }
> {
return new Promise((resolve) => {
const onError = (e: Error & { code?: string }) => {
const onError = (e: NodeJS.ErrnoException) => {
httpServer.off('error', onError)
httpServer.off('listening', onListening)
resolve({ success: false, error: e })
Expand Down