@@ -201,7 +201,7 @@ impl CachedTx {
201201 Self :: new ( tx, None )
202202 }
203203
204- async fn send ( & mut self , message : ServerJsonRpcMessage ) {
204+ fn next_event_id ( & self ) -> EventId {
205205 let index = self . cache . back ( ) . map_or ( 0 , |m| {
206206 m. event_id
207207 . as_deref ( )
@@ -211,14 +211,33 @@ impl CachedTx {
211211 . index
212212 + 1
213213 } ) ;
214- let event_id = EventId {
214+ EventId {
215215 http_request_id : self . http_request_id ,
216216 index,
217+ }
218+ }
219+
220+ async fn send ( & mut self , message : ServerJsonRpcMessage ) {
221+ let event_id = self . next_event_id ( ) ;
222+ let message = ServerSseMessage {
223+ event_id : Some ( event_id. to_string ( ) ) ,
224+ message : Some ( Arc :: new ( message) ) ,
225+ retry : None ,
217226 } ;
227+ self . cache_and_send ( message) . await ;
228+ }
229+
230+ async fn send_priming ( & mut self , retry : Duration ) {
231+ let event_id = self . next_event_id ( ) ;
218232 let message = ServerSseMessage {
219233 event_id : Some ( event_id. to_string ( ) ) ,
220- message : Arc :: new ( message) ,
234+ message : None ,
235+ retry : Some ( retry) ,
221236 } ;
237+ self . cache_and_send ( message) . await ;
238+ }
239+
240+ async fn cache_and_send ( & mut self , message : ServerSseMessage ) {
222241 if self . cache . len ( ) >= self . capacity {
223242 self . cache . pop_front ( ) ;
224243 self . cache . push_back ( message. clone ( ) ) ;
@@ -525,7 +544,53 @@ impl LocalSessionWorker {
525544 }
526545 }
527546 }
547+
548+ async fn close_sse_stream (
549+ & mut self ,
550+ http_request_id : Option < HttpRequestId > ,
551+ retry_interval : Option < Duration > ,
552+ ) -> Result < ( ) , SessionError > {
553+ match http_request_id {
554+ // Close a request-wise stream
555+ Some ( id) => {
556+ let request_wise = self
557+ . tx_router
558+ . get_mut ( & id)
559+ . ok_or ( SessionError :: ChannelClosed ( Some ( id) ) ) ?;
560+
561+ // Send priming event if retry interval is specified
562+ if let Some ( interval) = retry_interval {
563+ request_wise. tx . send_priming ( interval) . await ;
564+ }
565+
566+ // Close the stream by dropping the sender
567+ let ( tx, _rx) = tokio:: sync:: mpsc:: channel ( 1 ) ;
568+ request_wise. tx . tx = tx;
569+
570+ tracing:: debug!(
571+ http_request_id = id,
572+ "closed SSE stream for server-initiated disconnection"
573+ ) ;
574+ Ok ( ( ) )
575+ }
576+ // Close the standalone (common) stream
577+ None => {
578+ // Send priming event if retry interval is specified
579+ if let Some ( interval) = retry_interval {
580+ self . common . send_priming ( interval) . await ;
581+ }
582+
583+ // Close the stream by dropping the sender
584+ let ( tx, _rx) = tokio:: sync:: mpsc:: channel ( 1 ) ;
585+ self . common . tx = tx;
586+
587+ tracing:: debug!( "closed standalone SSE stream for server-initiated disconnection" ) ;
588+ Ok ( ( ) )
589+ }
590+ }
591+ }
528592}
593+
529594#[ derive( Debug ) ]
530595pub enum SessionEvent {
531596 ClientMessage {
@@ -548,6 +613,13 @@ pub enum SessionEvent {
548613 responder : oneshot:: Sender < Result < ServerJsonRpcMessage , SessionError > > ,
549614 } ,
550615 Close ,
616+ CloseSseStream {
617+ /// The HTTP request ID to close. If `None`, closes the standalone (common) stream.
618+ http_request_id : Option < HttpRequestId > ,
619+ /// Optional retry interval. If provided, a priming event is sent before closing.
620+ retry_interval : Option < Duration > ,
621+ responder : oneshot:: Sender < Result < ( ) , SessionError > > ,
622+ } ,
551623}
552624
553625#[ derive( Debug , Clone ) ]
@@ -683,6 +755,60 @@ impl LocalSessionHandle {
683755 rx. await
684756 . map_err ( |_| SessionError :: SessionServiceTerminated ) ?
685757 }
758+
759+ /// Close an SSE stream for a specific request.
760+ ///
761+ /// This closes the SSE connection for a POST request stream, but keeps the session
762+ /// and message cache active. Clients can reconnect using the `Last-Event-ID` header
763+ /// via a GET request to resume receiving messages.
764+ ///
765+ /// # Arguments
766+ ///
767+ /// * `http_request_id` - The HTTP request ID of the stream to close
768+ /// * `retry_interval` - Optional retry interval. If provided, a priming event is sent
769+ pub async fn close_sse_stream (
770+ & self ,
771+ http_request_id : HttpRequestId ,
772+ retry_interval : Option < Duration > ,
773+ ) -> Result < ( ) , SessionError > {
774+ let ( tx, rx) = tokio:: sync:: oneshot:: channel ( ) ;
775+ self . event_tx
776+ . send ( SessionEvent :: CloseSseStream {
777+ http_request_id : Some ( http_request_id) ,
778+ retry_interval,
779+ responder : tx,
780+ } )
781+ . await
782+ . map_err ( |_| SessionError :: SessionServiceTerminated ) ?;
783+ rx. await
784+ . map_err ( |_| SessionError :: SessionServiceTerminated ) ?
785+ }
786+
787+ /// Close the standalone SSE stream.
788+ ///
789+ /// This closes the standalone SSE connection (established via GET request),
790+ /// but keeps the session and message cache active. Clients can reconnect using
791+ /// the `Last-Event-ID` header via a GET request to resume receiving messages.
792+ ///
793+ /// # Arguments
794+ ///
795+ /// * `retry_interval` - Optional retry interval. If provided, a priming event is sent
796+ pub async fn close_standalone_sse_stream (
797+ & self ,
798+ retry_interval : Option < Duration > ,
799+ ) -> Result < ( ) , SessionError > {
800+ let ( tx, rx) = tokio:: sync:: oneshot:: channel ( ) ;
801+ self . event_tx
802+ . send ( SessionEvent :: CloseSseStream {
803+ http_request_id : None ,
804+ retry_interval,
805+ responder : tx,
806+ } )
807+ . await
808+ . map_err ( |_| SessionError :: SessionServiceTerminated ) ?;
809+ rx. await
810+ . map_err ( |_| SessionError :: SessionServiceTerminated ) ?
811+ }
686812}
687813
688814pub type SessionTransport = WorkerTransport < LocalSessionWorker > ;
@@ -848,6 +974,15 @@ impl Worker for LocalSessionWorker {
848974 InnerEvent :: FromHttpService ( SessionEvent :: Close ) => {
849975 return Err ( WorkerQuitReason :: TransportClosed ) ;
850976 }
977+ InnerEvent :: FromHttpService ( SessionEvent :: CloseSseStream {
978+ http_request_id,
979+ retry_interval,
980+ responder,
981+ } ) => {
982+ let handle_result =
983+ self . close_sse_stream ( http_request_id, retry_interval) . await ;
984+ let _ = responder. send ( handle_result) ;
985+ }
851986 _ => {
852987 // ignore
853988 }
0 commit comments