@@ -25,6 +25,7 @@ import (
25
25
"io"
26
26
"io/ioutil"
27
27
"log"
28
+ mrand "math/rand"
28
29
"net"
29
30
. "net/http"
30
31
"net/http/httptest"
@@ -6284,3 +6285,101 @@ func TestTransportRejectsSignInContentLength(t *testing.T) {
6284
6285
t .Fatalf ("Error mismatch\n Got: %q\n Wanted substring: %q" , got , want )
6285
6286
}
6286
6287
}
6288
+
6289
+ // dumpConn is a net.Conn which writes to Writer and reads from Reader
6290
+ type dumpConn struct {
6291
+ io.Writer
6292
+ io.Reader
6293
+ }
6294
+
6295
+ func (c * dumpConn ) Close () error { return nil }
6296
+ func (c * dumpConn ) LocalAddr () net.Addr { return nil }
6297
+ func (c * dumpConn ) RemoteAddr () net.Addr { return nil }
6298
+ func (c * dumpConn ) SetDeadline (t time.Time ) error { return nil }
6299
+ func (c * dumpConn ) SetReadDeadline (t time.Time ) error { return nil }
6300
+ func (c * dumpConn ) SetWriteDeadline (t time.Time ) error { return nil }
6301
+
6302
+ // delegateReader is a reader that delegates to another reader,
6303
+ // once it arrives on a channel.
6304
+ type delegateReader struct {
6305
+ c chan io.Reader
6306
+ r io.Reader // nil until received from c
6307
+ }
6308
+
6309
+ func (r * delegateReader ) Read (p []byte ) (int , error ) {
6310
+ if r .r == nil {
6311
+ var ok bool
6312
+ if r .r , ok = <- r .c ; ! ok {
6313
+ return 0 , errors .New ("delegate closed" )
6314
+ }
6315
+ }
6316
+ return r .r .Read (p )
6317
+ }
6318
+
6319
+ func testTransportRace (req * Request ) {
6320
+ save := req .Body
6321
+ pr , pw := io .Pipe ()
6322
+ defer pr .Close ()
6323
+ defer pw .Close ()
6324
+ dr := & delegateReader {c : make (chan io.Reader )}
6325
+
6326
+ t := & Transport {
6327
+ Dial : func (net , addr string ) (net.Conn , error ) {
6328
+ return & dumpConn {pw , dr }, nil
6329
+ },
6330
+ }
6331
+ defer t .CloseIdleConnections ()
6332
+
6333
+ quitReadCh := make (chan struct {})
6334
+ // Wait for the request before replying with a dummy response:
6335
+ go func () {
6336
+ defer close (quitReadCh )
6337
+
6338
+ req , err := ReadRequest (bufio .NewReader (pr ))
6339
+ if err == nil {
6340
+ // Ensure all the body is read; otherwise
6341
+ // we'll get a partial dump.
6342
+ io .Copy (ioutil .Discard , req .Body )
6343
+ req .Body .Close ()
6344
+ }
6345
+ select {
6346
+ case dr .c <- strings .NewReader ("HTTP/1.1 204 No Content\r \n Connection: close\r \n \r \n " ):
6347
+ case quitReadCh <- struct {}{}:
6348
+ // Ensure delegate is closed so Read doesn't block forever.
6349
+ close (dr .c )
6350
+ }
6351
+ }()
6352
+
6353
+ t .RoundTrip (req )
6354
+
6355
+ // Ensure the reader returns before we reset req.Body to prevent
6356
+ // a data race on req.Body.
6357
+ pw .Close ()
6358
+ <- quitReadCh
6359
+
6360
+ req .Body = save
6361
+ }
6362
+
6363
+ // Issue 37669
6364
+ // Test that a cancellation doesn't result in a data race due to the writeLoop
6365
+ // goroutine being left running, if the caller mutates the processed Request
6366
+ // upon completion.
6367
+ func TestErrorWriteLoopRace (t * testing.T ) {
6368
+ if testing .Short () {
6369
+ return
6370
+ }
6371
+ t .Parallel ()
6372
+ for i := 0 ; i < 1000 ; i ++ {
6373
+ delay := time .Duration (mrand .Intn (5 )) * time .Millisecond
6374
+ ctx , cancel := context .WithTimeout (context .Background (), delay )
6375
+ defer cancel ()
6376
+
6377
+ r := bytes .NewBuffer (make ([]byte , 10000 ))
6378
+ req , err := NewRequestWithContext (ctx , MethodPost , "http://example.com" , r )
6379
+ if err != nil {
6380
+ t .Fatal (err )
6381
+ }
6382
+
6383
+ testTransportRace (req )
6384
+ }
6385
+ }
0 commit comments