@@ -39,14 +39,17 @@ import (
3939)
4040
4141const (
42- // transportDefaultConnFlow is how many connection-level flow control
42+ // maxWriteFrameSize is the maximum possible frame size used to write to server.
43+ maxWriteFrameSize = 512 << 10
44+
45+ // defaultTransportDefaultConnFlow is how many connection-level flow control
4346 // tokens we give the server at start-up, past the default 64k.
44- transportDefaultConnFlow = 1 << 30
47+ defaultTransportDefaultConnFlow = 1 << 30
4548
46- // transportDefaultStreamFlow is how many stream-level flow
49+ // defaultTransportDefaultStreamFlow is how many stream-level flow
4750 // control tokens we announce to the peer, and how many bytes
4851 // we buffer per stream.
49- transportDefaultStreamFlow = 4 << 20
52+ defaultTransportDefaultStreamFlow = 4 << 20
5053
5154 defaultUserAgent = "Go-http-client/2.0"
5255
@@ -124,6 +127,21 @@ type Transport struct {
124127 // Values are bounded in the range 16k to 16M.
125128 MaxReadFrameSize uint32
126129
130+ // MaxWriteFrameSize is the maximum frame size that we can a client
131+ // connection can send to a server, even if server advertises a higher value.
132+ // If 0, then a default value is used.
133+ MaxWriteFrameSize uint32
134+
135+ // MaxDownloadBufferPerConnection is the maximum per connection buffer size,
136+ // used for receiving data from the server.
137+ // If 0, then a default value is used.
138+ MaxDownloadBufferPerConnection uint32
139+
140+ // MaxDownloadBufferPerStream is the maximum buffer to use for inflow data sent
141+ // by the server.
142+ // If 0, then a default value is used.
143+ MaxDownloadBufferPerStream uint32
144+
127145 // MaxDecoderHeaderTableSize optionally specifies the http2
128146 // SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
129147 // informs the remote endpoint of the maximum size of the header compression
@@ -304,6 +322,9 @@ type ClientConn struct {
304322 idleTimeout time.Duration // or 0 for never
305323 idleTimer * time.Timer
306324
325+ maxWriteFrameSize uint32
326+ maxDownloadBufferPerStream uint32
327+
307328 mu sync.Mutex // guards following
308329 cond * sync.Cond // hold mu; broadcast on flow/closed changes
309330 flow outflow // our conn-level flow control quota (cs.outflow is per stream)
@@ -731,25 +752,55 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
731752 return initialHeaderTableSize
732753}
733754
755+ func (t * Transport ) maxDownloadBufferPerConnection () uint32 {
756+ maxWindow := uint32 ((2 << 31 ) - 1 - initialWindowSize )
757+
758+ if v := t .MaxDownloadBufferPerConnection ; v >= initialWindowSize {
759+ if v > maxWindow {
760+ return maxWindow
761+ } else {
762+ return v
763+ }
764+ }
765+
766+ return defaultTransportDefaultConnFlow
767+ }
768+
769+ func (t * Transport ) maxDownloadBufferPerStream () uint32 {
770+ if v := t .MaxDownloadBufferPerStream ; v > 0 {
771+ return v
772+ }
773+ return defaultTransportDefaultStreamFlow
774+ }
775+
776+ func (t * Transport ) maxWriteFrameSize () uint32 {
777+ if v := t .MaxWriteFrameSize ; v > 0 && v <= maxWriteFrameSize {
778+ return v
779+ }
780+ return maxWriteFrameSize
781+ }
782+
734783func (t * Transport ) NewClientConn (c net.Conn ) (* ClientConn , error ) {
735784 return t .newClientConn (c , t .disableKeepAlives ())
736785}
737786
738787func (t * Transport ) newClientConn (c net.Conn , singleUse bool ) (* ClientConn , error ) {
739788 cc := & ClientConn {
740- t : t ,
741- tconn : c ,
742- readerDone : make (chan struct {}),
743- nextStreamID : 1 ,
744- maxFrameSize : 16 << 10 , // spec default
745- initialWindowSize : 65535 , // spec default
746- maxConcurrentStreams : initialMaxConcurrentStreams , // "infinite", per spec. Use a smaller value until we have received server settings.
747- peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
748- streams : make (map [uint32 ]* clientStream ),
749- singleUse : singleUse ,
750- wantSettingsAck : true ,
751- pings : make (map [[8 ]byte ]chan struct {}),
752- reqHeaderMu : make (chan struct {}, 1 ),
789+ t : t ,
790+ tconn : c ,
791+ readerDone : make (chan struct {}),
792+ nextStreamID : 1 ,
793+ maxFrameSize : 16 << 10 , // spec default
794+ initialWindowSize : 65535 , // spec default
795+ maxConcurrentStreams : initialMaxConcurrentStreams , // "infinite", per spec. Use a smaller value until we have received server settings.
796+ peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
797+ maxWriteFrameSize : t .maxWriteFrameSize (),
798+ maxDownloadBufferPerStream : t .maxDownloadBufferPerStream (),
799+ streams : make (map [uint32 ]* clientStream ),
800+ singleUse : singleUse ,
801+ wantSettingsAck : true ,
802+ pings : make (map [[8 ]byte ]chan struct {}),
803+ reqHeaderMu : make (chan struct {}, 1 ),
753804 }
754805 if d := t .idleConnTimeout (); d != 0 {
755806 cc .idleTimeout = d
@@ -796,7 +847,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
796847
797848 initialSettings := []Setting {
798849 {ID : SettingEnablePush , Val : 0 },
799- {ID : SettingInitialWindowSize , Val : transportDefaultStreamFlow },
850+ {ID : SettingInitialWindowSize , Val : t . maxDownloadBufferPerStream () },
800851 }
801852 if max := t .maxFrameReadSize (); max != 0 {
802853 initialSettings = append (initialSettings , Setting {ID : SettingMaxFrameSize , Val : max })
@@ -810,8 +861,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
810861
811862 cc .bw .Write (clientPreface )
812863 cc .fr .WriteSettings (initialSettings ... )
813- cc .fr .WriteWindowUpdate (0 , transportDefaultConnFlow )
814- cc .inflow .init (transportDefaultConnFlow + initialWindowSize )
864+ cc .fr .WriteWindowUpdate (0 , t . maxDownloadBufferPerConnection () )
865+ cc .inflow .init (int32 ( t . maxDownloadBufferPerConnection ()) + initialWindowSize )
815866 cc .bw .Flush ()
816867 if cc .werr != nil {
817868 cc .Close ()
@@ -1660,12 +1711,12 @@ var (
16601711// outgoing request bodies to read/write to/from.
16611712//
16621713// It returns max(1, min(peer's advertised max frame size,
1663- // Request.ContentLength+1, 512KB )).
1714+ // Request.ContentLength+1, Transport.MaxWriteFrameSize )).
16641715func (cs * clientStream ) frameScratchBufferLen (maxFrameSize int ) int {
1665- const max = 512 << 10
1716+ var maxSize = int64 ( cs . cc . maxWriteFrameSize )
16661717 n := int64 (maxFrameSize )
1667- if n > max {
1668- n = max
1718+ if n > maxSize {
1719+ n = maxSize
16691720 }
16701721 if cl := cs .reqBodyContentLength ; cl != - 1 && cl + 1 < n {
16711722 // Add an extra byte past the declared content-length to
@@ -2120,7 +2171,8 @@ type resAndError struct {
21202171func (cc * ClientConn ) addStreamLocked (cs * clientStream ) {
21212172 cs .flow .add (int32 (cc .initialWindowSize ))
21222173 cs .flow .setConnFlow (& cc .flow )
2123- cs .inflow .init (transportDefaultStreamFlow )
2174+ // no need to truncate since max is maxWriteFrameSize
2175+ cs .inflow .init (int32 (cc .maxDownloadBufferPerStream ))
21242176 cs .ID = cc .nextStreamID
21252177 cc .nextStreamID += 2
21262178 cc .streams [cs .ID ] = cs
0 commit comments