@@ -3852,200 +3852,128 @@ func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) {
38523852 }
38533853}
38543854
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 )
38653871 }
3866- return count
38673872}
38683873
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 ()
38703880
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 )
38773884
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+ ),
39033892 })
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 ()
40313896
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+ }
40363900}
40373901
40383902// The client sends a GOAWAY frame before the server finished processing a request.
40393903// 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 ()
40423934}
40433935
40443936// The client sends a GOAWAY frame before the server finishes processing a request,
40453937// but cancels the passed context before the request is completed. The expected
40463938// 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+ }
40493977}
40503978
40513979// Issue 25009: use Request.GetBody if present, even if it seems like
@@ -4164,10 +4092,6 @@ readFrames:
41644092 if err := rt .err (); err != bodyReadError {
41654093 t .Fatalf ("err = %v; want %v" , err , bodyReadError )
41664094 }
4167-
4168- if got := activeStreams (tc .cc ); got != 0 {
4169- t .Fatalf ("active streams count: %v; want 0" , got )
4170- }
41714095}
41724096
41734097func TestTransportBodyReadError_Immediately (t * testing.T ) { testTransportBodyReadError (t , nil ) }
0 commit comments