@@ -3852,200 +3852,128 @@ func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) {
3852
3852
}
3853
3853
}
3854
3854
3855
- func activeStreams (cc * ClientConn ) int {
3856
- count := 0
3857
- cc .mu .Lock ()
3858
- defer cc .mu .Unlock ()
3859
- for _ , cs := range cc .streams {
3860
- select {
3861
- case <- cs .abort :
3862
- default :
3863
- count ++
3864
- }
3855
+ // The client closes the connection just after the server got the client's HEADERS
3856
+ // frame, but before the server sends its HEADERS response back. The expected
3857
+ // result is an error on RoundTrip explaining the client closed the connection.
3858
+ func TestClientConnCloseAtHeaders (t * testing.T ) { synctestTest (t , testClientConnCloseAtHeaders ) }
3859
+ func testClientConnCloseAtHeaders (t testing.TB ) {
3860
+ tc := newTestClientConn (t )
3861
+ tc .greet ()
3862
+
3863
+ req , _ := http .NewRequest ("GET" , "https://dummy.tld/" , nil )
3864
+ rt := tc .roundTrip (req )
3865
+ tc .wantFrameType (FrameHeaders )
3866
+
3867
+ tc .cc .Close ()
3868
+ synctest .Wait ()
3869
+ if err := rt .err (); err != errClientConnForceClosed {
3870
+ t .Fatalf ("RoundTrip error = %v, want errClientConnForceClosed" , err )
3865
3871
}
3866
- return count
3867
3872
}
3868
3873
3869
- type closeMode int
3874
+ // The client closes the connection while reading the response.
3875
+ // The expected behavior is a response body io read error on the client.
3876
+ func TestClientConnCloseAtBody (t * testing.T ) { synctestTest (t , testClientConnCloseAtBody ) }
3877
+ func testClientConnCloseAtBody (t testing.TB ) {
3878
+ tc := newTestClientConn (t )
3879
+ tc .greet ()
3870
3880
3871
- const (
3872
- closeAtHeaders closeMode = iota
3873
- closeAtBody
3874
- shutdown
3875
- shutdownCancel
3876
- )
3881
+ req , _ := http .NewRequest ("GET" , "https://dummy.tld/" , nil )
3882
+ rt := tc .roundTrip (req )
3883
+ tc .wantFrameType (FrameHeaders )
3877
3884
3878
- // See golang.org/issue/17292
3879
- func testClientConnClose (t * testing.T , closeMode closeMode ) {
3880
- clientDone := make (chan struct {})
3881
- defer close (clientDone )
3882
- handlerDone := make (chan struct {})
3883
- closeDone := make (chan struct {})
3884
- beforeHeader := func () {}
3885
- bodyWrite := func (w http.ResponseWriter ) {}
3886
- ts := newTestServer (t , func (w http.ResponseWriter , r * http.Request ) {
3887
- defer close (handlerDone )
3888
- beforeHeader ()
3889
- w .WriteHeader (http .StatusOK )
3890
- w .(http.Flusher ).Flush ()
3891
- bodyWrite (w )
3892
- select {
3893
- case <- w .(http.CloseNotifier ).CloseNotify ():
3894
- // client closed connection before completion
3895
- if closeMode == shutdown || closeMode == shutdownCancel {
3896
- t .Error ("expected request to complete" )
3897
- }
3898
- case <- clientDone :
3899
- if closeMode == closeAtHeaders || closeMode == closeAtBody {
3900
- t .Error ("expected connection closed by client" )
3901
- }
3902
- }
3885
+ tc .writeHeaders (HeadersFrameParam {
3886
+ StreamID : rt .streamID (),
3887
+ EndHeaders : true ,
3888
+ EndStream : false ,
3889
+ BlockFragment : tc .makeHeaderBlockFragment (
3890
+ ":status" , "200" ,
3891
+ ),
3903
3892
})
3904
- tr := & Transport {TLSClientConfig : tlsConfigInsecure }
3905
- defer tr .CloseIdleConnections ()
3906
- ctx := context .Background ()
3907
- cc , err := tr .dialClientConn (ctx , ts .Listener .Addr ().String (), false )
3908
- req , err := http .NewRequest ("GET" , ts .URL , nil )
3909
- if err != nil {
3910
- t .Fatal (err )
3911
- }
3912
- if closeMode == closeAtHeaders {
3913
- beforeHeader = func () {
3914
- if err := cc .Close (); err != nil {
3915
- t .Error (err )
3916
- }
3917
- close (closeDone )
3918
- }
3919
- }
3920
- var sendBody chan struct {}
3921
- if closeMode == closeAtBody {
3922
- sendBody = make (chan struct {})
3923
- bodyWrite = func (w http.ResponseWriter ) {
3924
- <- sendBody
3925
- b := make ([]byte , 32 )
3926
- w .Write (b )
3927
- w .(http.Flusher ).Flush ()
3928
- if err := cc .Close (); err != nil {
3929
- t .Errorf ("unexpected ClientConn close error: %v" , err )
3930
- }
3931
- close (closeDone )
3932
- w .Write (b )
3933
- w .(http.Flusher ).Flush ()
3934
- }
3935
- }
3936
- res , err := cc .RoundTrip (req )
3937
- if res != nil {
3938
- defer res .Body .Close ()
3939
- }
3940
- if closeMode == closeAtHeaders {
3941
- got := fmt .Sprint (err )
3942
- want := "http2: client connection force closed via ClientConn.Close"
3943
- if got != want {
3944
- t .Fatalf ("RoundTrip error = %v, want %v" , got , want )
3945
- }
3946
- } else {
3947
- if err != nil {
3948
- t .Fatalf ("RoundTrip: %v" , err )
3949
- }
3950
- if got , want := activeStreams (cc ), 1 ; got != want {
3951
- t .Errorf ("got %d active streams, want %d" , got , want )
3952
- }
3953
- }
3954
- switch closeMode {
3955
- case shutdownCancel :
3956
- if err = cc .Shutdown (canceledCtx ); err != context .Canceled {
3957
- t .Errorf ("got %v, want %v" , err , context .Canceled )
3958
- }
3959
- if cc .closing == false {
3960
- t .Error ("expected closing to be true" )
3961
- }
3962
- if cc .CanTakeNewRequest () == true {
3963
- t .Error ("CanTakeNewRequest to return false" )
3964
- }
3965
- if v , want := len (cc .streams ), 1 ; v != want {
3966
- t .Errorf ("expected %d active streams, got %d" , want , v )
3967
- }
3968
- clientDone <- struct {}{}
3969
- <- handlerDone
3970
- case shutdown :
3971
- wait := make (chan struct {})
3972
- shutdownEnterWaitStateHook = func () {
3973
- close (wait )
3974
- shutdownEnterWaitStateHook = func () {}
3975
- }
3976
- defer func () { shutdownEnterWaitStateHook = func () {} }()
3977
- shutdown := make (chan struct {}, 1 )
3978
- go func () {
3979
- if err = cc .Shutdown (context .Background ()); err != nil {
3980
- t .Error (err )
3981
- }
3982
- close (shutdown )
3983
- }()
3984
- // Let the shutdown to enter wait state
3985
- <- wait
3986
- cc .mu .Lock ()
3987
- if cc .closing == false {
3988
- t .Error ("expected closing to be true" )
3989
- }
3990
- cc .mu .Unlock ()
3991
- if cc .CanTakeNewRequest () == true {
3992
- t .Error ("CanTakeNewRequest to return false" )
3993
- }
3994
- if got , want := activeStreams (cc ), 1 ; got != want {
3995
- t .Errorf ("got %d active streams, want %d" , got , want )
3996
- }
3997
- // Let the active request finish
3998
- clientDone <- struct {}{}
3999
- // Wait for the shutdown to end
4000
- select {
4001
- case <- shutdown :
4002
- case <- time .After (2 * time .Second ):
4003
- t .Fatal ("expected server connection to close" )
4004
- }
4005
- case closeAtHeaders , closeAtBody :
4006
- if closeMode == closeAtBody {
4007
- go close (sendBody )
4008
- if _ , err := io .Copy (io .Discard , res .Body ); err == nil {
4009
- t .Error ("expected a Copy error, got nil" )
4010
- }
4011
- }
4012
- <- closeDone
4013
- if got , want := activeStreams (cc ), 0 ; got != want {
4014
- t .Errorf ("got %d active streams, want %d" , got , want )
4015
- }
4016
- // wait for server to get the connection close notice
4017
- select {
4018
- case <- handlerDone :
4019
- case <- time .After (2 * time .Second ):
4020
- t .Fatal ("expected server connection to close" )
4021
- }
4022
- }
4023
- }
4024
-
4025
- // The client closes the connection just after the server got the client's HEADERS
4026
- // frame, but before the server sends its HEADERS response back. The expected
4027
- // result is an error on RoundTrip explaining the client closed the connection.
4028
- func TestClientConnCloseAtHeaders (t * testing.T ) {
4029
- testClientConnClose (t , closeAtHeaders )
4030
- }
3893
+ tc .writeData (rt .streamID (), false , make ([]byte , 64 ))
3894
+ tc .cc .Close ()
3895
+ synctest .Wait ()
4031
3896
4032
- // The client closes the connection between two server's response DATA frames.
4033
- // The expected behavior is a response body io read error on the client.
4034
- func TestClientConnCloseAtBody (t * testing.T ) {
4035
- testClientConnClose (t , closeAtBody )
3897
+ if _ , err := io .Copy (io .Discard , rt .response ().Body ); err == nil {
3898
+ t .Error ("expected a Copy error, got nil" )
3899
+ }
4036
3900
}
4037
3901
4038
3902
// The client sends a GOAWAY frame before the server finished processing a request.
4039
3903
// We expect the connection not to close until the request is completed.
4040
- func TestClientConnShutdown (t * testing.T ) {
4041
- testClientConnClose (t , shutdown )
3904
+ func TestClientConnShutdown (t * testing.T ) { synctestTest (t , testClientConnShutdown ) }
3905
+ func testClientConnShutdown (t testing.TB ) {
3906
+ tc := newTestClientConn (t )
3907
+ tc .greet ()
3908
+
3909
+ req , _ := http .NewRequest ("GET" , "https://dummy.tld/" , nil )
3910
+ rt := tc .roundTrip (req )
3911
+ tc .wantFrameType (FrameHeaders )
3912
+
3913
+ go tc .cc .Shutdown (context .Background ())
3914
+ synctest .Wait ()
3915
+
3916
+ tc .wantFrameType (FrameGoAway )
3917
+ tc .wantIdle () // connection is not closed
3918
+ body := []byte ("body" )
3919
+ tc .writeHeaders (HeadersFrameParam {
3920
+ StreamID : rt .streamID (),
3921
+ EndHeaders : true ,
3922
+ EndStream : false ,
3923
+ BlockFragment : tc .makeHeaderBlockFragment (
3924
+ ":status" , "200" ,
3925
+ ),
3926
+ })
3927
+ tc .writeData (rt .streamID (), true , body )
3928
+
3929
+ rt .wantStatus (200 )
3930
+ rt .wantBody (body )
3931
+
3932
+ // Now that the client has received the response, it closes the connection.
3933
+ tc .wantClosed ()
4042
3934
}
4043
3935
4044
3936
// The client sends a GOAWAY frame before the server finishes processing a request,
4045
3937
// but cancels the passed context before the request is completed. The expected
4046
3938
// behavior is the client closing the connection after the context is canceled.
4047
- func TestClientConnShutdownCancel (t * testing.T ) {
4048
- testClientConnClose (t , shutdownCancel )
3939
+ func TestClientConnShutdownCancel (t * testing.T ) { synctestTest (t , testClientConnShutdownCancel ) }
3940
+ func testClientConnShutdownCancel (t testing.TB ) {
3941
+ tc := newTestClientConn (t )
3942
+ tc .greet ()
3943
+
3944
+ req , _ := http .NewRequest ("GET" , "https://dummy.tld/" , nil )
3945
+ rt := tc .roundTrip (req )
3946
+ tc .wantFrameType (FrameHeaders )
3947
+
3948
+ ctx , cancel := context .WithCancel (t .Context ())
3949
+ var shutdownErr error
3950
+ go func () {
3951
+ shutdownErr = tc .cc .Shutdown (ctx )
3952
+ }()
3953
+ synctest .Wait ()
3954
+
3955
+ tc .wantFrameType (FrameGoAway )
3956
+ tc .wantIdle () // connection is not closed
3957
+
3958
+ cancel ()
3959
+ synctest .Wait ()
3960
+
3961
+ if shutdownErr != context .Canceled {
3962
+ t .Fatalf ("ClientConn.Shutdown(ctx) did not return context.Canceled after cancelling context" )
3963
+ }
3964
+
3965
+ // The documentation for this test states:
3966
+ // The expected behavior is the client closing the connection
3967
+ // after the context is canceled.
3968
+ //
3969
+ // This seems reasonable, but it isn't what we do.
3970
+ // When ClientConn.Shutdown's context is canceled, Shutdown returns but
3971
+ // the connection is not closed.
3972
+ //
3973
+ // TODO: Figure out the correct behavior.
3974
+ if rt .done () {
3975
+ t .Fatal ("RoundTrip unexpectedly returned during shutdown" )
3976
+ }
4049
3977
}
4050
3978
4051
3979
// Issue 25009: use Request.GetBody if present, even if it seems like
@@ -4164,10 +4092,6 @@ readFrames:
4164
4092
if err := rt .err (); err != bodyReadError {
4165
4093
t .Fatalf ("err = %v; want %v" , err , bodyReadError )
4166
4094
}
4167
-
4168
- if got := activeStreams (tc .cc ); got != 0 {
4169
- t .Fatalf ("active streams count: %v; want 0" , got )
4170
- }
4171
4095
}
4172
4096
4173
4097
func TestTransportBodyReadError_Immediately (t * testing.T ) { testTransportBodyReadError (t , nil ) }
0 commit comments