@@ -288,6 +288,7 @@ pin_project_lite::pin_project! {
288288 where R : SseStreamReconnect
289289 {
290290 retry_policy: Arc <dyn SseRetryPolicy >,
291+ reconnect_only_after_event_id: bool ,
291292 last_event_id: Option <String >,
292293 server_retry_interval: Option <Duration >,
293294 connector: R ,
@@ -304,6 +305,22 @@ impl<R: SseStreamReconnect> SseAutoReconnectStream<R> {
304305 ) -> Self {
305306 Self {
306307 retry_policy,
308+ reconnect_only_after_event_id : false ,
309+ last_event_id : None ,
310+ server_retry_interval : None ,
311+ connector,
312+ state : SseAutoReconnectStreamState :: Connected { stream } ,
313+ }
314+ }
315+
316+ pub fn new_after_event_id (
317+ stream : BoxedSseResponse ,
318+ connector : R ,
319+ retry_policy : Arc < dyn SseRetryPolicy > ,
320+ ) -> Self {
321+ Self {
322+ retry_policy,
323+ reconnect_only_after_event_id : true ,
307324 last_event_id : None ,
308325 server_retry_interval : None ,
309326 connector,
@@ -317,6 +334,7 @@ impl<E: std::error::Error + Send> SseAutoReconnectStream<NeverReconnect<E>> {
317334 pub ( crate ) fn never_reconnect ( stream : BoxedSseResponse , error_when_reconnect : E ) -> Self {
318335 Self {
319336 retry_policy : Arc :: new ( NeverRetry ) ,
337+ reconnect_only_after_event_id : false ,
320338 last_event_id : None ,
321339 server_retry_interval : None ,
322340 connector : NeverReconnect {
@@ -409,6 +427,10 @@ where
409427 this. state . set ( SseAutoReconnectStreamState :: Terminated ) ;
410428 return Poll :: Ready ( this. connector . map_fatal_stream_error ( e) . map ( Err ) ) ;
411429 }
430+ if * this. reconnect_only_after_event_id && this. last_event_id . is_none ( ) {
431+ this. state . set ( SseAutoReconnectStreamState :: Terminated ) ;
432+ return Poll :: Ready ( this. connector . map_fatal_stream_error ( e) . map ( Err ) ) ;
433+ }
412434 this. connector
413435 . handle_stream_error ( & e, this. last_event_id . as_deref ( ) ) ;
414436 let retrying = this
@@ -420,6 +442,13 @@ where
420442 }
421443 }
422444 None => {
445+ if * this. reconnect_only_after_event_id && this. last_event_id . is_none ( ) {
446+ tracing:: debug!(
447+ "sse response ended before an event ID was received; cannot resume"
448+ ) ;
449+ this. state . set ( SseAutoReconnectStreamState :: Terminated ) ;
450+ return Poll :: Ready ( None ) ;
451+ }
423452 // Per SEP-1699, a graceful stream close is
424453 // reconnectable. If the server sent a `retry` field
425454 // we MUST wait that long before reconnecting.
@@ -686,4 +715,24 @@ mod tests {
686715 && attempts. load( Ordering :: Relaxed ) == 0
687716 ) ;
688717 }
718+
719+ #[ tokio:: test]
720+ async fn response_without_event_id_does_not_reconnect ( ) {
721+ let attempts = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
722+ let connector = CountingReconnect {
723+ attempts : attempts. clone ( ) ,
724+ } ;
725+ let stream = SseAutoReconnectStream :: new_after_event_id (
726+ futures:: stream:: empty ( ) . boxed ( ) ,
727+ connector,
728+ Arc :: new ( FixedInterval {
729+ max_times : Some ( 1 ) ,
730+ duration : Duration :: ZERO ,
731+ } ) ,
732+ ) ;
733+ let mut stream = std:: pin:: pin!( stream) ;
734+
735+ assert ! ( stream. next( ) . await . is_none( ) ) ;
736+ assert_eq ! ( attempts. load( Ordering :: Relaxed ) , 0 ) ;
737+ }
689738}
0 commit comments