@@ -270,22 +270,29 @@ type ClientConn struct {
270
270
nextStreamID uint32
271
271
pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
272
272
pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
273
- bw * bufio.Writer
274
273
br * bufio.Reader
275
- fr * Framer
276
274
lastActive time.Time
277
275
lastIdle time.Time // time last idle
278
- // Settings from peer: (also guarded by mu )
276
+ // Settings from peer: (also guarded by wmu )
279
277
maxFrameSize uint32
280
278
maxConcurrentStreams uint32
281
279
peerMaxHeaderListSize uint64
282
280
initialWindowSize uint32
283
281
282
+ // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
283
+ // Write to reqHeaderMu to lock it, read from it to unlock.
284
+ // Lock reqmu BEFORE mu or wmu.
285
+ reqHeaderMu chan struct {}
286
+
287
+ // wmu is held while writing.
288
+ // Acquire BEFORE mu when holding both, to avoid blocking mu on network writes.
289
+ // Only acquire both at the same time when changing peer settings.
290
+ wmu sync.Mutex
291
+ bw * bufio.Writer
292
+ fr * Framer
293
+ werr error // first write error that has occurred
284
294
hbuf bytes.Buffer // HPACK encoder writes into this
285
295
henc * hpack.Encoder
286
-
287
- wmu sync.Mutex // held while writing; acquire AFTER mu if holding both
288
- werr error // first write error that has occurred
289
296
}
290
297
291
298
// clientStream is the state for a single HTTP/2 stream. One of these
@@ -404,10 +411,11 @@ func (cs *clientStream) abortRequestBodyWrite(err error) {
404
411
cc .mu .Lock ()
405
412
if cs .stopReqBody == nil {
406
413
cs .stopReqBody = err
407
- if cs .req .Body != nil {
408
- cs .req .Body .Close ()
409
- }
410
414
cc .cond .Broadcast ()
415
+ // Close the body after releasing the mutex, in case it blocks.
416
+ if body := cs .req .Body ; body != nil {
417
+ defer body .Close ()
418
+ }
411
419
}
412
420
cc .mu .Unlock ()
413
421
}
@@ -672,6 +680,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
672
680
singleUse : singleUse ,
673
681
wantSettingsAck : true ,
674
682
pings : make (map [[8 ]byte ]chan struct {}),
683
+ reqHeaderMu : make (chan struct {}, 1 ),
675
684
}
676
685
if d := t .idleConnTimeout (); d != 0 {
677
686
cc .idleTimeout = d
@@ -900,41 +909,48 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
900
909
901
910
func (cc * ClientConn ) sendGoAway () error {
902
911
cc .mu .Lock ()
903
- defer cc .mu .Unlock ()
904
- cc .wmu .Lock ()
905
- defer cc .wmu .Unlock ()
906
- if cc .closing {
912
+ closing := cc .closing
913
+ cc .closing = true
914
+ maxStreamID := cc .nextStreamID
915
+ cc .mu .Unlock ()
916
+ if closing {
907
917
// GOAWAY sent already
908
918
return nil
909
919
}
920
+
921
+ cc .wmu .Lock ()
922
+ defer cc .wmu .Unlock ()
910
923
// Send a graceful shutdown frame to server
911
- maxStreamID := cc .nextStreamID
912
924
if err := cc .fr .WriteGoAway (maxStreamID , ErrCodeNo , nil ); err != nil {
913
925
return err
914
926
}
915
927
if err := cc .bw .Flush (); err != nil {
916
928
return err
917
929
}
918
930
// Prevent new requests
919
- cc .closing = true
920
931
return nil
921
932
}
922
933
923
934
// closes the client connection immediately. In-flight requests are interrupted.
924
935
// err is sent to streams.
925
936
func (cc * ClientConn ) closeForError (err error ) error {
926
937
cc .mu .Lock ()
927
- defer cc .cond .Broadcast ()
928
- defer cc .mu .Unlock ()
929
- for id , cs := range cc .streams {
938
+ streams := cc .streams
939
+ cc .streams = nil
940
+ cc .closed = true
941
+ cc .mu .Unlock ()
942
+
943
+ for _ , cs := range streams {
930
944
select {
931
945
case cs .resc <- resAndError {err : err }:
932
946
default :
933
947
}
934
948
cs .bufPipe .CloseWithError (err )
935
- delete (cc .streams , id )
936
949
}
937
- cc .closed = true
950
+
951
+ cc .mu .Lock ()
952
+ defer cc .cond .Broadcast ()
953
+ defer cc .mu .Unlock ()
938
954
return cc .tconn .Close ()
939
955
}
940
956
@@ -1022,6 +1038,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
1022
1038
}
1023
1039
1024
1040
func (cc * ClientConn ) roundTrip (req * http.Request ) (res * http.Response , gotErrAfterReqBodyWrite bool , err error ) {
1041
+ ctx := req .Context ()
1025
1042
if err := checkConnHeaders (req ); err != nil {
1026
1043
return nil , false , err
1027
1044
}
@@ -1035,6 +1052,26 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
1035
1052
}
1036
1053
hasTrailers := trailers != ""
1037
1054
1055
+ // Acquire the new-request lock by writing to reqHeaderMu.
1056
+ // This lock guards the critical section covering allocating a new stream ID
1057
+ // (requires mu) and creating the stream (requires wmu).
1058
+ if cc .reqHeaderMu == nil {
1059
+ panic ("RoundTrip on initialized ClientConn" ) // for tests
1060
+ }
1061
+ select {
1062
+ case cc .reqHeaderMu <- struct {}{}:
1063
+ case <- req .Cancel :
1064
+ return nil , false , errRequestCanceled
1065
+ case <- ctx .Done ():
1066
+ return nil , false , ctx .Err ()
1067
+ }
1068
+ reqHeaderMuNeedsUnlock := true
1069
+ defer func () {
1070
+ if reqHeaderMuNeedsUnlock {
1071
+ <- cc .reqHeaderMu
1072
+ }
1073
+ }()
1074
+
1038
1075
cc .mu .Lock ()
1039
1076
if err := cc .awaitOpenSlotForRequest (req ); err != nil {
1040
1077
cc .mu .Unlock ()
@@ -1066,22 +1103,24 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
1066
1103
requestedGzip = true
1067
1104
}
1068
1105
1106
+ cs := cc .newStream ()
1107
+ cs .req = req
1108
+ cs .trace = httptrace .ContextClientTrace (req .Context ())
1109
+ cs .requestedGzip = requestedGzip
1110
+ bodyWriter := cc .t .getBodyWriterState (cs , body )
1111
+ cs .on100 = bodyWriter .on100
1112
+ cc .mu .Unlock ()
1113
+
1069
1114
// we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is
1070
1115
// sent by writeRequestBody below, along with any Trailers,
1071
1116
// again in form HEADERS{1}, CONTINUATION{0,})
1117
+ cc .wmu .Lock ()
1072
1118
hdrs , err := cc .encodeHeaders (req , requestedGzip , trailers , contentLen )
1073
1119
if err != nil {
1074
- cc .mu .Unlock ()
1120
+ cc .wmu .Unlock ()
1075
1121
return nil , false , err
1076
1122
}
1077
1123
1078
- cs := cc .newStream ()
1079
- cs .req = req
1080
- cs .trace = httptrace .ContextClientTrace (req .Context ())
1081
- cs .requestedGzip = requestedGzip
1082
- bodyWriter := cc .t .getBodyWriterState (cs , body )
1083
- cs .on100 = bodyWriter .on100
1084
-
1085
1124
defer func () {
1086
1125
cc .wmu .Lock ()
1087
1126
werr := cc .werr
@@ -1091,24 +1130,24 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
1091
1130
}
1092
1131
}()
1093
1132
1094
- cc .wmu .Lock ()
1095
1133
endStream := ! hasBody && ! hasTrailers
1096
- werr : = cc .writeHeaders (cs .ID , endStream , int (cc .maxFrameSize ), hdrs )
1134
+ err = cc .writeHeaders (cs .ID , endStream , int (cc .maxFrameSize ), hdrs )
1097
1135
cc .wmu .Unlock ()
1136
+ <- cc .reqHeaderMu // release the new-request lock
1137
+ reqHeaderMuNeedsUnlock = false
1098
1138
traceWroteHeaders (cs .trace )
1099
- cc .mu .Unlock ()
1100
1139
1101
- if werr != nil {
1140
+ if err != nil {
1102
1141
if hasBody {
1103
1142
bodyWriter .cancel ()
1104
1143
}
1105
1144
cc .forgetStreamID (cs .ID )
1106
1145
// Don't bother sending a RST_STREAM (our write already failed;
1107
1146
// no need to keep writing)
1108
- traceWroteRequest (cs .trace , werr )
1147
+ traceWroteRequest (cs .trace , err )
1109
1148
// TODO(dneil): An error occurred while writing the headers.
1110
1149
// Should we return an error indicating that this request can be retried?
1111
- return nil , false , werr
1150
+ return nil , false , err
1112
1151
}
1113
1152
1114
1153
var respHeaderTimer <- chan time.Time
@@ -1125,7 +1164,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
1125
1164
1126
1165
readLoopResCh := cs .resc
1127
1166
bodyWritten := false
1128
- ctx := req .Context ()
1129
1167
1130
1168
handleReadLoopResponse := func (re resAndError ) (* http.Response , bool , error ) {
1131
1169
res := re .res
@@ -1427,19 +1465,17 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
1427
1465
return nil
1428
1466
}
1429
1467
1468
+ cc .wmu .Lock ()
1430
1469
var trls []byte
1431
1470
if hasTrailers {
1432
- cc .mu .Lock ()
1433
1471
trls , err = cc .encodeTrailers (req )
1434
- cc .mu .Unlock ()
1435
1472
if err != nil {
1473
+ cc .wmu .Unlock ()
1436
1474
cc .writeStreamReset (cs .ID , ErrCodeInternal , err )
1437
1475
cc .forgetStreamID (cs .ID )
1438
1476
return err
1439
1477
}
1440
1478
}
1441
-
1442
- cc .wmu .Lock ()
1443
1479
defer cc .wmu .Unlock ()
1444
1480
1445
1481
// Two ways to send END_STREAM: either with trailers, or
@@ -1489,7 +1525,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
1489
1525
}
1490
1526
}
1491
1527
1492
- // requires cc.mu be held.
1528
+ // requires cc.wmu be held.
1493
1529
func (cc * ClientConn ) encodeHeaders (req * http.Request , addGzipHeader bool , trailers string , contentLength int64 ) ([]byte , error ) {
1494
1530
cc .hbuf .Reset ()
1495
1531
@@ -1677,7 +1713,7 @@ func shouldSendReqContentLength(method string, contentLength int64) bool {
1677
1713
}
1678
1714
}
1679
1715
1680
- // requires cc.mu be held.
1716
+ // requires cc.wmu be held.
1681
1717
func (cc * ClientConn ) encodeTrailers (req * http.Request ) ([]byte , error ) {
1682
1718
cc .hbuf .Reset ()
1683
1719
@@ -1826,15 +1862,19 @@ func (rl *clientConnReadLoop) cleanup() {
1826
1862
} else if err == io .EOF {
1827
1863
err = io .ErrUnexpectedEOF
1828
1864
}
1829
- for _ , cs := range cc .streams {
1865
+ cc .closed = true
1866
+ streams := cc .streams
1867
+ cc .streams = nil
1868
+ cc .mu .Unlock ()
1869
+ for _ , cs := range streams {
1830
1870
cs .bufPipe .CloseWithError (err ) // no-op if already closed
1831
1871
select {
1832
1872
case cs .resc <- resAndError {err : err }:
1833
1873
default :
1834
1874
}
1835
1875
close (cs .done )
1836
1876
}
1837
- cc .closed = true
1877
+ cc .mu . Lock ()
1838
1878
cc .cond .Broadcast ()
1839
1879
cc .mu .Unlock ()
1840
1880
}
@@ -2192,8 +2232,6 @@ func (b transportResponseBody) Read(p []byte) (n int, err error) {
2192
2232
}
2193
2233
2194
2234
cc .mu .Lock ()
2195
- defer cc .mu .Unlock ()
2196
-
2197
2235
var connAdd , streamAdd int32
2198
2236
// Check the conn-level first, before the stream-level.
2199
2237
if v := cc .inflow .available (); v < transportDefaultConnFlow / 2 {
@@ -2210,6 +2248,8 @@ func (b transportResponseBody) Read(p []byte) (n int, err error) {
2210
2248
cs .inflow .add (streamAdd )
2211
2249
}
2212
2250
}
2251
+ cc .mu .Unlock ()
2252
+
2213
2253
if connAdd != 0 || streamAdd != 0 {
2214
2254
cc .wmu .Lock ()
2215
2255
defer cc .wmu .Unlock ()
@@ -2235,19 +2275,25 @@ func (b transportResponseBody) Close() error {
2235
2275
2236
2276
if unread > 0 || ! serverSentStreamEnd {
2237
2277
cc .mu .Lock ()
2238
- cc .wmu .Lock ()
2239
2278
if ! serverSentStreamEnd {
2240
- cc .fr .WriteRSTStream (cs .ID , ErrCodeCancel )
2241
2279
cs .didReset = true
2242
2280
}
2243
2281
// Return connection-level flow control.
2244
2282
if unread > 0 {
2245
2283
cc .inflow .add (int32 (unread ))
2284
+ }
2285
+ cc .mu .Unlock ()
2286
+
2287
+ cc .wmu .Lock ()
2288
+ if ! serverSentStreamEnd {
2289
+ cc .fr .WriteRSTStream (cs .ID , ErrCodeCancel )
2290
+ }
2291
+ // Return connection-level flow control.
2292
+ if unread > 0 {
2246
2293
cc .fr .WriteWindowUpdate (0 , uint32 (unread ))
2247
2294
}
2248
2295
cc .bw .Flush ()
2249
2296
cc .wmu .Unlock ()
2250
- cc .mu .Unlock ()
2251
2297
}
2252
2298
2253
2299
cs .bufPipe .BreakWithError (errClosedResponseBody )
@@ -2325,6 +2371,10 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
2325
2371
}
2326
2372
if refund > 0 {
2327
2373
cc .inflow .add (int32 (refund ))
2374
+ }
2375
+ cc .mu .Unlock ()
2376
+
2377
+ if refund > 0 {
2328
2378
cc .wmu .Lock ()
2329
2379
cc .fr .WriteWindowUpdate (0 , uint32 (refund ))
2330
2380
if ! didReset {
@@ -2334,7 +2384,6 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
2334
2384
cc .bw .Flush ()
2335
2385
cc .wmu .Unlock ()
2336
2386
}
2337
- cc .mu .Unlock ()
2338
2387
2339
2388
if len (data ) > 0 && ! didReset {
2340
2389
if _ , err := cs .bufPipe .Write (data ); err != nil {
@@ -2399,6 +2448,23 @@ func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error {
2399
2448
}
2400
2449
2401
2450
func (rl * clientConnReadLoop ) processSettings (f * SettingsFrame ) error {
2451
+ cc := rl .cc
2452
+ // Locking both mu and wmu here allows frame encoding to read settings with only wmu held.
2453
+ // Acquiring wmu when f.IsAck() is unnecessary, but convenient and mostly harmless.
2454
+ cc .wmu .Lock ()
2455
+ defer cc .wmu .Unlock ()
2456
+
2457
+ if err := rl .processSettingsNoWrite (f ); err != nil {
2458
+ return err
2459
+ }
2460
+ if ! f .IsAck () {
2461
+ cc .fr .WriteSettingsAck ()
2462
+ cc .bw .Flush ()
2463
+ }
2464
+ return nil
2465
+ }
2466
+
2467
+ func (rl * clientConnReadLoop ) processSettingsNoWrite (f * SettingsFrame ) error {
2402
2468
cc := rl .cc
2403
2469
cc .mu .Lock ()
2404
2470
defer cc .mu .Unlock ()
@@ -2461,12 +2527,7 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
2461
2527
cc .seenSettings = true
2462
2528
}
2463
2529
2464
- cc .wmu .Lock ()
2465
- defer cc .wmu .Unlock ()
2466
-
2467
- cc .fr .WriteSettingsAck ()
2468
- cc .bw .Flush ()
2469
- return cc .werr
2530
+ return nil
2470
2531
}
2471
2532
2472
2533
func (rl * clientConnReadLoop ) processWindowUpdate (f * WindowUpdateFrame ) error {
0 commit comments