@@ -99,62 +99,57 @@ type testClientConn struct {
9999
100100 roundtrips []* testRoundTrip
101101
102- rerr error // returned by Read
103- rbuf bytes.Buffer // sent to the test conn
104- wbuf bytes.Buffer // sent by the test conn
102+ rerr error // returned by Read
103+ netConnClosed bool // set when the ClientConn closes the net.Conn
104+ rbuf bytes.Buffer // sent to the test conn
105+ wbuf bytes.Buffer // sent by the test conn
105106}
106107
107- func newTestClientConn (t * testing.T , opts ... func (* Transport )) * testClientConn {
108- t .Helper ()
109-
110- tr := & Transport {}
111- for _ , o := range opts {
112- o (tr )
113- }
114-
108+ func newTestClientConnFromClientConn (t * testing.T , cc * ClientConn ) * testClientConn {
115109 tc := & testClientConn {
116110 t : t ,
117- tr : tr ,
118- hooks : newTestSyncHooks (),
111+ tr : cc .t ,
112+ cc : cc ,
113+ hooks : cc .t .syncHooks ,
119114 }
115+ cc .tconn = (* testClientConnNetConn )(tc )
120116 tc .enc = hpack .NewEncoder (& tc .encbuf )
121117 tc .fr = NewFramer (& tc .rbuf , & tc .wbuf )
122118 tc .fr .ReadMetaHeaders = hpack .NewDecoder (initialHeaderTableSize , nil )
123119 tc .fr .SetMaxReadFrameSize (10 << 20 )
124-
125120 t .Cleanup (func () {
126121 tc .sync ()
127122 if tc .rerr == nil {
128123 tc .rerr = io .EOF
129124 }
130125 tc .sync ()
131- if tc .hooks .total != 0 {
132- t .Errorf ("%v goroutines still running after test completed" , tc .hooks .total )
133- }
134-
135126 })
127+ return tc
128+ }
136129
137- tc .hooks .newclientconn = func (cc * ClientConn ) {
138- tc .cc = cc
139- }
140- const singleUse = false
141- _ , err := tc .tr .newClientConn ((* testClientConnNetConn )(tc ), singleUse , tc .hooks )
142- if err != nil {
143- t .Fatal (err )
144- }
145- tc .sync ()
146- tc .hooks .newclientconn = nil
147-
130+ func (tc * testClientConn ) readClientPreface () {
131+ tc .t .Helper ()
148132 // Read the client's HTTP/2 preface, sent prior to any HTTP/2 frames.
149133 buf := make ([]byte , len (clientPreface ))
150134 if _ , err := io .ReadFull (& tc .wbuf , buf ); err != nil {
151- t .Fatalf ("reading preface: %v" , err )
135+ tc . t .Fatalf ("reading preface: %v" , err )
152136 }
153137 if ! bytes .Equal (buf , clientPreface ) {
154- t .Fatalf ("client preface: %q, want %q" , buf , clientPreface )
138+ tc . t .Fatalf ("client preface: %q, want %q" , buf , clientPreface )
155139 }
140+ }
156141
157- return tc
142+ func newTestClientConn (t * testing.T , opts ... func (* Transport )) * testClientConn {
143+ t .Helper ()
144+
145+ tt := newTestTransport (t , opts ... )
146+ const singleUse = false
147+ _ , err := tt .tr .newClientConn (nil , singleUse , tt .tr .syncHooks )
148+ if err != nil {
149+ t .Fatalf ("newClientConn: %v" , err )
150+ }
151+
152+ return tt .getConn ()
158153}
159154
160155// sync waits for the ClientConn under test to reach a stable state,
@@ -349,7 +344,7 @@ func (b *testRequestBody) closeWithError(err error) {
349344// the request times out, or some other terminal condition is reached.)
350345func (tc * testClientConn ) roundTrip (req * http.Request ) * testRoundTrip {
351346 rt := & testRoundTrip {
352- tc : tc ,
347+ t : tc . t ,
353348 donec : make (chan struct {}),
354349 }
355350 tc .roundtrips = append (tc .roundtrips , rt )
@@ -362,6 +357,9 @@ func (tc *testClientConn) roundTrip(req *http.Request) *testRoundTrip {
362357 tc .hooks .newstream = nil
363358
364359 tc .t .Cleanup (func () {
360+ if ! rt .done () {
361+ return
362+ }
365363 res , _ := rt .result ()
366364 if res != nil {
367365 res .Body .Close ()
@@ -460,6 +458,14 @@ func (tc *testClientConn) writeContinuation(streamID uint32, endHeaders bool, he
460458 tc .sync ()
461459}
462460
461+ func (tc * testClientConn ) writeRSTStream (streamID uint32 , code ErrCode ) {
462+ tc .t .Helper ()
463+ if err := tc .fr .WriteRSTStream (streamID , code ); err != nil {
464+ tc .t .Fatal (err )
465+ }
466+ tc .sync ()
467+ }
468+
463469func (tc * testClientConn ) writePing (ack bool , data [8 ]byte ) {
464470 tc .t .Helper ()
465471 if err := tc .fr .WritePing (ack , data ); err != nil {
@@ -491,9 +497,25 @@ func (tc *testClientConn) closeWrite(err error) {
491497 tc .sync ()
492498}
493499
500+ // inflowWindow returns the amount of inbound flow control available for a stream,
501+ // or for the connection if streamID is 0.
502+ func (tc * testClientConn ) inflowWindow (streamID uint32 ) int32 {
503+ tc .cc .mu .Lock ()
504+ defer tc .cc .mu .Unlock ()
505+ if streamID == 0 {
506+ return tc .cc .inflow .avail + tc .cc .inflow .unsent
507+ }
508+ cs := tc .cc .streams [streamID ]
509+ if cs == nil {
510+ tc .t .Errorf ("no stream with id %v" , streamID )
511+ return - 1
512+ }
513+ return cs .inflow .avail + cs .inflow .unsent
514+ }
515+
494516// testRoundTrip manages a RoundTrip in progress.
495517type testRoundTrip struct {
496- tc * testClientConn
518+ t * testing. T
497519 resp * http.Response
498520 respErr error
499521 donec chan struct {}
@@ -502,6 +524,9 @@ type testRoundTrip struct {
502524
503525// streamID returns the HTTP/2 stream ID of the request.
504526func (rt * testRoundTrip ) streamID () uint32 {
527+ if rt .cs == nil {
528+ panic ("stream ID unknown" )
529+ }
505530 return rt .cs .ID
506531}
507532
@@ -517,20 +542,20 @@ func (rt *testRoundTrip) done() bool {
517542
518543// result returns the result of the RoundTrip.
519544func (rt * testRoundTrip ) result () (* http.Response , error ) {
520- t := rt .tc . t
545+ t := rt .t
521546 t .Helper ()
522547 select {
523548 case <- rt .donec :
524549 default :
525- t .Fatalf ("RoundTrip (stream %v) is not done; want it to be" , rt . streamID () )
550+ t .Fatalf ("RoundTrip is not done; want it to be" )
526551 }
527552 return rt .resp , rt .respErr
528553}
529554
530555// response returns the response of a successful RoundTrip.
531556// If the RoundTrip unexpectedly failed, it calls t.Fatal.
532557func (rt * testRoundTrip ) response () * http.Response {
533- t := rt .tc . t
558+ t := rt .t
534559 t .Helper ()
535560 resp , err := rt .result ()
536561 if err != nil {
@@ -544,15 +569,15 @@ func (rt *testRoundTrip) response() *http.Response {
544569
545570// err returns the (possibly nil) error result of RoundTrip.
546571func (rt * testRoundTrip ) err () error {
547- t := rt .tc . t
572+ t := rt .t
548573 t .Helper ()
549574 _ , err := rt .result ()
550575 return err
551576}
552577
553578// wantStatus indicates the expected response StatusCode.
554579func (rt * testRoundTrip ) wantStatus (want int ) {
555- t := rt .tc . t
580+ t := rt .t
556581 t .Helper ()
557582 if got := rt .response ().StatusCode ; got != want {
558583 t .Fatalf ("got response status %v, want %v" , got , want )
@@ -561,15 +586,15 @@ func (rt *testRoundTrip) wantStatus(want int) {
561586
562587// body reads the contents of the response body.
563588func (rt * testRoundTrip ) readBody () ([]byte , error ) {
564- t := rt .tc . t
589+ t := rt .t
565590 t .Helper ()
566591 return io .ReadAll (rt .response ().Body )
567592}
568593
569594// wantBody indicates the expected response body.
570595// (Note that this consumes the body.)
571596func (rt * testRoundTrip ) wantBody (want []byte ) {
572- t := rt .tc . t
597+ t := rt .t
573598 t .Helper ()
574599 got , err := rt .readBody ()
575600 if err != nil {
@@ -582,7 +607,7 @@ func (rt *testRoundTrip) wantBody(want []byte) {
582607
583608// wantHeaders indicates the expected response headers.
584609func (rt * testRoundTrip ) wantHeaders (want http.Header ) {
585- t := rt .tc . t
610+ t := rt .t
586611 t .Helper ()
587612 res := rt .response ()
588613 if diff := diffHeaders (res .Header , want ); diff != "" {
@@ -592,7 +617,7 @@ func (rt *testRoundTrip) wantHeaders(want http.Header) {
592617
593618// wantTrailers indicates the expected response trailers.
594619func (rt * testRoundTrip ) wantTrailers (want http.Header ) {
595- t := rt .tc . t
620+ t := rt .t
596621 t .Helper ()
597622 res := rt .response ()
598623 if diff := diffHeaders (res .Trailer , want ); diff != "" {
@@ -630,7 +655,8 @@ func (nc *testClientConnNetConn) Write(b []byte) (n int, err error) {
630655 return nc .wbuf .Write (b )
631656}
632657
633- func (* testClientConnNetConn ) Close () error {
658+ func (nc * testClientConnNetConn ) Close () error {
659+ nc .netConnClosed = true
634660 return nil
635661}
636662
@@ -639,3 +665,91 @@ func (*testClientConnNetConn) RemoteAddr() (_ net.Addr) { return }
639665func (* testClientConnNetConn ) SetDeadline (t time.Time ) error { return nil }
640666func (* testClientConnNetConn ) SetReadDeadline (t time.Time ) error { return nil }
641667func (* testClientConnNetConn ) SetWriteDeadline (t time.Time ) error { return nil }
668+
669+ // A testTransport allows testing Transport.RoundTrip against fake servers.
670+ // Tests that aren't specifically exercising RoundTrip's retry loop or connection pooling
671+ // should use testClientConn instead.
672+ type testTransport struct {
673+ t * testing.T
674+ tr * Transport
675+
676+ ccs []* testClientConn
677+ }
678+
679+ func newTestTransport (t * testing.T , opts ... func (* Transport )) * testTransport {
680+ tr := & Transport {
681+ syncHooks : newTestSyncHooks (),
682+ }
683+ for _ , o := range opts {
684+ o (tr )
685+ }
686+
687+ tt := & testTransport {
688+ t : t ,
689+ tr : tr ,
690+ }
691+ tr .syncHooks .newclientconn = func (cc * ClientConn ) {
692+ tt .ccs = append (tt .ccs , newTestClientConnFromClientConn (t , cc ))
693+ }
694+
695+ t .Cleanup (func () {
696+ tt .sync ()
697+ if len (tt .ccs ) > 0 {
698+ t .Fatalf ("%v test ClientConns created, but not examined by test" , len (tt .ccs ))
699+ }
700+ if tt .tr .syncHooks .total != 0 {
701+ t .Errorf ("%v goroutines still running after test completed" , tt .tr .syncHooks .total )
702+ }
703+ })
704+
705+ return tt
706+ }
707+
708+ func (tt * testTransport ) sync () {
709+ tt .tr .syncHooks .waitInactive ()
710+ }
711+
712+ func (tt * testTransport ) advance (d time.Duration ) {
713+ tt .tr .syncHooks .advance (d )
714+ tt .sync ()
715+ }
716+
717+ func (tt * testTransport ) hasConn () bool {
718+ return len (tt .ccs ) > 0
719+ }
720+
721+ func (tt * testTransport ) getConn () * testClientConn {
722+ tt .t .Helper ()
723+ if len (tt .ccs ) == 0 {
724+ tt .t .Fatalf ("no new ClientConns created; wanted one" )
725+ }
726+ tc := tt .ccs [0 ]
727+ tt .ccs = tt .ccs [1 :]
728+ tc .sync ()
729+ tc .readClientPreface ()
730+ return tc
731+ }
732+
733+ func (tt * testTransport ) roundTrip (req * http.Request ) * testRoundTrip {
734+ rt := & testRoundTrip {
735+ t : tt .t ,
736+ donec : make (chan struct {}),
737+ }
738+ tt .tr .syncHooks .goRun (func () {
739+ defer close (rt .donec )
740+ rt .resp , rt .respErr = tt .tr .RoundTrip (req )
741+ })
742+ tt .sync ()
743+
744+ tt .t .Cleanup (func () {
745+ if ! rt .done () {
746+ return
747+ }
748+ res , _ := rt .result ()
749+ if res != nil {
750+ res .Body .Close ()
751+ }
752+ })
753+
754+ return rt
755+ }
0 commit comments