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