24
24
import io .netty .channel .ChannelHandlerContext ;
25
25
import io .netty .channel .ChannelId ;
26
26
import io .netty .channel .ChannelMetadata ;
27
+ import io .netty .channel .ChannelOption ;
27
28
import io .netty .channel .ChannelOutboundBuffer ;
28
29
import io .netty .channel .ChannelPipeline ;
29
30
import io .netty .channel .ChannelProgressivePromise ;
41
42
import io .netty .handler .ssl .SslCloseCompletionEvent ;
42
43
import io .netty .util .DefaultAttributeMap ;
43
44
import io .netty .util .ReferenceCountUtil ;
45
+ import io .netty .util .internal .ObjectUtil ;
44
46
import io .netty .util .internal .StringUtil ;
45
47
import io .netty .util .internal .logging .InternalLogger ;
46
48
import io .netty .util .internal .logging .InternalLoggerFactory ;
49
51
import java .net .SocketAddress ;
50
52
import java .nio .channels .ClosedChannelException ;
51
53
import java .util .ArrayDeque ;
54
+ import java .util .Map ;
52
55
import java .util .Queue ;
53
56
import java .util .concurrent .RejectedExecutionException ;
54
57
import java .util .concurrent .atomic .AtomicIntegerFieldUpdater ;
@@ -906,24 +909,19 @@ void readEOS() {
906
909
readEOS = true ;
907
910
}
908
911
909
- private void updateLocalWindowIfNeeded () {
910
- if (flowControlledBytes != 0 && !parentContext ().isRemoved ()) {
912
+ private boolean updateLocalWindowIfNeeded () {
913
+ if (flowControlledBytes != 0 && !parentContext ().isRemoved () && config . autoStreamFlowControl ) {
911
914
int bytes = flowControlledBytes ;
912
915
flowControlledBytes = 0 ;
913
- ChannelFuture future = write0 (parentContext (), new DefaultHttp2WindowUpdateFrame (bytes ).stream (stream ));
914
- // window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously
915
- // completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need,
916
- // to assume there was a write done that needs to be flushed or we risk flow control starvation.
917
- writeDoneAndNoFlush = true ;
918
- // Add a listener which will notify and teardown the stream
919
- // when a window update fails if needed or check the result of the future directly if it was completed
920
- // already.
921
- // See https://github.com/netty/netty/issues/9663
922
- if (future .isDone ()) {
923
- windowUpdateFrameWriteComplete (future , AbstractHttp2StreamChannel .this );
924
- } else {
925
- future .addListener (windowUpdateFrameWriteListener );
926
- }
916
+ writeWindowUpdateFrame (new DefaultHttp2WindowUpdateFrame (bytes ).stream (stream ));
917
+ return true ;
918
+ }
919
+ return false ;
920
+ }
921
+
922
+ void updateLocalWindowIfNeededAndFlush () {
923
+ if (updateLocalWindowIfNeeded ()) {
924
+ flush ();
927
925
}
928
926
}
929
927
@@ -982,6 +980,24 @@ void doRead0(Http2Frame frame, RecvByteBufAllocator.Handle allocHandle) {
982
980
pipeline ().fireChannelRead (frame );
983
981
}
984
982
983
+ private ChannelFuture writeWindowUpdateFrame (Http2WindowUpdateFrame windowUpdateFrame ) {
984
+ ChannelFuture future = write0 (parentContext (), windowUpdateFrame );
985
+ // window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously
986
+ // completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need,
987
+ // to assume there was a write done that needs to be flushed or we risk flow control starvation.
988
+ writeDoneAndNoFlush = true ;
989
+ // Add a listener which will notify and teardown the stream
990
+ // when a window update fails if needed or check the result of the future directly if it was completed
991
+ // already.
992
+ // See https://github.com/netty/netty/issues/9663
993
+ if (future .isDone ()) {
994
+ windowUpdateFrameWriteComplete (future , AbstractHttp2StreamChannel .this );
995
+ } else {
996
+ future .addListener (windowUpdateFrameWriteListener );
997
+ }
998
+ return future ;
999
+ }
1000
+
985
1001
@ Override
986
1002
public void write (Object msg , final ChannelPromise promise ) {
987
1003
// After this point its not possible to cancel a write anymore.
@@ -1001,7 +1017,42 @@ public void write(Object msg, final ChannelPromise promise) {
1001
1017
try {
1002
1018
if (msg instanceof Http2StreamFrame ) {
1003
1019
Http2StreamFrame frame = validateStreamFrame ((Http2StreamFrame ) msg ).stream (stream ());
1004
- writeHttp2StreamFrame (frame , promise );
1020
+ if (msg instanceof Http2WindowUpdateFrame ) {
1021
+ Http2WindowUpdateFrame updateFrame = (Http2WindowUpdateFrame ) msg ;
1022
+ if (config .autoStreamFlowControl ) {
1023
+ ReferenceCountUtil .release (msg );
1024
+ promise .setFailure (new UnsupportedOperationException (
1025
+ Http2StreamChannelOption .AUTO_STREAM_FLOW_CONTROL + " is set to false" ));
1026
+ return ;
1027
+ }
1028
+ try {
1029
+ ObjectUtil .checkInRange (updateFrame .windowSizeIncrement (), 0 ,
1030
+ flowControlledBytes , "windowSizeIncrement" );
1031
+ } catch (RuntimeException e ) {
1032
+ ReferenceCountUtil .release (updateFrame );
1033
+ promise .setFailure (e );
1034
+ return ;
1035
+ }
1036
+ flowControlledBytes -= updateFrame .windowSizeIncrement ();
1037
+ if (parentContext ().isRemoved ()) {
1038
+ ReferenceCountUtil .release (msg );
1039
+ promise .setFailure (new ClosedChannelException ());
1040
+ return ;
1041
+ }
1042
+ ChannelFuture f = writeWindowUpdateFrame (updateFrame );
1043
+ if (f .isDone ()) {
1044
+ writeComplete (f , promise );
1045
+ } else {
1046
+ f .addListener (new ChannelFutureListener () {
1047
+ @ Override
1048
+ public void operationComplete (ChannelFuture future ) {
1049
+ writeComplete (future , promise );
1050
+ }
1051
+ });
1052
+ }
1053
+ } else {
1054
+ writeHttp2StreamFrame (frame , promise );
1055
+ }
1005
1056
} else {
1006
1057
String msgStr = msg .toString ();
1007
1058
ReferenceCountUtil .release (msg );
@@ -1152,6 +1203,8 @@ public ChannelOutboundBuffer outboundBuffer() {
1152
1203
* changes.
1153
1204
*/
1154
1205
private static final class Http2StreamChannelConfig extends DefaultChannelConfig {
1206
+
1207
+ volatile boolean autoStreamFlowControl = true ;
1155
1208
Http2StreamChannelConfig (Channel channel ) {
1156
1209
super (channel );
1157
1210
}
@@ -1175,6 +1228,49 @@ public ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
1175
1228
super .setRecvByteBufAllocator (allocator );
1176
1229
return this ;
1177
1230
}
1231
+
1232
+ @ Override
1233
+ public Map <ChannelOption <?>, Object > getOptions () {
1234
+ return getOptions (
1235
+ super .getOptions (),
1236
+ Http2StreamChannelOption .AUTO_STREAM_FLOW_CONTROL );
1237
+ }
1238
+
1239
+ @ SuppressWarnings ("unchecked" )
1240
+ @ Override
1241
+ public <T > T getOption (ChannelOption <T > option ) {
1242
+ if (option == Http2StreamChannelOption .AUTO_STREAM_FLOW_CONTROL ) {
1243
+ return (T ) Boolean .valueOf (autoStreamFlowControl );
1244
+ }
1245
+ return super .getOption (option );
1246
+ }
1247
+
1248
+ @ Override
1249
+ public <T > boolean setOption (ChannelOption <T > option , T value ) {
1250
+ validate (option , value );
1251
+ if (option == Http2StreamChannelOption .AUTO_STREAM_FLOW_CONTROL ) {
1252
+ boolean newValue = (Boolean ) value ;
1253
+ boolean changed = newValue && !autoStreamFlowControl ;
1254
+ autoStreamFlowControl = (Boolean ) value ;
1255
+ if (changed ) {
1256
+ if (channel .isRegistered ()) {
1257
+ final Http2ChannelUnsafe unsafe = (Http2ChannelUnsafe ) channel .unsafe ();
1258
+ if (channel .eventLoop ().inEventLoop ()) {
1259
+ unsafe .updateLocalWindowIfNeededAndFlush ();
1260
+ } else {
1261
+ channel .eventLoop ().execute (new Runnable () {
1262
+ @ Override
1263
+ public void run () {
1264
+ unsafe .updateLocalWindowIfNeededAndFlush ();
1265
+ }
1266
+ });
1267
+ }
1268
+ }
1269
+ }
1270
+ return true ;
1271
+ }
1272
+ return super .setOption (option , value );
1273
+ }
1178
1274
}
1179
1275
1180
1276
private void maybeAddChannelToReadCompletePendingQueue () {
0 commit comments