@@ -2,16 +2,13 @@ import type { NuxtOptions } from '@nuxt/schema'
2
2
import type { ParsedArgs } from 'citty'
3
3
import type { HTTPSOptions , ListenOptions } from 'listhen'
4
4
import type { ChildProcess } from 'node:child_process'
5
- import type { IncomingMessage , ServerResponse } from 'node:http'
6
- import type { TLSSocket } from 'node:tls'
7
5
import type { NuxtDevContext , NuxtDevIPCMessage } from '../dev/utils'
8
6
9
7
import { fork } from 'node:child_process'
10
8
import process from 'node:process'
11
9
12
10
import { defineCommand } from 'citty'
13
11
import { isSocketSupported } from 'get-port-please'
14
- import { createProxyServer } from 'httpxy'
15
12
import { listen } from 'listhen'
16
13
import { getArgs as getListhenArgs , parseArgs as parseListhenArgs } from 'listhen/cli'
17
14
import { resolve } from 'pathe'
@@ -20,8 +17,10 @@ import { isBun, isDeno, isTest } from 'std-env'
20
17
21
18
import { initialize } from '../dev'
22
19
import { renderError } from '../dev/error'
20
+ import { createFetchHandler } from '../dev/fetch'
23
21
import { isSocketURL , parseSocketURL } from '../dev/socket'
24
22
import { resolveLoadingTemplate } from '../dev/utils'
23
+ import { connectToChildNetwork , connectToChildSocket } from '../dev/websocket'
25
24
import { showVersions } from '../utils/banner'
26
25
import { overrideEnv } from '../utils/env'
27
26
import { loadKit } from '../utils/kit'
@@ -131,14 +130,14 @@ const command = defineCommand({
131
130
}
132
131
}
133
132
134
- // Start proxy Listener
135
- const devProxy = await createDevProxy ( cwd , nuxtOptions , listenOptions )
133
+ // Start listener
134
+ const devHandler = await createDevHandler ( cwd , nuxtOptions , listenOptions )
136
135
137
136
const nuxtSocketEnv = process . env . NUXT_SOCKET ? process . env . NUXT_SOCKET === '1' : undefined
138
137
139
138
const useSocket = nuxtSocketEnv ?? ( nuxtOptions . _majorVersion === 4 && await isSocketSupported ( ) )
140
139
141
- const urls = await devProxy . listener . getURLs ( )
140
+ const urls = await devHandler . listener . getURLs ( )
142
141
// run initially in in no-fork mode
143
142
const { onRestart, onReady, close } = await initialize ( {
144
143
cwd,
@@ -147,16 +146,16 @@ const command = defineCommand({
147
146
public : listenOptions . public ,
148
147
publicURLs : urls . map ( r => r . url ) ,
149
148
proxy : {
150
- url : devProxy . listener . url ,
149
+ url : devHandler . listener . url ,
151
150
urls,
152
- https : devProxy . listener . https ,
153
- addr : devProxy . listener . address ,
151
+ https : devHandler . listener . https ,
152
+ addr : devHandler . listener . address ,
154
153
} ,
155
154
// if running with nuxt v4 or `NUXT_SOCKET=1`, we use the socket listener
156
155
// otherwise pass 'true' to listen on a random port instead
157
156
} , { } , useSocket ? undefined : true )
158
157
159
- onReady ( address => devProxy . setAddress ( address ) )
158
+ onReady ( address => devHandler . setAddress ( address ) )
160
159
161
160
// ... then fall back to pre-warmed fork if a hard restart is required
162
161
const fork = startSubprocess ( cwd , ctx . args , ctx . rawArgs , listenOptions )
@@ -165,16 +164,16 @@ const command = defineCommand({
165
164
fork ,
166
165
devServer . close ( ) . catch ( ( ) => { } ) ,
167
166
] )
168
- await subprocess . initialize ( devProxy , useSocket )
167
+ await subprocess . initialize ( devHandler , useSocket )
169
168
} )
170
169
171
170
return {
172
- listener : devProxy . listener ,
171
+ listener : devHandler . listener ,
173
172
async close ( ) {
174
173
await close ( )
175
174
const subprocess = await fork
176
175
subprocess . kill ( 0 )
177
- await devProxy . listener . close ( )
176
+ await devHandler . listener . close ( )
178
177
} ,
179
178
}
180
179
} ,
@@ -189,42 +188,46 @@ type ArgsT = Exclude<
189
188
undefined | ( ( ...args : unknown [ ] ) => unknown )
190
189
>
191
190
192
- type DevProxy = Awaited < ReturnType < typeof createDevProxy > >
191
+ type DevHandler = Awaited < ReturnType < typeof createDevHandler > >
193
192
194
- async function createDevProxy ( cwd : string , nuxtOptions : NuxtOptions , listenOptions : Partial < ListenOptions > ) {
193
+ async function createDevHandler ( cwd : string , nuxtOptions : NuxtOptions , listenOptions : Partial < ListenOptions > ) {
195
194
let loadingMessage = 'Nuxt dev server is starting...'
196
195
let error : Error | undefined
197
196
let address : string | undefined
198
197
199
198
let loadingTemplate = nuxtOptions . devServer . loadingTemplate
200
199
201
- const proxy = createProxyServer ( { } )
202
-
203
- proxy . on ( 'proxyReq' , ( proxyReq , req ) => {
204
- if ( ! proxyReq . hasHeader ( 'x-forwarded-for' ) ) {
205
- const address = req . socket . remoteAddress
206
- if ( address ) {
207
- proxyReq . appendHeader ( 'x-forwarded-for' , address )
200
+ // Create fetch-based handler
201
+ const fetchHandler = createFetchHandler (
202
+ ( ) => {
203
+ if ( ! address ) {
204
+ return undefined
208
205
}
209
- }
210
- if ( ! proxyReq . hasHeader ( 'x-forwarded-port' ) ) {
211
- const localPort = req ?. socket ?. localPort
212
- if ( localPort ) {
213
- proxyReq . setHeader ( 'x-forwarded-port' , req . socket . localPort )
206
+
207
+ // Convert address string to DevAddress format
208
+ if ( isSocketURL ( address ) ) {
209
+ const { socketPath } = parseSocketURL ( address )
210
+ return { socketPath }
214
211
}
215
- }
216
- if ( ! proxyReq . hasHeader ( 'x-forwarded-Proto' ) ) {
217
- const encrypted = ( req ?. connection as TLSSocket ) ?. encrypted
218
- proxyReq . setHeader ( 'x-forwarded-proto' , encrypted ? 'https' : 'http' )
219
- }
220
- } )
221
212
222
- const listener = await listen ( ( req : IncomingMessage , res : ServerResponse ) => {
223
- if ( error ) {
213
+ // Parse network address
214
+ try {
215
+ const url = new URL ( address )
216
+ return {
217
+ host : url . hostname ,
218
+ port : Number . parseInt ( url . port ) || 80 ,
219
+ }
220
+ }
221
+ catch {
222
+ return undefined
223
+ }
224
+ } ,
225
+ // Error handler
226
+ async ( req , res ) => {
224
227
renderError ( req , res , error )
225
- return
226
- }
227
- if ( ! address ) {
228
+ } ,
229
+ // Loading handler
230
+ async ( req , res ) => {
228
231
res . statusCode = 503
229
232
res . setHeader ( 'Content-Type' , 'text/html' )
230
233
res . setHeader ( 'Cache-Control' , 'no-store' )
@@ -239,10 +242,10 @@ async function createDevProxy(cwd: string, nuxtOptions: NuxtOptions, listenOptio
239
242
res . end ( loadingTemplate ( { loading : loadingMessage } ) )
240
243
}
241
244
return resolveLoadingMessage ( )
242
- }
243
- const target = isSocketURL ( address ) ? parseSocketURL ( address ) : address
244
- proxy . web ( req , res , { target } )
245
- } , listenOptions )
245
+ } ,
246
+ )
247
+
248
+ const listener = await listen ( fetchHandler , listenOptions )
246
249
247
250
listener . server . on ( 'upgrade' , ( req , socket , head ) => {
248
251
if ( ! address ) {
@@ -251,13 +254,23 @@ async function createDevProxy(cwd: string, nuxtOptions: NuxtOptions, listenOptio
251
254
}
252
255
return
253
256
}
254
- const target = isSocketURL ( address ) ? parseSocketURL ( address ) : address
255
- // @ts -expect-error TODO: fix socket type in httpxy
256
- return proxy . ws ( req , socket , { target, xfwd : true } , head ) . catch ( ( ) => {
257
- if ( ! socket . destroyed ) {
258
- socket . end ( )
257
+ if ( isSocketURL ( address ) ) {
258
+ const { socketPath } = parseSocketURL ( address )
259
+ connectToChildSocket ( socketPath , req , socket , head )
260
+ }
261
+ else {
262
+ try {
263
+ const url = new URL ( address )
264
+ const host = url . hostname
265
+ const port = Number . parseInt ( url . port ) || 80
266
+ connectToChildNetwork ( host , port , req , socket , head )
259
267
}
260
- } )
268
+ catch {
269
+ if ( ! socket . destroyed ) {
270
+ socket . end ( )
271
+ }
272
+ }
273
+ }
261
274
} )
262
275
263
276
return {
@@ -279,7 +292,7 @@ async function createDevProxy(cwd: string, nuxtOptions: NuxtOptions, listenOptio
279
292
280
293
async function startSubprocess ( cwd : string , args : { logLevel : string , clear : boolean , dotenv : string , envName : string , extends ?: string } , rawArgs : string [ ] , listenOptions : Partial < ListenOptions > ) {
281
294
let childProc : ChildProcess | undefined
282
- let devProxy : DevProxy
295
+ let devHandler : DevHandler
283
296
let ready : Promise < void > | undefined
284
297
const kill = ( signal : NodeJS . Signals | number ) => {
285
298
if ( childProc ) {
@@ -288,9 +301,9 @@ async function startSubprocess(cwd: string, args: { logLevel: string, clear: boo
288
301
}
289
302
}
290
303
291
- async function initialize ( proxy : DevProxy , socket : boolean ) {
292
- devProxy = proxy
293
- const urls = await devProxy . listener . getURLs ( )
304
+ async function initialize ( handler : DevHandler , socket : boolean ) {
305
+ devHandler = handler
306
+ const urls = await devHandler . listener . getURLs ( )
294
307
await ready
295
308
childProc ! . send ( {
296
309
type : 'nuxt:internal:dev:context' ,
@@ -302,16 +315,16 @@ async function startSubprocess(cwd: string, args: { logLevel: string, clear: boo
302
315
public : listenOptions . public ,
303
316
publicURLs : urls . map ( r => r . url ) ,
304
317
proxy : {
305
- url : devProxy . listener . url ,
318
+ url : devHandler . listener . url ,
306
319
urls,
307
- https : devProxy . listener . https ,
320
+ https : devHandler . listener . https ,
308
321
} ,
309
322
} satisfies NuxtDevContext ,
310
323
} )
311
324
}
312
325
313
326
async function restart ( ) {
314
- devProxy ?. clearError ( )
327
+ devHandler ?. clearError ( )
315
328
// Kill previous process with restart signal (not supported on Windows)
316
329
if ( process . platform === 'win32' ) {
317
330
kill ( 'SIGTERM' )
@@ -343,19 +356,19 @@ async function startSubprocess(cwd: string, args: { logLevel: string, clear: boo
343
356
resolve ( )
344
357
}
345
358
else if ( message . type === 'nuxt:internal:dev:ready' ) {
346
- devProxy . setAddress ( message . address )
359
+ devHandler . setAddress ( message . address )
347
360
if ( startTime ) {
348
361
logger . debug ( `Dev server ready for connections in ${ Date . now ( ) - startTime } ms` )
349
362
}
350
363
}
351
364
else if ( message . type === 'nuxt:internal:dev:loading' ) {
352
- devProxy . setAddress ( undefined )
353
- devProxy . setLoadingMessage ( message . message )
354
- devProxy . clearError ( )
365
+ devHandler . setAddress ( undefined )
366
+ devHandler . setLoadingMessage ( message . message )
367
+ devHandler . clearError ( )
355
368
}
356
369
else if ( message . type === 'nuxt:internal:dev:loading:error' ) {
357
- devProxy . setAddress ( undefined )
358
- devProxy . setError ( message . error )
370
+ devHandler . setAddress ( undefined )
371
+ devHandler . setError ( message . error )
359
372
}
360
373
else if ( message . type === 'nuxt:internal:dev:restart' ) {
361
374
restart ( )
0 commit comments