feat(connection): dispatch TypeRT remotes through shared API

This commit is contained in:
imccyu
2026-08-06 16:41:24 +08:00
parent 4eff751058
commit 36516e97b9
20 changed files with 439 additions and 182 deletions

View File

@@ -12,6 +12,7 @@ import { rejectWebSocketUpgrade, WebSocketDownlinks } from './websocket-downlink
export type {
ConnectionRpcAuthority,
ConnectionRpcEndpointMatcher,
ConnectionRpcHandler,
ConnectionRpcHandlerOptions,
HostConnectionHandle,
@@ -24,7 +25,7 @@ export { API_PATH, HOST_EVENTS_PATH, MUX_EVENTS_PATH } from './api-path.ts'
/** Stable Cordis plugin name. */
export const name = 'client-connection'
/** Services required before providing Connection; legacy `/api` attaches when apiProxy is present. */
/** Services required before providing Connection; API Proxy is an optional `/api` fallback. */
export const inject = ['httpServer']
/** Plugin config: the deployment's non-loopback serving authorities. */
@@ -93,35 +94,44 @@ export function apply(ctx: Context, config?: ConnectionConfig): void {
// Config boundary: a malformed entry fails the load loudly here rather than
// silently authorizing its hostname prefix at request time.
for (const entry of trustedHosts) assertTrustedAuthority(entry)
new HostConnectionService(ctx, trustedHosts)
const connection = new HostConnectionService(ctx, trustedHosts)
const fetchHandler = connection.createSharedFetchHandler(API_PATH, {
async fetch(request) {
const pathname = new URL(request.url).pathname
const method = pathname.startsWith(`${API_PATH}/`)
? pathname.slice(API_PATH.length + 1)
: undefined
if (method !== undefined
&& PRIVILEGED_METHODS.has(method)
&& !isTrustedApiRequest(request, [])) {
return new Response('forbidden', { status: 403 })
}
if (request.method === 'GET' && (pathname === MUX_EVENTS_PATH || pathname === HOST_EVENTS_PATH)) {
return new Response('upgrade required', {
status: 426,
headers: { connection: 'Upgrade', upgrade: 'websocket' },
})
}
const apiProxy = ctx.get('apiProxy')
if (apiProxy === undefined) return new Response('not found', { status: 404 })
return toFetchHandler(apiProxy).fetch(request)
},
})
const route: WebRoute = {
kind: 'prefix',
path: API_PATH,
handler: async (req, res) => {
if (!isTrustedApiRequest(req, trustedHosts)) {
res.writeHead(403)
res.end('forbidden')
return
}
await bridge(req, res, fetchHandler)
},
}
ctx.effect(() => ctx.httpServer.register(route), 'client-connection: /api route')
ctx.inject(['apiProxy'], (apiCtx) => {
const apiHandler = toFetchHandler(apiCtx.apiProxy)
const downlinks = new WebSocketDownlinks(apiCtx.apiProxy)
const route: WebRoute = {
kind: 'prefix',
path: API_PATH,
handler: async (req, res) => {
const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname
const method = pathname.startsWith(`${API_PATH}/`)
? pathname.slice(API_PATH.length + 1)
: undefined
const allowed = method !== undefined && PRIVILEGED_METHODS.has(method)
? isTrustedApiRequest(req, [])
: isTrustedApiRequest(req, trustedHosts)
if (!allowed) {
res.writeHead(403)
res.end('forbidden')
return
}
if (req.method === 'GET' && (pathname === MUX_EVENTS_PATH || pathname === HOST_EVENTS_PATH)) {
res.writeHead(426, { connection: 'Upgrade', upgrade: 'websocket' })
res.end('upgrade required')
return
}
await bridge(req, res, apiHandler)
},
}
apiCtx.effect(() => apiCtx.httpServer.register(route), 'client-connection: /api route')
const registerDownlink = (
path: string,
handle: WebUpgradeRoute['handler'],