@@ -1205,64 +1205,80 @@ func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) }
1205
1205
func testClientTimeout (t * testing.T , h2 bool ) {
1206
1206
setParallel (t )
1207
1207
defer afterTest (t )
1208
- testDone := make (chan struct {}) // closed in defer below
1209
1208
1210
- sawRoot := make (chan bool , 1 )
1211
- sawSlow := make (chan bool , 1 )
1209
+ var (
1210
+ mu sync.Mutex
1211
+ nonce string // a unique per-request string
1212
+ sawSlowNonce bool // true if the handler saw /slow?nonce=<nonce>
1213
+ )
1212
1214
cst := newClientServerTest (t , h2 , HandlerFunc (func (w ResponseWriter , r * Request ) {
1215
+ _ = r .ParseForm ()
1213
1216
if r .URL .Path == "/" {
1214
- sawRoot <- true
1215
- Redirect (w , r , "/slow" , StatusFound )
1217
+ Redirect (w , r , "/slow?nonce=" + r .Form .Get ("nonce" ), StatusFound )
1216
1218
return
1217
1219
}
1218
1220
if r .URL .Path == "/slow" {
1219
- sawSlow <- true
1221
+ mu .Lock ()
1222
+ if r .Form .Get ("nonce" ) == nonce {
1223
+ sawSlowNonce = true
1224
+ } else {
1225
+ t .Logf ("mismatched nonce: received %s, want %s" , r .Form .Get ("nonce" ), nonce )
1226
+ }
1227
+ mu .Unlock ()
1228
+
1220
1229
w .Write ([]byte ("Hello" ))
1221
1230
w .(Flusher ).Flush ()
1222
- <- testDone
1231
+ <- r . Context (). Done ()
1223
1232
return
1224
1233
}
1225
1234
}))
1226
1235
defer cst .close ()
1227
- defer close (testDone ) // before cst.close, to unblock /slow handler
1228
1236
1229
- // 200ms should be long enough to get a normal request (the /
1230
- // handler), but not so long that it makes the test slow.
1231
- const timeout = 200 * time .Millisecond
1232
- cst .c .Timeout = timeout
1233
-
1234
- res , err := cst .c .Get (cst .ts .URL )
1235
- if err != nil {
1236
- if strings .Contains (err .Error (), "Client.Timeout" ) {
1237
- t .Skipf ("host too slow to get fast resource in %v" , timeout )
1237
+ // Try to trigger a timeout after reading part of the response body.
1238
+ // The initial timeout is emprically usually long enough on a decently fast
1239
+ // machine, but if we undershoot we'll retry with exponentially longer
1240
+ // timeouts until the test either passes or times out completely.
1241
+ // This keeps the test reasonably fast in the typical case but allows it to
1242
+ // also eventually succeed on arbitrarily slow machines.
1243
+ timeout := 10 * time .Millisecond
1244
+ nextNonce := 0
1245
+ for ; ; timeout *= 2 {
1246
+ if timeout <= 0 {
1247
+ // The only way we can feasibly hit this while the test is running is if
1248
+ // the request fails without actually waiting for the timeout to occur.
1249
+ t .Fatalf ("timeout overflow" )
1250
+ }
1251
+ if deadline , ok := t .Deadline (); ok && ! time .Now ().Add (timeout ).Before (deadline ) {
1252
+ t .Fatalf ("failed to produce expected timeout before test deadline" )
1253
+ }
1254
+ t .Logf ("attempting test with timeout %v" , timeout )
1255
+ cst .c .Timeout = timeout
1256
+
1257
+ mu .Lock ()
1258
+ nonce = fmt .Sprint (nextNonce )
1259
+ nextNonce ++
1260
+ sawSlowNonce = false
1261
+ mu .Unlock ()
1262
+ res , err := cst .c .Get (cst .ts .URL + "/?nonce=" + nonce )
1263
+ if err != nil {
1264
+ if strings .Contains (err .Error (), "Client.Timeout" ) {
1265
+ // Timed out before handler could respond.
1266
+ t .Logf ("timeout before response received" )
1267
+ continue
1268
+ }
1269
+ t .Fatal (err )
1238
1270
}
1239
- t .Fatal (err )
1240
- }
1241
1271
1242
- select {
1243
- case <- sawRoot :
1244
- // good.
1245
- default :
1246
- t .Fatal ("handler never got / request" )
1247
- }
1248
-
1249
- select {
1250
- case <- sawSlow :
1251
- // good.
1252
- default :
1253
- t .Fatal ("handler never got /slow request" )
1254
- }
1272
+ mu .Lock ()
1273
+ ok := sawSlowNonce
1274
+ mu .Unlock ()
1275
+ if ! ok {
1276
+ t .Fatal ("handler never got /slow request, but client returned response" )
1277
+ }
1255
1278
1256
- errc := make (chan error , 1 )
1257
- go func () {
1258
- _ , err := io .ReadAll (res .Body )
1259
- errc <- err
1279
+ _ , err = io .ReadAll (res .Body )
1260
1280
res .Body .Close ()
1261
- }()
1262
1281
1263
- const failTime = 5 * time .Second
1264
- select {
1265
- case err := <- errc :
1266
1282
if err == nil {
1267
1283
t .Fatal ("expected error from ReadAll" )
1268
1284
}
@@ -1275,8 +1291,8 @@ func testClientTimeout(t *testing.T, h2 bool) {
1275
1291
if got := ne .Error (); ! strings .Contains (got , "(Client.Timeout" ) {
1276
1292
t .Errorf ("error string = %q; missing timeout substring" , got )
1277
1293
}
1278
- case <- time . After ( failTime ):
1279
- t . Errorf ( "timeout after %v waiting for timeout of %v" , failTime , timeout )
1294
+
1295
+ break
1280
1296
}
1281
1297
}
1282
1298
0 commit comments