@@ -953,7 +953,7 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
953
953
// Loop over the waiting list until we find a w that isn't done already, and hand it pconn.
954
954
for q .len () > 0 {
955
955
w := q .popFront ()
956
- if w .tryDeliver (pconn , nil ) {
956
+ if w .tryDeliver (pconn , nil , time. Time {} ) {
957
957
done = true
958
958
break
959
959
}
@@ -965,7 +965,7 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
965
965
// list unconditionally, for any future clients too.
966
966
for q .len () > 0 {
967
967
w := q .popFront ()
968
- w .tryDeliver (pconn , nil )
968
+ w .tryDeliver (pconn , nil , time. Time {} )
969
969
}
970
970
}
971
971
if q .len () == 0 {
@@ -1069,7 +1069,7 @@ func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) {
1069
1069
list = list [:len (list )- 1 ]
1070
1070
continue
1071
1071
}
1072
- delivered = w .tryDeliver (pconn , nil )
1072
+ delivered = w .tryDeliver (pconn , nil , pconn . idleAt )
1073
1073
if delivered {
1074
1074
if pconn .alt != nil {
1075
1075
// HTTP/2: multiple clients can share pconn.
@@ -1203,69 +1203,77 @@ func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, e
1203
1203
// These three options are racing against each other and use
1204
1204
// wantConn to coordinate and agree about the winning outcome.
1205
1205
type wantConn struct {
1206
- cm connectMethod
1207
- key connectMethodKey // cm.key()
1208
- ready chan struct {} // closed when pc, err pair is delivered
1206
+ cm connectMethod
1207
+ key connectMethodKey // cm.key()
1209
1208
1210
1209
// hooks for testing to know when dials are done
1211
1210
// beforeDial is called in the getConn goroutine when the dial is queued.
1212
1211
// afterDial is called when the dial is completed or canceled.
1213
1212
beforeDial func ()
1214
1213
afterDial func ()
1215
1214
1216
- mu sync.Mutex // protects ctx, pc, err, close(ready)
1217
- ctx context.Context // context for dial, cleared after delivered or canceled
1218
- pc * persistConn
1219
- err error
1215
+ mu sync.Mutex // protects ctx, done and sending of the result
1216
+ ctx context.Context // context for dial, cleared after delivered or canceled
1217
+ done bool // true after delivered or canceled
1218
+ result chan connOrError // channel to deliver connection or error
1219
+ }
1220
+
1221
+ type connOrError struct {
1222
+ pc * persistConn
1223
+ err error
1224
+ idleAt time.Time
1220
1225
}
1221
1226
1222
1227
// waiting reports whether w is still waiting for an answer (connection or error).
1223
1228
func (w * wantConn ) waiting () bool {
1224
- select {
1225
- case <- w .ready :
1226
- return false
1227
- default :
1228
- return true
1229
- }
1229
+ w .mu .Lock ()
1230
+ defer w .mu .Unlock ()
1231
+
1232
+ return ! w .done
1230
1233
}
1231
1234
1232
1235
// getCtxForDial returns context for dial or nil if connection was delivered or canceled.
1233
1236
func (w * wantConn ) getCtxForDial () context.Context {
1234
1237
w .mu .Lock ()
1235
1238
defer w .mu .Unlock ()
1239
+
1236
1240
return w .ctx
1237
1241
}
1238
1242
1239
1243
// tryDeliver attempts to deliver pc, err to w and reports whether it succeeded.
1240
- func (w * wantConn ) tryDeliver (pc * persistConn , err error ) bool {
1244
+ func (w * wantConn ) tryDeliver (pc * persistConn , err error , idleAt time. Time ) bool {
1241
1245
w .mu .Lock ()
1242
1246
defer w .mu .Unlock ()
1243
1247
1244
- if w .pc != nil || w . err != nil {
1248
+ if w .done {
1245
1249
return false
1246
1250
}
1247
-
1248
- w .ctx = nil
1249
- w .pc = pc
1250
- w .err = err
1251
- if w .pc == nil && w .err == nil {
1251
+ if (pc == nil ) == (err == nil ) {
1252
1252
panic ("net/http: internal error: misuse of tryDeliver" )
1253
1253
}
1254
- close (w .ready )
1254
+ w .ctx = nil
1255
+ w .done = true
1256
+
1257
+ w .result <- connOrError {pc : pc , err : err , idleAt : idleAt }
1258
+ close (w .result )
1259
+
1255
1260
return true
1256
1261
}
1257
1262
1258
1263
// cancel marks w as no longer wanting a result (for example, due to cancellation).
1259
1264
// If a connection has been delivered already, cancel returns it with t.putOrCloseIdleConn.
1260
1265
func (w * wantConn ) cancel (t * Transport , err error ) {
1261
1266
w .mu .Lock ()
1262
- if w .pc == nil && w .err == nil {
1263
- close (w .ready ) // catch misbehavior in future delivery
1267
+ var pc * persistConn
1268
+ if w .done {
1269
+ if r , ok := <- w .result ; ok {
1270
+ pc = r .pc
1271
+ }
1272
+ } else {
1273
+ close (w .result )
1264
1274
}
1265
- pc := w .pc
1266
1275
w .ctx = nil
1267
- w .pc = nil
1268
- w .err = err
1276
+ w .done = true
1269
1277
w .mu .Unlock ()
1270
1278
1271
1279
if pc != nil {
@@ -1355,7 +1363,7 @@ func (t *Transport) customDialTLS(ctx context.Context, network, addr string) (co
1355
1363
// specified in the connectMethod. This includes doing a proxy CONNECT
1356
1364
// and/or setting up TLS. If this doesn't return an error, the persistConn
1357
1365
// is ready to write requests to.
1358
- func (t * Transport ) getConn (treq * transportRequest , cm connectMethod ) (pc * persistConn , err error ) {
1366
+ func (t * Transport ) getConn (treq * transportRequest , cm connectMethod ) (_ * persistConn , err error ) {
1359
1367
req := treq .Request
1360
1368
trace := treq .trace
1361
1369
ctx := req .Context ()
@@ -1367,7 +1375,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi
1367
1375
cm : cm ,
1368
1376
key : cm .key (),
1369
1377
ctx : ctx ,
1370
- ready : make (chan struct {} , 1 ),
1378
+ result : make (chan connOrError , 1 ),
1371
1379
beforeDial : testHookPrePendingDial ,
1372
1380
afterDial : testHookPostPendingDial ,
1373
1381
}
@@ -1377,38 +1385,41 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi
1377
1385
}
1378
1386
}()
1379
1387
1388
+ var cancelc chan error
1389
+
1380
1390
// Queue for idle connection.
1381
1391
if delivered := t .queueForIdleConn (w ); delivered {
1382
- pc := w .pc
1383
- // Trace only for HTTP/1.
1384
- // HTTP/2 calls trace.GotConn itself.
1385
- if pc .alt == nil && trace != nil && trace .GotConn != nil {
1386
- trace .GotConn (pc .gotIdleConnTrace (pc .idleAt ))
1387
- }
1388
1392
// set request canceler to some non-nil function so we
1389
1393
// can detect whether it was cleared between now and when
1390
1394
// we enter roundTrip
1391
1395
t .setReqCanceler (treq .cancelKey , func (error ) {})
1392
- return pc , nil
1393
- }
1394
-
1395
- cancelc := make (chan error , 1 )
1396
- t .setReqCanceler (treq .cancelKey , func (err error ) { cancelc <- err })
1396
+ } else {
1397
+ cancelc = make (chan error , 1 )
1398
+ t .setReqCanceler (treq .cancelKey , func (err error ) { cancelc <- err })
1397
1399
1398
- // Queue for permission to dial.
1399
- t .queueForDial (w )
1400
+ // Queue for permission to dial.
1401
+ t .queueForDial (w )
1402
+ }
1400
1403
1401
1404
// Wait for completion or cancellation.
1402
1405
select {
1403
- case <- w .ready :
1406
+ case r := <- w .result :
1404
1407
// Trace success but only for HTTP/1.
1405
1408
// HTTP/2 calls trace.GotConn itself.
1406
- if w .pc != nil && w .pc .alt == nil && trace != nil && trace .GotConn != nil {
1407
- trace .GotConn (httptrace.GotConnInfo {Conn : w .pc .conn , Reused : w .pc .isReused ()})
1409
+ if r .pc != nil && r .pc .alt == nil && trace != nil && trace .GotConn != nil {
1410
+ info := httptrace.GotConnInfo {
1411
+ Conn : r .pc .conn ,
1412
+ Reused : r .pc .isReused (),
1413
+ }
1414
+ if ! r .idleAt .IsZero () {
1415
+ info .WasIdle = true
1416
+ info .IdleTime = time .Since (r .idleAt )
1417
+ }
1418
+ trace .GotConn (info )
1408
1419
}
1409
- if w .err != nil {
1420
+ if r .err != nil {
1410
1421
// If the request has been canceled, that's probably
1411
- // what caused w .err; if so, prefer to return the
1422
+ // what caused r .err; if so, prefer to return the
1412
1423
// cancellation error (see golang.org/issue/16049).
1413
1424
select {
1414
1425
case <- req .Cancel :
@@ -1424,7 +1435,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi
1424
1435
// return below
1425
1436
}
1426
1437
}
1427
- return w .pc , w .err
1438
+ return r .pc , r .err
1428
1439
case <- req .Cancel :
1429
1440
return nil , errRequestCanceledConn
1430
1441
case <- req .Context ().Done ():
@@ -1478,7 +1489,7 @@ func (t *Transport) dialConnFor(w *wantConn) {
1478
1489
}
1479
1490
1480
1491
pc , err := t .dialConn (ctx , w .cm )
1481
- delivered := w .tryDeliver (pc , err )
1492
+ delivered := w .tryDeliver (pc , err , time. Time {} )
1482
1493
if err == nil && (! delivered || pc .alt != nil ) {
1483
1494
// pconn was not passed to w,
1484
1495
// or it is HTTP/2 and can be shared.
@@ -1996,18 +2007,6 @@ func (pc *persistConn) isReused() bool {
1996
2007
return r
1997
2008
}
1998
2009
1999
- func (pc * persistConn ) gotIdleConnTrace (idleAt time.Time ) (t httptrace.GotConnInfo ) {
2000
- pc .mu .Lock ()
2001
- defer pc .mu .Unlock ()
2002
- t .Reused = pc .reused
2003
- t .Conn = pc .conn
2004
- t .WasIdle = true
2005
- if ! idleAt .IsZero () {
2006
- t .IdleTime = time .Since (idleAt )
2007
- }
2008
- return
2009
- }
2010
-
2011
2010
func (pc * persistConn ) cancelRequest (err error ) {
2012
2011
pc .mu .Lock ()
2013
2012
defer pc .mu .Unlock ()
0 commit comments