@@ -81,6 +81,7 @@ import {
8181 Service ,
8282 Socket ,
8383 SocketIdentifier ,
84+ SocketPorts ,
8485 Worker_Binding ,
8586 Worker_Module ,
8687 kInspectorSocket ,
@@ -546,14 +547,15 @@ export function _initialiseInstanceRegistry() {
546547}
547548
548549export class Miniflare {
550+ #previousSharedOpts?: PluginSharedOptions ;
549551 #sharedOpts: PluginSharedOptions ;
550552 #workerOpts: PluginWorkerOptions [ ] ;
551553 #log: Log ;
552554
553555 readonly #runtime?: Runtime ;
554556 readonly #removeRuntimeExitHook?: ( ) => void ;
555557 #runtimeEntryURL?: URL ;
556- #socketPorts?: Map < SocketIdentifier , number > ;
558+ #socketPorts?: SocketPorts ;
557559 #runtimeClient?: Client ;
558560 #proxyClient?: ProxyClient ;
559561
@@ -877,6 +879,21 @@ export class Miniflare {
877879 } ) ;
878880 }
879881
882+ #getSocketAddress(
883+ id : SocketIdentifier ,
884+ previousRequestedPort : number | undefined ,
885+ host = DEFAULT_HOST ,
886+ requestedPort ?: number
887+ ) {
888+ // If `port` is set to `0`, was previously set to `0`, and we previously had
889+ // a port for this socket, reuse that random port
890+ if ( requestedPort === 0 && previousRequestedPort === 0 ) {
891+ requestedPort = this . #socketPorts?. get ( id ) ;
892+ }
893+ // Otherwise, default to a new random port
894+ return `${ host } :${ requestedPort ?? 0 } ` ;
895+ }
896+
880897 async #assembleConfig( loopbackPort : number ) : Promise < Config > {
881898 const allWorkerOpts = this . #workerOpts;
882899 const sharedOpts = this . #sharedOpts;
@@ -967,13 +984,24 @@ export class Miniflare {
967984
968985 // Allow additional sockets to be opened directly to specific workers,
969986 // bypassing Miniflare's entry worker.
970- let { unsafeDirectHost, unsafeDirectPort } = workerOpts . core ;
987+ const { unsafeDirectHost, unsafeDirectPort } = workerOpts . core ;
971988 if ( unsafeDirectHost !== undefined || unsafeDirectPort !== undefined ) {
972- unsafeDirectHost ??= DEFAULT_HOST ;
973- unsafeDirectPort ??= 0 ;
989+ const name = getDirectSocketName ( i ) ;
990+ const address = this . #getSocketAddress(
991+ name ,
992+ // We don't attempt to reuse allocated ports for `unsafeDirectPort: 0`
993+ // as there's not always a clear mapping between current/previous
994+ // worker options. We could do it by index, names, script, etc.
995+ // This is an unsafe option primarily intended for Wrangler's
996+ // inspector proxy, which will usually set this value to `9229`.
997+ // We could consider changing this in the future.
998+ /* previousRequestedPort */ undefined ,
999+ unsafeDirectHost ,
1000+ unsafeDirectPort
1001+ ) ;
9741002 sockets . push ( {
975- name : getDirectSocketName ( i ) ,
976- address : ` ${ unsafeDirectHost } : ${ unsafeDirectPort } ` ,
1003+ name,
1004+ address,
9771005 service : { name : getUserServiceName ( workerName ) } ,
9781006 http : { } ,
9791007 } ) ;
@@ -1059,13 +1087,27 @@ export class Miniflare {
10591087 const host = this . #sharedOpts. core . host ?? DEFAULT_HOST ;
10601088 const urlSafeHost = getURLSafeHost ( host ) ;
10611089 const accessibleHost = getAccessibleHost ( host ) ;
1090+ const entryAddress = this . #getSocketAddress(
1091+ SOCKET_ENTRY ,
1092+ this . #previousSharedOpts?. core . port ,
1093+ host ,
1094+ this . #sharedOpts. core . port
1095+ ) ;
1096+ let inspectorAddress : string | undefined ;
1097+ if ( this . #sharedOpts. core . inspectorPort !== undefined ) {
1098+ inspectorAddress = this . #getSocketAddress(
1099+ kInspectorSocket ,
1100+ this . #previousSharedOpts?. core . inspectorPort ,
1101+ "localhost" ,
1102+ this . #sharedOpts. core . inspectorPort
1103+ ) ;
1104+ }
10621105 const runtimeOpts : Abortable & RuntimeOptions = {
10631106 signal : this . #disposeController. signal ,
1064- entryHost : urlSafeHost ,
1065- entryPort : this . #sharedOpts. core . port ?? 0 ,
1107+ entryAddress,
10661108 loopbackPort,
10671109 requiredSockets,
1068- inspectorPort : this . #sharedOpts . core . inspectorPort ,
1110+ inspectorAddress ,
10691111 verbose : this . #sharedOpts. core . verbose ,
10701112 } ;
10711113 const maybeSocketPorts = await this . #runtime. updateConfig (
@@ -1223,6 +1265,7 @@ export class Miniflare {
12231265
12241266 // Split and validate options
12251267 const [ sharedOpts , workerOpts ] = validateOptions ( opts ) ;
1268+ this . #previousSharedOpts = this . #sharedOpts;
12261269 this . #sharedOpts = sharedOpts ;
12271270 this . #workerOpts = workerOpts ;
12281271 this . #log = this . #sharedOpts. core . log ?? this . #log;
0 commit comments