@@ -2659,3 +2659,94 @@ func TestConnectionError_Unwrap(t *testing.T) {
26592659 t .Error ("ConnectionError does not unwrap" )
26602660 }
26612661}
2662+
2663+ // Test that in the event of a graceful client transport shutdown, i.e.,
2664+ // clientTransport.Close(), client sends a goaway to the server with the correct
2665+ // error code and debug data.
2666+ func (s ) TestClientSendsAGoAwayFrame (t * testing.T ) {
2667+ // Create a server.
2668+ lis , err := net .Listen ("tcp" , "localhost:0" )
2669+ if err != nil {
2670+ t .Fatalf ("Error while listening: %v" , err )
2671+ }
2672+ defer lis .Close ()
2673+ // greetDone is used to notify when server is done greeting the client.
2674+ greetDone := make (chan struct {})
2675+ // errorCh verifies that desired GOAWAY not received by server
2676+ errorCh := make (chan error )
2677+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
2678+ defer cancel ()
2679+ // Launch the server.
2680+ go func () {
2681+ sconn , err := lis .Accept ()
2682+ if err != nil {
2683+ t .Errorf ("Error while accepting: %v" , err )
2684+ }
2685+ defer sconn .Close ()
2686+ if _ , err := io .ReadFull (sconn , make ([]byte , len (clientPreface ))); err != nil {
2687+ t .Errorf ("Error while writing settings ack: %v" , err )
2688+ return
2689+ }
2690+ sfr := http2 .NewFramer (sconn , sconn )
2691+ if err := sfr .WriteSettings (); err != nil {
2692+ t .Errorf ("Error while writing settings %v" , err )
2693+ return
2694+ }
2695+ fr , _ := sfr .ReadFrame ()
2696+ if _ , ok := fr .(* http2.SettingsFrame ); ! ok {
2697+ t .Errorf ("Expected settings frame, got %v" , fr )
2698+ }
2699+ fr , _ = sfr .ReadFrame ()
2700+ if fr , ok := fr .(* http2.SettingsFrame ); ! ok && fr .IsAck () {
2701+ t .Errorf ("Expected settings ACK frame, got %v" , fr )
2702+ }
2703+ fr , _ = sfr .ReadFrame ()
2704+ if fr , ok := fr .(* http2.HeadersFrame ); ! ok && fr .Flags .Has (http2 .FlagHeadersEndStream ) {
2705+ t .Errorf ("Expected Headers frame with END_HEADERS frame, got %v" , fr )
2706+ }
2707+ close (greetDone )
2708+
2709+ frame , err := sfr .ReadFrame ()
2710+ if err != nil {
2711+ return
2712+ }
2713+ switch fr := frame .(type ) {
2714+ case * http2.GoAwayFrame :
2715+ // Records that the server successfully received a GOAWAY frame.
2716+ goAwayFrame := fr
2717+ if goAwayFrame .ErrCode == http2 .ErrCodeNo {
2718+ t .Logf ("Received goAway frame from client" )
2719+ close (errorCh )
2720+ } else {
2721+ errorCh <- fmt .Errorf ("received unexpected goAway frame: %v" , err )
2722+ close (errorCh )
2723+ }
2724+ return
2725+ default :
2726+ errorCh <- fmt .Errorf ("server received a frame other than GOAWAY: %v" , err )
2727+ close (errorCh )
2728+ return
2729+ }
2730+ }()
2731+
2732+ ct , err := NewClientTransport (ctx , context .Background (), resolver.Address {Addr : lis .Addr ().String ()}, ConnectOptions {}, func (GoAwayReason ) {})
2733+ if err != nil {
2734+ t .Fatalf ("Error while creating client transport: %v" , err )
2735+ }
2736+ _ , err = ct .NewStream (ctx , & CallHdr {})
2737+ if err != nil {
2738+ t .Fatalf ("failed to open stream: %v" , err )
2739+ }
2740+ // Wait until server receives the headers and settings frame as part of greet.
2741+ <- greetDone
2742+ ct .Close (errors .New ("manually closed by client" ))
2743+ t .Logf ("Closed the client connection" )
2744+ select {
2745+ case err := <- errorCh :
2746+ if err != nil {
2747+ t .Errorf ("Error receiving the GOAWAY frame: %v" , err )
2748+ }
2749+ case <- ctx .Done ():
2750+ t .Errorf ("Context timed out" )
2751+ }
2752+ }
0 commit comments