Skip to content

Commit 3940156

Browse files
committed
refactor(devframe): trim the shared-server wss endpoint change
1 parent 8c10677 commit 3940156

2 files changed

Lines changed: 29 additions & 35 deletions

File tree

‎packages/devframe/src/node/instance-shell.ts‎

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type { DevframeRpcConnection, WsOriginRegistry, WsRpcTransport } from 'de
55
import type { H3, H3Event } from 'h3'
66
import type { Buffer } from 'node:buffer'
77
import type { IncomingMessage, Server as NodeHttpServer, ServerResponse } from 'node:http'
8-
import type { AddressInfo } from 'node:net'
98
import type { Duplex } from 'node:stream'
109
import type { ConnectionMeta, DevframeNodeContext, DevframeNodeRpcSession, DevframeNodeRpcSessionMeta, DevframeRpcClientFunctions, DevframeRpcServerFunctions } from '../types'
1110
import type { DevframeSseOptions, DevframeWsOptions } from '../types/devframe'
@@ -145,16 +144,23 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
145144

146145
const address = httpServer.address()
147146
const resolvedPort = typeof address === 'object' && address ? address.port : port
148-
const origin = httpServer instanceof TlsServer
149-
? `https://${formatHostForUrl(bindHost)}:${resolvedPort}`
150-
: normalizeHttpServerUrl(bindHost, resolvedPort)
147+
const origin = normalizeHttpServerUrl(bindHost, resolvedPort)
148+
// A shared server may not be listening yet (Vite listens after plugins configure).
151149
let wsUrl: string | undefined
152-
const cancelPublish = websocket
153-
? whenListening(httpServer, (boundPort) => {
154-
wsUrl = `${wsScheme(httpServer)}://${formatHostForUrl(bindHost)}:${boundPort}${options.path ?? ''}`
155-
getInternalContext(context).setWsEndpoint({ url: wsUrl })
156-
})
157-
: () => {}
150+
const publishWsEndpoint = (): void => {
151+
const bound = httpServer.address()
152+
if (!bound || typeof bound === 'string')
153+
return
154+
const scheme = httpServer instanceof TlsServer ? 'wss' : 'ws'
155+
wsUrl = `${scheme}://${formatHostForUrl(bindHost)}:${bound.port}${options.path ?? ''}`
156+
getInternalContext(context).setWsEndpoint({ url: wsUrl })
157+
}
158+
if (websocket) {
159+
if (httpServer.listening)
160+
publishWsEndpoint()
161+
else
162+
httpServer.once('listening', publishWsEndpoint)
163+
}
158164

159165
return {
160166
origin,
@@ -164,7 +170,7 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
164170
rpcGroup: core.rpcGroup,
165171
connectionMeta: () => websocketConnectionMeta(rpcHost, options.path),
166172
async close() {
167-
cancelPublish()
173+
httpServer.off('listening', publishWsEndpoint)
168174
await closeWs()
169175
if (ownsHttpServer)
170176
await new Promise<void>(r => httpServer.close(() => r()))
@@ -174,24 +180,6 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
174180
}
175181
}
176182

177-
function wsScheme(server: NodeHttpServer): 'ws' | 'wss' {
178-
return server instanceof TlsServer ? 'wss' : 'ws'
179-
}
180-
181-
function whenListening(server: NodeHttpServer, fn: (port: number, address: AddressInfo) => void): () => void {
182-
const run = (): void => {
183-
const address = server.address()
184-
if (typeof address === 'object' && address)
185-
fn(address.port, address)
186-
}
187-
if (server.listening) {
188-
run()
189-
return () => {}
190-
}
191-
server.once('listening', run)
192-
return () => server.off('listening', run)
193-
}
194-
195183
/**
196184
* Structural view of `Bun.serve`, typed loosely so devframe carries no
197185
* dependency on Bun's own types (`tsc` runs with Node lib only).
@@ -965,12 +953,19 @@ export function createInstanceShell<TContext extends DevframeNodeContext>(
965953
* {@link bindHttpAndWs} does the same for the tiers it owns.
966954
*/
967955
function publishWsEndpoint(server: NodeHttpServer): void {
968-
whenListening(server, (boundPort, address) => {
956+
const record = (): void => {
957+
const address = server.address()
958+
if (typeof address !== 'object' || !address)
959+
return
969960
const host = options.host ?? (address.address === '::' || address.address === '0.0.0.0' ? 'localhost' : address.address)
970961
getInternalContext(ctx).setWsEndpoint({
971-
url: `${wsScheme(server)}://${formatHostForUrl(host)}:${boundPort}${routePath}`,
962+
url: `ws://${formatHostForUrl(host)}:${address.port}${routePath}`,
972963
})
973-
})
964+
}
965+
if (server.listening)
966+
record()
967+
else
968+
server.once('listening', record)
974969
}
975970

976971
function handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void {

‎packages/vite/src/hub.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,8 @@ export function viteDevframeHub(options: ViteDevframeHubOptions = {}): Plugin {
180180
// the hub client runtime imports it into the host page.
181181
const devframes = attachClientScripts(options.devframes, options.clientScripts)
182182

183-
// Vite's https dev server is an `Http2SecureServer` (or an
184-
// `https.Server` with `server.proxy`). Both emit `upgrade` for HTTP/1.1
185-
// requests, as Vite's own HMR socket relies on, so share it too.
183+
// `server.https` makes Vite use an `Http2SecureServer`, not a `node:http`
184+
// `Server`. It still emits `upgrade` for HTTP/1.1, so the cast is safe.
186185
const httpServer = (server.httpServer ?? undefined) as NodeHttpServer | undefined
187186

188187
const hub = initHub({

0 commit comments

Comments
 (0)