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