@@ -4,6 +4,7 @@ import { type WebSocketEventType } from "coder/site/src/utils/OneWayWebSocket";
44import { EventSource } from "eventsource" ;
55
66import { createStreamingFetchAdapter } from "../api/streamingFetchAdapter" ;
7+ import { type Logger } from "../logging/logger" ;
78
89import { getQueryString } from "./utils" ;
910
@@ -24,11 +25,14 @@ export type SseConnectionInit = {
2425 location : { protocol : string ; host : string } ;
2526 apiRoute : string ;
2627 searchParams ?: Record < string , string > | URLSearchParams ;
28+ optionsHeaders ?: Record < string , string > ;
2729 axiosInstance : AxiosInstance ;
30+ logger : Logger ;
2831} ;
2932
3033export class SseConnection implements UnidirectionalStream < ServerSentEvent > {
3134 private readonly eventSource : EventSource ;
35+ private readonly logger : Logger ;
3236 private readonly callbacks = {
3337 open : new Set < EventHandler < ServerSentEvent , "open" > > ( ) ,
3438 close : new Set < EventHandler < ServerSentEvent , "close" > > ( ) ,
@@ -43,9 +47,13 @@ export class SseConnection implements UnidirectionalStream<ServerSentEvent> {
4347 public readonly url : string ;
4448
4549 public constructor ( init : SseConnectionInit ) {
50+ this . logger = init . logger ;
4651 this . url = this . buildUrl ( init ) ;
4752 this . eventSource = new EventSource ( this . url , {
48- fetch : createStreamingFetchAdapter ( init . axiosInstance ) ,
53+ fetch : createStreamingFetchAdapter (
54+ init . axiosInstance ,
55+ init . optionsHeaders ,
56+ ) ,
4957 } ) ;
5058 this . setupEventHandlers ( ) ;
5159 }
@@ -58,28 +66,48 @@ export class SseConnection implements UnidirectionalStream<ServerSentEvent> {
5866
5967 private setupEventHandlers ( ) : void {
6068 this . eventSource . addEventListener ( "open" , ( ) =>
61- this . callbacks . open . forEach ( ( cb ) => cb ( { } as WsEvent ) ) ,
69+ this . invokeCallbacks ( this . callbacks . open , { } as WsEvent , "open" ) ,
6270 ) ;
6371
6472 this . eventSource . addEventListener ( "data" , ( event : MessageEvent ) => {
65- [ ... this . messageWrappers . values ( ) ] . forEach ( ( wrapper ) => wrapper ( event ) ) ;
73+ this . invokeCallbacks ( this . messageWrappers . values ( ) , event , "message" ) ;
6674 } ) ;
6775
6876 this . eventSource . addEventListener ( "error" , ( error : Event | ErrorEvent ) => {
69- this . callbacks . error . forEach ( ( cb ) => cb ( this . createErrorEvent ( error ) ) ) ;
77+ this . invokeCallbacks (
78+ this . callbacks . error ,
79+ this . createErrorEvent ( error ) ,
80+ "error" ,
81+ ) ;
7082
7183 if ( this . eventSource . readyState === EventSource . CLOSED ) {
72- this . callbacks . close . forEach ( ( cb ) =>
73- cb ( {
84+ this . invokeCallbacks (
85+ this . callbacks . close ,
86+ {
7487 code : 1006 ,
7588 reason : "Connection lost" ,
7689 wasClean : false ,
77- } as WsCloseEvent ) ,
90+ } as WsCloseEvent ,
91+ "close" ,
7892 ) ;
7993 }
8094 } ) ;
8195 }
8296
97+ private invokeCallbacks < T > (
98+ callbacks : Iterable < ( event : T ) => void > ,
99+ event : T ,
100+ eventType : string ,
101+ ) : void {
102+ for ( const cb of callbacks ) {
103+ try {
104+ cb ( event ) ;
105+ } catch ( err ) {
106+ this . logger . error ( `Error in SSE ${ eventType } callback:` , err ) ;
107+ }
108+ }
109+ }
110+
83111 private createErrorEvent ( event : Event | ErrorEvent ) : WsErrorEvent {
84112 const errorMessage =
85113 event instanceof ErrorEvent && event . message
@@ -177,12 +205,14 @@ export class SseConnection implements UnidirectionalStream<ServerSentEvent> {
177205
178206 public close ( code ?: number , reason ?: string ) : void {
179207 this . eventSource . close ( ) ;
180- this . callbacks . close . forEach ( ( cb ) =>
181- cb ( {
208+ this . invokeCallbacks (
209+ this . callbacks . close ,
210+ {
182211 code : code ?? 1000 ,
183212 reason : reason ?? "Normal closure" ,
184213 wasClean : true ,
185- } as WsCloseEvent ) ,
214+ } as WsCloseEvent ,
215+ "close" ,
186216 ) ;
187217
188218 Object . values ( this . callbacks ) . forEach ( ( callbackSet ) => callbackSet . clear ( ) ) ;
0 commit comments