@@ -590,6 +590,108 @@ async fn test_bad_request_params_return_invalid_params_and_connection_stays_aliv
590590 . await ;
591591}
592592
593+ #[ tokio:: test( flavor = "current_thread" ) ]
594+ async fn test_bad_notification_params_swallowed_and_connection_stays_alive ( ) {
595+ use tokio:: io:: { AsyncWriteExt , BufReader } ;
596+ use tokio:: task:: LocalSet ;
597+
598+ let local = LocalSet :: new ( ) ;
599+
600+ local
601+ . run_until ( async {
602+ let ( mut client_writer, server_reader) = tokio:: io:: duplex ( 2048 ) ;
603+ let ( server_writer, client_reader) = tokio:: io:: duplex ( 2048 ) ;
604+
605+ let server_reader = server_reader. compat ( ) ;
606+ let server_writer = server_writer. compat_write ( ) ;
607+
608+ let server_transport =
609+ agent_client_protocol_core:: ByteStreams :: new ( server_writer, server_reader) ;
610+ let server = UntypedRole
611+ . builder ( )
612+ . on_receive_notification (
613+ async |_notif : SimpleNotification ,
614+ _connection : ConnectionTo < UntypedRole > | {
615+ // If we get here, the notification parsed successfully.
616+ Ok ( ( ) )
617+ } ,
618+ agent_client_protocol_core:: on_receive_notification!( ) ,
619+ )
620+ . on_receive_request (
621+ async |request : SimpleRequest ,
622+ responder : Responder < SimpleResponse > ,
623+ _connection : ConnectionTo < UntypedRole > | {
624+ responder. respond ( SimpleResponse {
625+ result : format ! ( "echo: {}" , request. message) ,
626+ } )
627+ } ,
628+ agent_client_protocol_core:: on_receive_request!( ) ,
629+ ) ;
630+
631+ tokio:: task:: spawn_local ( async move {
632+ if let Err ( err) = server. connect_to ( server_transport) . await {
633+ panic ! ( "server should stay alive: {err:?}" ) ;
634+ }
635+ } ) ;
636+
637+ let mut client_reader = BufReader :: new ( client_reader) ;
638+
639+ // Send a notification with bad params (wrong field name).
640+ // Notifications have no "id", so the server sends an error
641+ // notification (id: null) and keeps the connection alive.
642+ client_writer
643+ . write_all (
644+ br#"{"jsonrpc":"2.0","method":"simple_notification","params":{"wrong_field":"hello"}}
645+ "# ,
646+ )
647+ . await
648+ . unwrap ( ) ;
649+ client_writer. flush ( ) . await . unwrap ( ) ;
650+
651+ // The server sends an error notification (id: null) for the
652+ // malformed notification.
653+ let error_notification = read_jsonrpc_response_line ( & mut client_reader) . await ;
654+ expect ! [ [ r#"
655+ {
656+ "error": {
657+ "code": -32602,
658+ "data": {
659+ "error": "missing field `message`",
660+ "json": {
661+ "wrong_field": "hello"
662+ },
663+ "phase": "deserialization"
664+ },
665+ "message": "Invalid params"
666+ },
667+ "jsonrpc": "2.0"
668+ }"# ] ]
669+ . assert_eq ( & serde_json:: to_string_pretty ( & error_notification) . unwrap ( ) ) ;
670+
671+ // Now send a valid request to prove the connection is still alive.
672+ client_writer
673+ . write_all (
674+ br#"{"jsonrpc":"2.0","id":10,"method":"simple_method","params":{"message":"after bad notification"}}
675+ "# ,
676+ )
677+ . await
678+ . unwrap ( ) ;
679+ client_writer. flush ( ) . await . unwrap ( ) ;
680+
681+ let ok_response = read_jsonrpc_response_line ( & mut client_reader) . await ;
682+ expect ! [ [ r#"
683+ {
684+ "id": 10,
685+ "jsonrpc": "2.0",
686+ "result": {
687+ "result": "echo: after bad notification"
688+ }
689+ }"# ] ]
690+ . assert_eq ( & serde_json:: to_string_pretty ( & ok_response) . unwrap ( ) ) ;
691+ } )
692+ . await ;
693+ }
694+
593695#[ tokio:: test( flavor = "current_thread" ) ]
594696async fn test_match_dispatch_from_if_message_invalid_params_keeps_connection_alive ( ) {
595697 use tokio:: io:: { AsyncWriteExt , BufReader } ;
0 commit comments