@@ -5719,3 +5719,78 @@ func TestInvalidHeaderResponse(t *testing.T) {
5719
5719
t .Errorf (`bad "Foo " header value: %q, want %q` , v , "bar" )
5720
5720
}
5721
5721
}
5722
+
5723
+ // breakableConn is a net.Conn wrapper with a Write method
5724
+ // that will fail when its brokenState is true.
5725
+ type breakableConn struct {
5726
+ net.Conn
5727
+ * brokenState
5728
+ }
5729
+
5730
+ type brokenState struct {
5731
+ sync.Mutex
5732
+ broken bool
5733
+ }
5734
+
5735
+ func (w * breakableConn ) Write (b []byte ) (n int , err error ) {
5736
+ w .Lock ()
5737
+ defer w .Unlock ()
5738
+ if w .broken {
5739
+ return 0 , errors .New ("some write error" )
5740
+ }
5741
+ return w .Conn .Write (b )
5742
+ }
5743
+
5744
+ // Issue 34978: don't cache a broken HTTP/2 connection
5745
+ func TestDontCacheBrokenHTTP2Conn (t * testing.T ) {
5746
+ cst := newClientServerTest (t , h2Mode , HandlerFunc (func (w ResponseWriter , r * Request ) {}), optQuietLog )
5747
+ defer cst .close ()
5748
+
5749
+ var brokenState brokenState
5750
+
5751
+ cst .tr .Dial = func (netw , addr string ) (net.Conn , error ) {
5752
+ c , err := net .Dial (netw , addr )
5753
+ if err != nil {
5754
+ t .Errorf ("unexpected Dial error: %v" , err )
5755
+ return nil , err
5756
+ }
5757
+ return & breakableConn {c , & brokenState }, err
5758
+ }
5759
+
5760
+ const numReqs = 5
5761
+ var gotConns uint32 // atomic
5762
+ for i := 1 ; i <= numReqs ; i ++ {
5763
+ brokenState .Lock ()
5764
+ brokenState .broken = false
5765
+ brokenState .Unlock ()
5766
+
5767
+ // doBreak controls whether we break the TCP connection after the TLS
5768
+ // handshake (before the HTTP/2 handshake). We test a few failures
5769
+ // in a row followed by a final success.
5770
+ doBreak := i != numReqs
5771
+
5772
+ ctx := httptrace .WithClientTrace (context .Background (), & httptrace.ClientTrace {
5773
+ GotConn : func (info httptrace.GotConnInfo ) {
5774
+ atomic .AddUint32 (& gotConns , 1 )
5775
+ },
5776
+ TLSHandshakeDone : func (cfg tls.ConnectionState , err error ) {
5777
+ brokenState .Lock ()
5778
+ defer brokenState .Unlock ()
5779
+ if doBreak {
5780
+ brokenState .broken = true
5781
+ }
5782
+ },
5783
+ })
5784
+ req , err := NewRequestWithContext (ctx , "GET" , cst .ts .URL , nil )
5785
+ if err != nil {
5786
+ t .Fatal (err )
5787
+ }
5788
+ _ , err = cst .c .Do (req )
5789
+ if doBreak != (err != nil ) {
5790
+ t .Errorf ("for iteration %d, doBreak=%v; unexpected error %v" , i , doBreak , err )
5791
+ }
5792
+ }
5793
+ if got , want := atomic .LoadUint32 (& gotConns ), 1 ; int (got ) != want {
5794
+ t .Errorf ("GotConn calls = %v; want %v" , got , want )
5795
+ }
5796
+ }
0 commit comments