| 618 | } |
| 619 | |
| 620 | private async handleWS(ctx: KoaContext, HandlerClass, checker, conn?, layer?, savedContext?: CordisContext) { |
| 621 | const { args } = ctx.HydroContext; |
| 622 | const sub = await forkContextWithScope(savedContext); |
| 623 | const h = new HandlerClass(ctx, sub.ctx); |
| 624 | let stream: PassThrough; |
| 625 | if (!conn) { |
| 626 | // By HTTP |
| 627 | stream = new PassThrough(); |
| 628 | ctx.request.socket.setTimeout(0); |
| 629 | ctx.req.socket.setNoDelay(true); |
| 630 | ctx.req.socket.setKeepAlive(true); |
| 631 | ctx.set({ |
| 632 | 'X-Accel-Buffering': 'no', |
| 633 | 'Content-Type': 'text/event-stream', |
| 634 | 'Cache-Control': 'no-cache', |
| 635 | Connection: 'keep-alive', |
| 636 | }); |
| 637 | ctx.HydroContext.request.websocket = true; |
| 638 | ctx.compress = false; |
| 639 | conn = { |
| 640 | close() { |
| 641 | stream.end(); |
| 642 | }, |
| 643 | send(data: any) { |
| 644 | stream.write(`${args.sse ? 'data: ' : ''}${data}\n${args.sse ? '\n' : ''}`); |
| 645 | }, |
| 646 | }; |
| 647 | } |
| 648 | ctx.handler = h; |
| 649 | h.conn = conn; |
| 650 | let closed = false; |
| 651 | this.activeHandlers.set(h, { start: Date.now(), name: HandlerClass.name }); |
| 652 | |
| 653 | const clean = async (err?: Error) => { |
| 654 | if (closed) return; |
| 655 | closed = true; |
| 656 | try { |
| 657 | try { |
| 658 | if (err) await h.onerror(err); |
| 659 | // FIXME: should pass type check |
| 660 | else (this.ctx.emit as any)('connection/close', h); |
| 661 | } finally { |
| 662 | h.active = false; |
| 663 | if (layer) layer.clients.delete(conn); |
| 664 | if (err && !layer) ctx.status = 500; |
| 665 | await h.cleanup?.(args); |
| 666 | } |
| 667 | } finally { |
| 668 | await sub.dispose(); |
| 669 | this.activeHandlers.delete(h); |
| 670 | } |
| 671 | }; |
| 672 | |
| 673 | try { |
| 674 | // FIXME: should pass type check |
| 675 | await (this.ctx.parallel as any)('handler/create', h, 'ws'); |
| 676 | await (this.ctx.parallel as any)('handler/create/ws', h); |
| 677 | checker.call(h); |