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 private int outstandingControlFrames;
33
34 private final int maxOutstandingControlFrames;
35 private final ChannelFutureListener outstandingControlFramesListener = f-> outstandingControlFrames--;
36 private Http2LifecycleManager lifecycleManager;
37 private boolean limitReached;
38
39 Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) {
40 super(delegate);
41 this.maxOutstandingControlFrames = ObjectUtil.checkPositive(maxOutstandingControlFrames,
42 "maxOutstandingControlFrames");
43 }
44
45 @Override
46 public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
47 this.lifecycleManager = lifecycleManager;
48 super.lifecycleManager(lifecycleManager);
49 }
50
51 @Override
52 public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
53 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
54 if (newPromise == null) {
55 return promise;
56 }
57 return super.writeSettingsAck(ctx, newPromise);
58 }
59
60 @Override
61 public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
62
63 if (ack) {
64 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
65 if (newPromise == null) {
66 return promise;
67 }
68 return super.writePing(ctx, ack, data, newPromise);
69 }
70 return super.writePing(ctx, ack, data, promise);
71 }
72
73 @Override
74 public ChannelFuture writeRstStream(
75 ChannelHandlerContext ctx, int streamId, long errorCode, ChannelPromise promise) {
76 ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
77 if (newPromise == null) {
78 return promise;
79 }
80 return super.writeRstStream(ctx, streamId, errorCode, newPromise);
81 }
82
83 private ChannelPromise handleOutstandingControlFrames(ChannelHandlerContext ctx, ChannelPromise promise) {
84 if (!limitReached) {
85 if (outstandingControlFrames == maxOutstandingControlFrames) {
86
87 ctx.flush();
88 }
89 if (outstandingControlFrames == maxOutstandingControlFrames) {
90 limitReached = true;
91 Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM,
92 "Maximum number %d of outstanding control frames reached", maxOutstandingControlFrames);
93 logger.info("{} Maximum number {} of outstanding control frames reached, closing channel.",
94 ctx.channel(), maxOutstandingControlFrames, exception);
95
96
97 lifecycleManager.onError(ctx, true, exception);
98 ctx.close();
99 }
100 outstandingControlFrames++;
101
102
103
104 return promise.unvoid().addListener(outstandingControlFramesListener);
105 }
106 return promise;
107 }
108 }