( wsUrl: string, authHeader: string, wsAuthHeader: string, )
| 174 | } |
| 175 | |
| 176 | function startBunRelay( |
| 177 | wsUrl: string, |
| 178 | authHeader: string, |
| 179 | wsAuthHeader: string, |
| 180 | ): UpstreamProxyRelay { |
| 181 | // Bun TCP sockets don't auto-buffer partial writes: sock.write() returns |
| 182 | // the byte count actually handed to the kernel, and the remainder is |
| 183 | // silently dropped. When the kernel buffer fills, we queue the tail and |
| 184 | // let the drain handler flush it. Per-socket because the adapter closure |
| 185 | // outlives individual handler calls. |
| 186 | type BunState = ConnState & { writeBuf: Uint8Array[] } |
| 187 | |
| 188 | // eslint-disable-next-line custom-rules/require-bun-typeof-guard -- caller dispatches on typeof Bun |
| 189 | const server = Bun.listen<BunState>({ |
| 190 | hostname: '127.0.0.1', |
| 191 | port: 0, |
| 192 | socket: { |
| 193 | open(sock) { |
| 194 | sock.data = { ...newConnState(), writeBuf: [] } |
| 195 | }, |
| 196 | data(sock, data) { |
| 197 | const st = sock.data |
| 198 | const adapter: ClientSocket = { |
| 199 | write: payload => { |
| 200 | const bytes = |
| 201 | typeof payload === 'string' |
| 202 | ? Buffer.from(payload, 'utf8') |
| 203 | : payload |
| 204 | if (st.writeBuf.length > 0) { |
| 205 | st.writeBuf.push(bytes) |
| 206 | return |
| 207 | } |
| 208 | const n = sock.write(bytes) |
| 209 | if (n < bytes.length) st.writeBuf.push(bytes.subarray(n)) |
| 210 | }, |
| 211 | end: () => sock.end(), |
| 212 | } |
| 213 | handleData(adapter, st, data, wsUrl, authHeader, wsAuthHeader) |
| 214 | }, |
| 215 | drain(sock) { |
| 216 | const st = sock.data |
| 217 | while (st.writeBuf.length > 0) { |
| 218 | const chunk = st.writeBuf[0]! |
| 219 | const n = sock.write(chunk) |
| 220 | if (n < chunk.length) { |
| 221 | st.writeBuf[0] = chunk.subarray(n) |
| 222 | return |
| 223 | } |
| 224 | st.writeBuf.shift() |
| 225 | } |
| 226 | }, |
| 227 | close(sock) { |
| 228 | cleanupConn(sock.data) |
| 229 | }, |
| 230 | error(sock, err) { |
| 231 | logForDebugging(`[upstreamproxy] client socket error: ${err.message}`) |
| 232 | cleanupConn(sock.data) |
| 233 | }, |
no test coverage detected