1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.handler.codec.http2;
16
17 import io.netty.channel.ChannelFuture;
18 import io.netty.channel.ChannelFutureListener;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.util.internal.ObjectUtil;
22 import io.netty.util.internal.logging.InternalLogger;
23 import io.netty.util.internal.logging.InternalLoggerFactory;
24
25
26
27
28
29
30 final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncoder {
31 private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class);
32
33 private final int maxOutstandingControlFrames;
34 private final ChannelFutureListener outstandingControlFramesListener = new ChannelFutureListener() {
35 @Override
36 public void operationComplete(ChannelFuture future) {
37 outstandingControlFrames--;
38 }
39 };
40 private Http2LifecycleManager lifecycleManager;
41 private int outstandingControlFrames;
42 private boolean limitReached;
43
44 Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) {
45 super(delegate);
46 this.maxOutstandingControlFrames = ObjectUtil.checkPositive(maxOutstandingControlFrames,
47 "maxOutstandingControlFrames");
48 }
49
50 @Override
51 public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
52 this.lifecycleManager = lifecycleManager;
53 super.lifecycleManager(lifecycleManager);
54 }
55
56 @Override
57 public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
58 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
59 if (newPromise == null) {
60 return promise;
61 }
62 return super.writeSettingsAck(ctx, newPromise);
63 }
64
65 @Override
66 public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
67
68 if (ack) {
69 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
70 if (newPromise == null) {
71 return promise;
72 }
73 return super.writePing(ctx, ack, data, newPromise);
74 }
75 return super.writePing(ctx, ack, data, promise);
76 }
77
78 @Override
79 public ChannelFuture writeRstStream(
80 ChannelHandlerContext ctx, int streamId, long errorCode, ChannelPromise promise) {
81 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
82 if (newPromise == null) {
83 return promise;
84 }
85 return super.writeRstStream(ctx, streamId, errorCode, newPromise);
86 }
87
88 private ChannelPromise handleOutstandingControlFrames(ChannelHandlerContext ctx, ChannelPromise promise) {
89 if (!limitReached) {
90 if (outstandingControlFrames == maxOutstandingControlFrames) {
91
92 ctx.flush();
93 }
94 if (outstandingControlFrames == maxOutstandingControlFrames) {
95 limitReached = true;
96 Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM,
97 "Maximum number %d of outstanding control frames reached", maxOutstandingControlFrames);
98 logger.info("Maximum number {} of outstanding control frames reached. Closing channel {}",
99 maxOutstandingControlFrames, ctx.channel(), exception);
100
101
102 lifecycleManager.onError(ctx, true, exception);
103 ctx.close();
104 }
105 outstandingControlFrames++;
106
107
108
109 return promise.unvoid().addListener(outstandingControlFramesListener);
110 }
111 return promise;
112 }
113 }