@@ -10,13 +10,16 @@ package http2
1010import (
1111 "bytes"
1212 "context"
13+ "crypto/tls"
1314 "fmt"
1415 "io"
16+ "net"
1517 "net/http"
1618 "os"
1719 "reflect"
1820 "runtime"
1921 "slices"
22+ "sync"
2023 "sync/atomic"
2124 "testing"
2225 "time"
@@ -81,6 +84,56 @@ func TestTestClientConn(t *testing.T) {
8184 rt .wantBody (nil )
8285}
8386
87+ // TestConnectTimeout tests that a request does not exceed request timeout + dial timeout
88+ func TestConnectTimeout (t * testing.T ) {
89+ tr := & Transport {
90+ DialTLSContext : func (ctx context.Context , network , addr string , cfg * tls.Config ) (net.Conn , error ) {
91+ // mock a net dialler with 1s timeout, encountering network issue
92+ // keeping dialing until timeout
93+ var dialer = net.Dialer {Timeout : time .Duration (- 1 )}
94+ select {
95+ case <- time .After (time .Second ):
96+ case <- ctx .Done ():
97+ }
98+ return dialer .DialContext (ctx , network , addr )
99+ },
100+ AllowHTTP : true ,
101+ }
102+
103+ var sg sync.WaitGroup
104+ parentCtx , cancel := context .WithCancel (context .Background ())
105+ defer cancel ()
106+
107+ for j := 0 ; j < 2 ; j ++ {
108+ sg .Add (1 )
109+ go func () {
110+ for i := 0 ; i < 10000 ; i ++ {
111+ sg .Add (1 )
112+ go func () {
113+ ctx , _ := context .WithTimeout (parentCtx , time .Second )
114+ req , err := http .NewRequestWithContext (ctx , "GET" , "http://127.0.0.1:80" , nil )
115+ if err != nil {
116+ t .Errorf ("NewRequest: %v" , err )
117+ }
118+
119+ start := time .Now ()
120+ tr .RoundTrip (req )
121+ duration := time .Since (start )
122+ // duration should not exceed request timeout + dial timeout
123+ if duration > 2 * time .Second {
124+ t .Errorf ("RoundTrip took %s; want <2s" , duration .String ())
125+ }
126+ sg .Done ()
127+ }()
128+ time .Sleep (1 * time .Millisecond )
129+ }
130+ sg .Done ()
131+ }()
132+ }
133+
134+ sg .Wait ()
135+ }
136+
84137// A testClientConn allows testing ClientConn.RoundTrip against a fake server.
85138//
86139// A test using testClientConn consists of:
0 commit comments