@@ -73,7 +73,7 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler {
73
73
private var wroteFrame : Bool = false
74
74
75
75
/// This object deploys heuristics to attempt to detect denial of service attacks.
76
- private var denialOfServiceValidator : DOSHeuristics
76
+ private var denialOfServiceValidator : DOSHeuristics < RealNIODeadlineClock >
77
77
78
78
/// The mode this handler is operating in.
79
79
private let mode : ParserMode
@@ -209,7 +209,9 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler {
209
209
headerBlockValidation: headerBlockValidation,
210
210
contentLengthValidation: contentLengthValidation,
211
211
maximumSequentialEmptyDataFrames: 1 ,
212
- maximumBufferedControlFrames: 10000 )
212
+ maximumBufferedControlFrames: 10000 ,
213
+ maximumResetFrameCount: 200 ,
214
+ resetFrameCounterWindow: . seconds( 30 ) )
213
215
}
214
216
215
217
/// Constructs a ``NIOHTTP2Handler``.
@@ -236,23 +238,47 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler {
236
238
headerBlockValidation: headerBlockValidation,
237
239
contentLengthValidation: contentLengthValidation,
238
240
maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames,
239
- maximumBufferedControlFrames: maximumBufferedControlFrames)
241
+ maximumBufferedControlFrames: maximumBufferedControlFrames,
242
+ maximumResetFrameCount: 200 ,
243
+ resetFrameCounterWindow: . seconds( 30 ) )
240
244
241
245
}
242
246
247
+ /// Constructs a ``NIOHTTP2Handler``.
248
+ ///
249
+ /// - Parameters:
250
+ /// - mode: The mode for this handler, client or server.
251
+ /// - connectionConfiguration: The settings that will be used when establishing the connection.
252
+ /// - streamConfiguration: The settings that will be used when establishing new streams.
253
+ public convenience init ( mode: ParserMode ,
254
+ connectionConfiguration: ConnectionConfiguration = . init( ) ,
255
+ streamConfiguration: StreamConfiguration = . init( ) ) {
256
+ self . init ( mode: mode,
257
+ eventLoop: nil ,
258
+ initialSettings: connectionConfiguration. initialSettings,
259
+ headerBlockValidation: connectionConfiguration. headerBlockValidation,
260
+ contentLengthValidation: connectionConfiguration. contentLengthValidation,
261
+ maximumSequentialEmptyDataFrames: connectionConfiguration. maximumSequentialEmptyDataFrames,
262
+ maximumBufferedControlFrames: connectionConfiguration. maximumBufferedControlFrames,
263
+ maximumResetFrameCount: streamConfiguration. streamResetFrameRateLimit. maximumCount,
264
+ resetFrameCounterWindow: streamConfiguration. streamResetFrameRateLimit. windowLength)
265
+ }
266
+
243
267
private init ( mode: ParserMode ,
244
268
eventLoop: EventLoop ? ,
245
269
initialSettings: HTTP2Settings ,
246
270
headerBlockValidation: ValidationState ,
247
271
contentLengthValidation: ValidationState ,
248
272
maximumSequentialEmptyDataFrames: Int ,
249
- maximumBufferedControlFrames: Int ) {
273
+ maximumBufferedControlFrames: Int ,
274
+ maximumResetFrameCount: Int ,
275
+ resetFrameCounterWindow: TimeAmount ) {
250
276
self . eventLoop = eventLoop
251
277
self . stateMachine = HTTP2ConnectionStateMachine ( role: . init( mode) , headerBlockValidation: . init( headerBlockValidation) , contentLengthValidation: . init( contentLengthValidation) )
252
278
self . mode = mode
253
279
self . initialSettings = initialSettings
254
280
self . outboundBuffer = CompoundOutboundBuffer ( mode: mode, initialMaxOutboundStreams: 100 , maxBufferedControlFrames: maximumBufferedControlFrames)
255
- self . denialOfServiceValidator = DOSHeuristics ( maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames)
281
+ self . denialOfServiceValidator = DOSHeuristics ( maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames, maximumResetFrameCount : maximumResetFrameCount , resetFrameCounterWindow : resetFrameCounterWindow )
256
282
self . tolerateImpossibleStateTransitionsInDebugMode = false
257
283
self . inboundStreamMultiplexerState = . uninitializedLegacy
258
284
}
@@ -271,19 +297,25 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler {
271
297
/// upper limit on the depth of this queue. Defaults to 10,000.
272
298
/// - tolerateImpossibleStateTransitionsInDebugMode: Whether impossible state transitions should be tolerated
273
299
/// in debug mode.
300
+ /// - maximumResetFrameCount: Controls the maximum permitted reset frames within a given time window. Too many may exhaust CPU resources. To protect
301
+ /// against this DoS vector we put an upper limit on this rate. Defaults to 200.
302
+ /// - resetFrameCounterWindow: Controls the sliding window used to enforce the maximum permitted reset frames rate. Too many may exhaust CPU resources. To protect
303
+ /// against this DoS vector we put an upper limit on this rate. 30 seconds.
274
304
internal init ( mode: ParserMode ,
275
305
initialSettings: HTTP2Settings = nioDefaultSettings,
276
306
headerBlockValidation: ValidationState = . enabled,
277
307
contentLengthValidation: ValidationState = . enabled,
278
308
maximumSequentialEmptyDataFrames: Int = 1 ,
279
309
maximumBufferedControlFrames: Int = 10000 ,
280
- tolerateImpossibleStateTransitionsInDebugMode: Bool = false ) {
310
+ tolerateImpossibleStateTransitionsInDebugMode: Bool = false ,
311
+ maximumResetFrameCount: Int = 200 ,
312
+ resetFrameCounterWindow: TimeAmount = . seconds( 30 ) ) {
281
313
self . stateMachine = HTTP2ConnectionStateMachine ( role: . init( mode) , headerBlockValidation: . init( headerBlockValidation) , contentLengthValidation: . init( contentLengthValidation) )
282
314
self . mode = mode
283
315
self . eventLoop = nil
284
316
self . initialSettings = initialSettings
285
317
self . outboundBuffer = CompoundOutboundBuffer ( mode: mode, initialMaxOutboundStreams: 100 , maxBufferedControlFrames: maximumBufferedControlFrames)
286
- self . denialOfServiceValidator = DOSHeuristics ( maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames)
318
+ self . denialOfServiceValidator = DOSHeuristics ( maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames, maximumResetFrameCount : maximumResetFrameCount , resetFrameCounterWindow : resetFrameCounterWindow )
287
319
self . tolerateImpossibleStateTransitionsInDebugMode = tolerateImpossibleStateTransitionsInDebugMode
288
320
self . inboundStreamMultiplexerState = . uninitializedLegacy
289
321
}
@@ -1040,7 +1072,9 @@ extension NIOHTTP2Handler {
1040
1072
headerBlockValidation: connectionConfiguration. headerBlockValidation,
1041
1073
contentLengthValidation: connectionConfiguration. contentLengthValidation,
1042
1074
maximumSequentialEmptyDataFrames: connectionConfiguration. maximumSequentialEmptyDataFrames,
1043
- maximumBufferedControlFrames: connectionConfiguration. maximumBufferedControlFrames
1075
+ maximumBufferedControlFrames: connectionConfiguration. maximumBufferedControlFrames,
1076
+ maximumResetFrameCount: streamConfiguration. streamResetFrameRateLimit. maximumCount,
1077
+ resetFrameCounterWindow: streamConfiguration. streamResetFrameRateLimit. windowLength
1044
1078
)
1045
1079
1046
1080
self . inboundStreamMultiplexerState = . uninitializedInline( streamConfiguration, inboundStreamInitializer, streamDelegate)
@@ -1061,7 +1095,9 @@ extension NIOHTTP2Handler {
1061
1095
headerBlockValidation: connectionConfiguration. headerBlockValidation,
1062
1096
contentLengthValidation: connectionConfiguration. contentLengthValidation,
1063
1097
maximumSequentialEmptyDataFrames: connectionConfiguration. maximumSequentialEmptyDataFrames,
1064
- maximumBufferedControlFrames: connectionConfiguration. maximumBufferedControlFrames
1098
+ maximumBufferedControlFrames: connectionConfiguration. maximumBufferedControlFrames,
1099
+ maximumResetFrameCount: streamConfiguration. streamResetFrameRateLimit. maximumCount,
1100
+ resetFrameCounterWindow: streamConfiguration. streamResetFrameRateLimit. windowLength
1065
1101
)
1066
1102
self . inboundStreamMultiplexerState = . uninitializedAsync( streamConfiguration, inboundStreamInitializerWithAnyOutput, streamDelegate)
1067
1103
}
@@ -1086,6 +1122,17 @@ extension NIOHTTP2Handler {
1086
1122
public var targetWindowSize : Int = 65535
1087
1123
public var outboundBufferSizeHighWatermark : Int = 8196
1088
1124
public var outboundBufferSizeLowWatermark : Int = 4092
1125
+ public var streamResetFrameRateLimit : StreamResetFrameRateLimitConfiguration = . init( )
1126
+ public init ( ) { }
1127
+ }
1128
+
1129
+ /// Stream reset frame rate limit configuration.
1130
+ ///
1131
+ /// The settings that control the maximum permitted reset frames within a given time window. Too many may exhaust CPU resources.
1132
+ /// To protect against this DoS vector we put an upper limit on this rate.
1133
+ public struct StreamResetFrameRateLimitConfiguration : Hashable , Sendable {
1134
+ public var maximumCount : Int = 200
1135
+ public var windowLength : TimeAmount = . seconds( 30 )
1089
1136
public init ( ) { }
1090
1137
}
1091
1138
0 commit comments