| 78 | } |
| 79 | |
| 80 | createWebSocket(transportFactory: (url: URL) => Transport, guid?: string) { |
| 81 | assert(!this._wsGuid, 'can only create one main websocket transport per server'); |
| 82 | this._wsGuid = guid || createGuid(); |
| 83 | // HMR begin: route upgrades manually with `noServer` so Vite HMR's upgrade |
| 84 | // listener on the same http.Server is not pre-empted. With `{ server, path }` |
| 85 | // the ws library aborts non-matching upgrades with 400. |
| 86 | const wsPath = '/' + this._wsGuid; |
| 87 | const wss = new wsServer({ noServer: true }); |
| 88 | this._server.on('upgrade', (request, socket, head) => { |
| 89 | const pathname = new URL(request.url ?? '/', 'http://localhost').pathname; |
| 90 | if (pathname !== wsPath) |
| 91 | return; |
| 92 | wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request)); |
| 93 | }); |
| 94 | // HMR end |
| 95 | wss.on('connection', (ws, request) => { |
| 96 | const url = new URL(request.url ?? '/', 'http://localhost'); |
| 97 | const transport = transportFactory(url); |
| 98 | transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params })); |
| 99 | transport.close = () => ws.close(); |
| 100 | transport.onconnect(); |
| 101 | ws.on('message', async message => { |
| 102 | const { id, method, params } = JSON.parse(String(message)); |
| 103 | try { |
| 104 | const result = await transport.dispatch(method, params); |
| 105 | ws.send(JSON.stringify({ id, result })); |
| 106 | } catch (e) { |
| 107 | ws.send(JSON.stringify({ id, error: String(e) })); |
| 108 | } |
| 109 | }); |
| 110 | ws.on('close', () => transport.onclose()); |
| 111 | ws.on('error', () => transport.onclose()); |
| 112 | }); |
| 113 | } |
| 114 | |
| 115 | wsGuid(): string | undefined { |
| 116 | return this._wsGuid; |