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.ChannelHandlerContext;
19 import io.netty.channel.ChannelPromise;
20 import io.netty.util.concurrent.Ticker;
21 import io.netty.util.internal.logging.InternalLogger;
22 import io.netty.util.internal.logging.InternalLoggerFactory;
23
24 import java.util.concurrent.TimeUnit;
25
26
27
28
29
30
31 final class Http2MaxRstFrameLimitEncoder extends DecoratingHttp2ConnectionEncoder {
32 private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2MaxRstFrameLimitEncoder.class);
33
34 private final long nanosPerWindow;
35 private final int maxRstFramesPerWindow;
36 private final Ticker ticker;
37 private long lastRstFrameNano;
38 private int sendRstInWindow;
39 private Http2LifecycleManager lifecycleManager;
40
41 Http2MaxRstFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxRstFramesPerWindow, int secondsPerWindow) {
42 this(delegate, maxRstFramesPerWindow, secondsPerWindow, Ticker.systemTicker());
43 }
44
45 Http2MaxRstFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxRstFramesPerWindow, int secondsPerWindow,
46 Ticker ticker) {
47 super(delegate);
48 this.maxRstFramesPerWindow = maxRstFramesPerWindow;
49 this.nanosPerWindow = TimeUnit.SECONDS.toNanos(secondsPerWindow);
50 this.ticker = ticker;
51 lastRstFrameNano = ticker.nanoTime();
52 }
53
54 @Override
55 public void lifecycleManager(Http2LifecycleManager lifecycleManager) {
56 this.lifecycleManager = lifecycleManager;
57 super.lifecycleManager(lifecycleManager);
58 }
59
60 @Override
61 public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
62 ChannelPromise promise) {
63 ChannelFuture future = super.writeRstStream(ctx, streamId, errorCode, promise);
64 if (countRstFrameErrorCode(errorCode)) {
65 long currentNano = ticker.nanoTime();
66 if (currentNano - lastRstFrameNano >= nanosPerWindow) {
67 lastRstFrameNano = currentNano;
68 sendRstInWindow = 1;
69 } else {
70 sendRstInWindow++;
71 if (sendRstInWindow > maxRstFramesPerWindow) {
72 Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM,
73 "Maximum number %d of RST frames frames reached within %d seconds", maxRstFramesPerWindow,
74 TimeUnit.NANOSECONDS.toSeconds(nanosPerWindow));
75
76 logger.debug("{} Maximum number {} of RST frames reached within {} seconds, " +
77 "closing connection with {} error", ctx.channel(), maxRstFramesPerWindow,
78 TimeUnit.NANOSECONDS.toSeconds(nanosPerWindow), exception.error(),
79 exception);
80
81 lifecycleManager.onError(ctx, true, exception);
82 ctx.close();
83 }
84 }
85 }
86
87 return future;
88 }
89
90 private boolean countRstFrameErrorCode(long errorCode) {
91
92 return errorCode != Http2Error.CANCEL.code() && errorCode != Http2Error.NO_ERROR.code();
93 }
94 }