@@ -39,14 +39,17 @@ import (
39
39
)
40
40
41
41
const (
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
43
46
// tokens we give the server at start-up, past the default 64k.
44
- transportDefaultConnFlow = 1 << 30
47
+ defaultTransportDefaultConnFlow = 1 << 30
45
48
46
- // transportDefaultStreamFlow is how many stream-level flow
49
+ // defaultTransportDefaultStreamFlow is how many stream-level flow
47
50
// control tokens we announce to the peer, and how many bytes
48
51
// we buffer per stream.
49
- transportDefaultStreamFlow = 4 << 20
52
+ defaultTransportDefaultStreamFlow = 4 << 20
50
53
51
54
defaultUserAgent = "Go-http-client/2.0"
52
55
@@ -124,6 +127,21 @@ type Transport struct {
124
127
// Values are bounded in the range 16k to 16M.
125
128
MaxReadFrameSize uint32
126
129
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
+
127
145
// MaxDecoderHeaderTableSize optionally specifies the http2
128
146
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
129
147
// informs the remote endpoint of the maximum size of the header compression
@@ -304,6 +322,9 @@ type ClientConn struct {
304
322
idleTimeout time.Duration // or 0 for never
305
323
idleTimer * time.Timer
306
324
325
+ maxWriteFrameSize uint32
326
+ maxDownloadBufferPerStream uint32
327
+
307
328
mu sync.Mutex // guards following
308
329
cond * sync.Cond // hold mu; broadcast on flow/closed changes
309
330
flow outflow // our conn-level flow control quota (cs.outflow is per stream)
@@ -731,25 +752,55 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
731
752
return initialHeaderTableSize
732
753
}
733
754
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
+
734
783
func (t * Transport ) NewClientConn (c net.Conn ) (* ClientConn , error ) {
735
784
return t .newClientConn (c , t .disableKeepAlives ())
736
785
}
737
786
738
787
func (t * Transport ) newClientConn (c net.Conn , singleUse bool ) (* ClientConn , error ) {
739
788
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 ),
753
804
}
754
805
if d := t .idleConnTimeout (); d != 0 {
755
806
cc .idleTimeout = d
@@ -796,7 +847,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
796
847
797
848
initialSettings := []Setting {
798
849
{ID : SettingEnablePush , Val : 0 },
799
- {ID : SettingInitialWindowSize , Val : transportDefaultStreamFlow },
850
+ {ID : SettingInitialWindowSize , Val : t . maxDownloadBufferPerStream () },
800
851
}
801
852
if max := t .maxFrameReadSize (); max != 0 {
802
853
initialSettings = append (initialSettings , Setting {ID : SettingMaxFrameSize , Val : max })
@@ -810,8 +861,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
810
861
811
862
cc .bw .Write (clientPreface )
812
863
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 )
815
866
cc .bw .Flush ()
816
867
if cc .werr != nil {
817
868
cc .Close ()
@@ -1660,12 +1711,12 @@ var (
1660
1711
// outgoing request bodies to read/write to/from.
1661
1712
//
1662
1713
// It returns max(1, min(peer's advertised max frame size,
1663
- // Request.ContentLength+1, 512KB )).
1714
+ // Request.ContentLength+1, Transport.MaxWriteFrameSize )).
1664
1715
func (cs * clientStream ) frameScratchBufferLen (maxFrameSize int ) int {
1665
- const max = 512 << 10
1716
+ var maxSize = int64 ( cs . cc . maxWriteFrameSize )
1666
1717
n := int64 (maxFrameSize )
1667
- if n > max {
1668
- n = max
1718
+ if n > maxSize {
1719
+ n = maxSize
1669
1720
}
1670
1721
if cl := cs .reqBodyContentLength ; cl != - 1 && cl + 1 < n {
1671
1722
// Add an extra byte past the declared content-length to
@@ -2120,7 +2171,8 @@ type resAndError struct {
2120
2171
func (cc * ClientConn ) addStreamLocked (cs * clientStream ) {
2121
2172
cs .flow .add (int32 (cc .initialWindowSize ))
2122
2173
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 ))
2124
2176
cs .ID = cc .nextStreamID
2125
2177
cc .nextStreamID += 2
2126
2178
cc .streams [cs .ID ] = cs
0 commit comments