@@ -264,9 +264,8 @@ type ClientConn struct {
264
264
peerMaxHeaderListSize uint64
265
265
initialWindowSize uint32
266
266
267
- hbuf bytes.Buffer // HPACK encoder writes into this
268
- henc * hpack.Encoder
269
- freeBuf [][]byte
267
+ hbuf bytes.Buffer // HPACK encoder writes into this
268
+ henc * hpack.Encoder
270
269
271
270
wmu sync.Mutex // held while writing; acquire AFTER mu if holding both
272
271
werr error // first write error that has occurred
@@ -913,46 +912,6 @@ func (cc *ClientConn) closeForLostPing() error {
913
912
return cc .closeForError (err )
914
913
}
915
914
916
- const maxAllocFrameSize = 512 << 10
917
-
918
- // frameBuffer returns a scratch buffer suitable for writing DATA frames.
919
- // They're capped at the min of the peer's max frame size or 512KB
920
- // (kinda arbitrarily), but definitely capped so we don't allocate 4GB
921
- // bufers.
922
- func (cc * ClientConn ) frameScratchBuffer () []byte {
923
- cc .mu .Lock ()
924
- size := cc .maxFrameSize
925
- if size > maxAllocFrameSize {
926
- size = maxAllocFrameSize
927
- }
928
- for i , buf := range cc .freeBuf {
929
- if len (buf ) >= int (size ) {
930
- cc .freeBuf [i ] = nil
931
- cc .mu .Unlock ()
932
- return buf [:size ]
933
- }
934
- }
935
- cc .mu .Unlock ()
936
- return make ([]byte , size )
937
- }
938
-
939
- func (cc * ClientConn ) putFrameScratchBuffer (buf []byte ) {
940
- cc .mu .Lock ()
941
- defer cc .mu .Unlock ()
942
- const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate.
943
- if len (cc .freeBuf ) < maxBufs {
944
- cc .freeBuf = append (cc .freeBuf , buf )
945
- return
946
- }
947
- for i , old := range cc .freeBuf {
948
- if old == nil {
949
- cc .freeBuf [i ] = buf
950
- return
951
- }
952
- }
953
- // forget about it.
954
- }
955
-
956
915
// errRequestCanceled is a copy of net/http's errRequestCanceled because it's not
957
916
// exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests.
958
917
var errRequestCanceled = errors .New ("net/http: request canceled" )
@@ -1295,11 +1254,35 @@ var (
1295
1254
errReqBodyTooLong = errors .New ("http2: request body larger than specified content length" )
1296
1255
)
1297
1256
1257
+ // frameScratchBufferLen returns the length of a buffer to use for
1258
+ // outgoing request bodies to read/write to/from.
1259
+ //
1260
+ // It returns max(1, min(peer's advertised max frame size,
1261
+ // Request.ContentLength+1, 512KB)).
1262
+ func (cs * clientStream ) frameScratchBufferLen (maxFrameSize int ) int {
1263
+ const max = 512 << 10
1264
+ n := int64 (maxFrameSize )
1265
+ if n > max {
1266
+ n = max
1267
+ }
1268
+ if cl := actualContentLength (cs .req ); cl != - 1 && cl + 1 < n {
1269
+ // Add an extra byte past the declared content-length to
1270
+ // give the caller's Request.Body io.Reader a chance to
1271
+ // give us more bytes than they declared, so we can catch it
1272
+ // early.
1273
+ n = cl + 1
1274
+ }
1275
+ if n < 1 {
1276
+ return 1
1277
+ }
1278
+ return int (n ) // doesn't truncate; max is 512K
1279
+ }
1280
+
1281
+ var bufPool sync.Pool // of *[]byte
1282
+
1298
1283
func (cs * clientStream ) writeRequestBody (body io.Reader , bodyCloser io.Closer ) (err error ) {
1299
1284
cc := cs .cc
1300
1285
sentEnd := false // whether we sent the final DATA frame w/ END_STREAM
1301
- buf := cc .frameScratchBuffer ()
1302
- defer cc .putFrameScratchBuffer (buf )
1303
1286
1304
1287
defer func () {
1305
1288
traceWroteRequest (cs .trace , err )
@@ -1318,9 +1301,24 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
1318
1301
remainLen := actualContentLength (req )
1319
1302
hasContentLen := remainLen != - 1
1320
1303
1304
+ cc .mu .Lock ()
1305
+ maxFrameSize := int (cc .maxFrameSize )
1306
+ cc .mu .Unlock ()
1307
+
1308
+ // Scratch buffer for reading into & writing from.
1309
+ scratchLen := cs .frameScratchBufferLen (maxFrameSize )
1310
+ var buf []byte
1311
+ if bp , ok := bufPool .Get ().(* []byte ); ok && len (* bp ) >= scratchLen {
1312
+ defer bufPool .Put (bp )
1313
+ buf = * bp
1314
+ } else {
1315
+ buf = make ([]byte , scratchLen )
1316
+ defer bufPool .Put (& buf )
1317
+ }
1318
+
1321
1319
var sawEOF bool
1322
1320
for ! sawEOF {
1323
- n , err := body .Read (buf [:len (buf )- 1 ])
1321
+ n , err := body .Read (buf [:len (buf )])
1324
1322
if hasContentLen {
1325
1323
remainLen -= int64 (n )
1326
1324
if remainLen == 0 && err == nil {
@@ -1331,8 +1329,9 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
1331
1329
// to send the END_STREAM bit early, double-check that we're actually
1332
1330
// at EOF. Subsequent reads should return (0, EOF) at this point.
1333
1331
// If either value is different, we return an error in one of two ways below.
1332
+ var scratch [1 ]byte
1334
1333
var n1 int
1335
- n1 , err = body .Read (buf [ n :])
1334
+ n1 , err = body .Read (scratch [ :])
1336
1335
remainLen -= int64 (n1 )
1337
1336
}
1338
1337
if remainLen < 0 {
@@ -1402,10 +1401,6 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
1402
1401
}
1403
1402
}
1404
1403
1405
- cc .mu .Lock ()
1406
- maxFrameSize := int (cc .maxFrameSize )
1407
- cc .mu .Unlock ()
1408
-
1409
1404
cc .wmu .Lock ()
1410
1405
defer cc .wmu .Unlock ()
1411
1406
0 commit comments