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