Bun's fetch was following Go's 303 redirects internally, which caused ECONNRESET errors during the auto-login redirect chain. Let the browser handle redirects instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import type { ServerWebSocket } from 'bun'
|
|
|
|
export interface WsData {
|
|
path: string
|
|
protocols: string[]
|
|
}
|
|
|
|
const GO_PORT = 8000
|
|
const GO_BASE = `http://127.0.0.1:${GO_PORT}`
|
|
|
|
const upstreams = new Map<ServerWebSocket<WsData>, WebSocket>()
|
|
|
|
export function createProxy(isHealthy: () => boolean, isRunning: () => boolean) {
|
|
async function proxyFetch(req: Request): Promise<Response> {
|
|
const url = new URL(req.url)
|
|
|
|
if (url.pathname === '/ok') {
|
|
if (!isHealthy()) return new Response('starting', { status: isRunning() ? 200 : 503 })
|
|
return fetch(`${GO_BASE}/health`)
|
|
.then((r) => (r.ok ? new Response('ok') : new Response('unhealthy', { status: 503 })))
|
|
.catch(() => new Response('unhealthy', { status: 503 }))
|
|
}
|
|
|
|
const hasBody = req.method !== 'GET' && req.method !== 'HEAD'
|
|
const body = hasBody ? await req.arrayBuffer() : undefined
|
|
|
|
return fetch(`${GO_BASE}${url.pathname}${url.search}`, {
|
|
method: req.method,
|
|
headers: req.headers,
|
|
body,
|
|
redirect: 'manual',
|
|
}).then((r) => {
|
|
// Bun auto-decompresses gzip but leaves content-encoding header.
|
|
// Strip it so the next proxy layer doesn't try to decompress again.
|
|
const headers = new Headers(r.headers)
|
|
headers.delete('content-encoding')
|
|
headers.delete('content-length')
|
|
return new Response(r.body, { status: r.status, headers })
|
|
}).catch((e) => {
|
|
console.error('Proxy error:', e)
|
|
return new Response('Tronbyt server is not responding', { status: 502 })
|
|
})
|
|
}
|
|
|
|
const websocket = {
|
|
open(ws: ServerWebSocket<WsData>) {
|
|
const upstream = new WebSocket(`ws://127.0.0.1:${GO_PORT}${ws.data.path}`, ws.data.protocols)
|
|
upstream.binaryType = 'arraybuffer'
|
|
upstreams.set(ws, upstream)
|
|
|
|
upstream.addEventListener('message', (e) => ws.send(e.data as string | ArrayBuffer))
|
|
upstream.addEventListener('close', () => { upstreams.delete(ws); ws.close() })
|
|
upstream.addEventListener('error', () => { upstreams.delete(ws); ws.close() })
|
|
},
|
|
|
|
message(ws: ServerWebSocket<WsData>, msg: string | ArrayBuffer | Uint8Array) {
|
|
const upstream = upstreams.get(ws)
|
|
if (upstream?.readyState === WebSocket.OPEN) upstream.send(msg)
|
|
},
|
|
|
|
close(ws: ServerWebSocket<WsData>) {
|
|
upstreams.get(ws)?.close()
|
|
upstreams.delete(ws)
|
|
},
|
|
}
|
|
|
|
return { proxyFetch, websocket }
|
|
}
|