@@ -521,6 +521,54 @@ func TestConnection_DoesNotCancelInboundContextBeforeDrainingNotificationsOnDisc
521521 }
522522}
523523
524+ func TestConnection_CancelsRequestHandlersOnDisconnectEvenWithNotificationBacklog (t * testing.T ) {
525+ const numNotifications = 200
526+
527+ incomingR , incomingW := io .Pipe ()
528+
529+ reqDone := make (chan struct {})
530+
531+ c := NewConnection (func (ctx context.Context , method string , _ json.RawMessage ) (any , * RequestError ) {
532+ switch method {
533+ case "test/notify" :
534+ // Slow down to create a backlog of queued notifications.
535+ time .Sleep (5 * time .Millisecond )
536+ return nil , nil
537+ case "test/request" :
538+ // Requests should be canceled promptly on disconnect (uses c.ctx).
539+ <- ctx .Done ()
540+ close (reqDone )
541+ return nil , NewInternalError (map [string ]any {"error" : "canceled" })
542+ default :
543+ return nil , nil
544+ }
545+ }, io .Discard , incomingR )
546+
547+ for i := 0 ; i < numNotifications ; i ++ {
548+ if _ , err := io .WriteString (incomingW , `{"jsonrpc":"2.0","method":"test/notify","params":{}}` + "\n " ); err != nil {
549+ t .Fatalf ("write notification: %v" , err )
550+ }
551+ }
552+ if _ , err := io .WriteString (incomingW , `{"jsonrpc":"2.0","id":1,"method":"test/request","params":{}}` + "\n " ); err != nil {
553+ t .Fatalf ("write request: %v" , err )
554+ }
555+ _ = incomingW .Close ()
556+
557+ // Disconnect should be observed quickly.
558+ select {
559+ case <- c .Done ():
560+ case <- time .After (2 * time .Second ):
561+ t .Fatalf ("timeout waiting for connection Done()" )
562+ }
563+
564+ // Even with a big notification backlog, the request handler should be canceled promptly.
565+ select {
566+ case <- reqDone :
567+ case <- time .After (1 * time .Second ):
568+ t .Fatalf ("timeout waiting for request handler cancellation" )
569+ }
570+ }
571+
524572// Test initialize method behavior
525573func TestConnectionHandlesInitialize (t * testing.T ) {
526574 c2aR , c2aW := io .Pipe ()
0 commit comments