@@ -24,37 +24,61 @@ constexpr uint32_t kDefaultMaxDirectErrorHandlingPerInterval = 100;
2424constexpr uint32_t kMaxDirectErrorHandlingPerIntervalLowerBound = 50 ;
2525constexpr std::chrono::milliseconds kDefaultDirectErrorHandlingDuration {100 };
2626
27+ constexpr uint32_t kDefaultMaxHeadersPerInterval = 500 ;
28+ constexpr uint32_t kMaxHeadersPerIntervalLowerBound = 100 ;
29+ constexpr std::chrono::milliseconds kDefaultHeadersDuration {100 };
30+
31+ enum RateLimitTarget {
32+ CONTROL_MSGS ,
33+ DIRECT_ERROR_HANDLING ,
34+ HEADERS ,
35+ };
36+
2737/* *
2838 * This class implements the rate limiting logic for control messages and
2939 * stream errors (that might produce HTTP error pages). If a rate limit is
3040 * exeeded, the callback is converted to a session level error with
3141 * ProxygenError = kErrorDropped. This is a signal to the codec callback that
3242 * the codec would like the connection dropped.
43+ *
44+ * TODO: Refactor this into separate filters, or group related parameters
45+ * into structs.
3346 */
3447class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
3548 public:
3649 explicit ControlMessageRateLimitFilter (folly::HHWheelTimer* timer,
3750 HTTPSessionStats* httpSessionStats)
3851 : resetControlMessages_(numControlMsgsInCurrentInterval_,
52+ RateLimitTarget::CONTROL_MSGS ,
3953 httpSessionStats),
54+ resetDirectErrors_(numDirectErrorHandlingInCurrentInterval_,
55+ RateLimitTarget::DIRECT_ERROR_HANDLING ,
56+ httpSessionStats),
57+ resetHeaders_(numHeadersInCurrentInterval_,
58+ RateLimitTarget::HEADERS ,
59+ httpSessionStats),
4060 timer_(timer),
4161 httpSessionStats_(httpSessionStats) {
4262 }
4363
4464 void setSessionStats (HTTPSessionStats* httpSessionStats) {
4565 httpSessionStats_ = httpSessionStats;
4666 resetControlMessages_.httpSessionStats = httpSessionStats;
67+ resetHeaders_.httpSessionStats = httpSessionStats;
4768 }
4869
49- void setParams (
50- uint32_t maxControlMsgsPerInterval,
51- uint32_t maxDirectErrorHandlingPerInterval,
52- std::chrono::milliseconds controlMsgIntervalDuration,
53- std::chrono::milliseconds directErrorHandlingIntervalDuration) {
70+ void setParams (uint32_t maxControlMsgsPerInterval,
71+ uint32_t maxDirectErrorHandlingPerInterval,
72+ uint32_t maxHeadersPerInterval,
73+ std::chrono::milliseconds controlMsgIntervalDuration,
74+ std::chrono::milliseconds directErrorHandlingIntervalDuration,
75+ std::chrono::milliseconds headersIntervalDuration) {
5476 maxControlMsgsPerInterval_ = maxControlMsgsPerInterval;
5577 maxDirectErrorHandlingPerInterval_ = maxDirectErrorHandlingPerInterval;
5678 controlMsgIntervalDuration_ = controlMsgIntervalDuration;
5779 directErrorHandlingIntervalDuration_ = directErrorHandlingIntervalDuration;
80+ maxHeadersPerInterval_ = maxHeadersPerInterval;
81+ headersIntervalDuration_ = headersIntervalDuration;
5882 }
5983
6084 // Filter functions
@@ -86,6 +110,13 @@ class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
86110 }
87111 }
88112
113+ void onHeadersComplete (StreamID stream,
114+ std::unique_ptr<HTTPMessage> msg) override {
115+ if (!incrementNumHeadersInCurInterval ()) {
116+ callback_->onHeadersComplete (stream, std::move (msg));
117+ }
118+ }
119+
89120 void onError (HTTPCodec::StreamID streamID,
90121 const HTTPException& error,
91122 bool newTxn) override {
@@ -104,6 +135,7 @@ class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
104135 void detachThreadLocals () {
105136 resetControlMessages_.cancelTimeout ();
106137 resetDirectErrors_.cancelTimeout ();
138+ resetHeaders_.cancelTimeout ();
107139 timer_ = nullptr ;
108140 // Free pass when switching threads
109141 numControlMsgsInCurrentInterval_ = 0 ;
@@ -140,6 +172,25 @@ class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
140172 return false ;
141173 }
142174
175+ bool incrementNumHeadersInCurInterval () {
176+ if (numHeadersInCurrentInterval_ == 0 ) {
177+ // The first header (or first after a reset) schedules the next
178+ // reset timer
179+ CHECK (timer_);
180+ timer_->scheduleTimeout (&resetHeaders_, headersIntervalDuration_);
181+ }
182+
183+ if (++numHeadersInCurrentInterval_ > maxHeadersPerInterval_) {
184+ if (httpSessionStats_) {
185+ httpSessionStats_->recordHeadersRateLimited ();
186+ }
187+ callback_->onGoaway (http2::kMaxStreamID , ErrorCode::NO_ERROR );
188+ return true ;
189+ }
190+
191+ return false ;
192+ }
193+
143194 bool incrementDirectErrorHandlingInCurInterval () {
144195 if (numDirectErrorHandlingInCurrentInterval_ == 0 ) {
145196 // The first control message (or first after a reset) schedules the next
@@ -168,20 +219,34 @@ class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
168219 class ResetCounterTimeout : public folly ::HHWheelTimer::Callback {
169220 public:
170221 explicit ResetCounterTimeout (uint32_t & counterIn,
222+ RateLimitTarget rateLimitTargetIn,
171223 HTTPSessionStats* httpSessionStatsIn = nullptr )
172- : counter(counterIn), httpSessionStats(httpSessionStatsIn) {
224+ : counter(counterIn),
225+ rateLimitTarget(rateLimitTargetIn),
226+ httpSessionStats(httpSessionStatsIn) {
173227 }
174228
175229 void timeoutExpired () noexcept override {
176230 if (counter > 0 && httpSessionStats) {
177- httpSessionStats->recordControlMsgsInInterval (counter);
231+ switch (rateLimitTarget) {
232+ case RateLimitTarget::CONTROL_MSGS :
233+ httpSessionStats->recordControlMsgsInInterval (counter);
234+ break ;
235+ case RateLimitTarget::DIRECT_ERROR_HANDLING :
236+ // No stats for this one
237+ break ;
238+ case RateLimitTarget::HEADERS :
239+ httpSessionStats->recordHeadersInInterval (counter);
240+ break ;
241+ }
178242 }
179243 counter = 0 ;
180244 }
181245 void callbackCanceled () noexcept override {
182246 }
183247
184248 uint32_t & counter;
249+ RateLimitTarget rateLimitTarget;
185250 HTTPSessionStats* httpSessionStats{nullptr };
186251 };
187252
@@ -197,14 +262,18 @@ class ControlMessageRateLimitFilter : public PassThroughHTTPCodecFilter {
197262 uint32_t maxDirectErrorHandlingPerInterval_{
198263 kDefaultMaxDirectErrorHandlingPerInterval };
199264
265+ uint32_t numHeadersInCurrentInterval_{0 };
266+ uint32_t maxHeadersPerInterval_{kDefaultMaxHeadersPerInterval };
267+
200268 std::chrono::milliseconds controlMsgIntervalDuration_{
201269 kDefaultControlMsgDuration };
202270 std::chrono::milliseconds directErrorHandlingIntervalDuration_{
203271 kDefaultDirectErrorHandlingDuration };
272+ std::chrono::milliseconds headersIntervalDuration_{kDefaultHeadersDuration };
204273
205274 ResetCounterTimeout resetControlMessages_;
206- ResetCounterTimeout resetDirectErrors_{
207- numDirectErrorHandlingInCurrentInterval_} ;
275+ ResetCounterTimeout resetDirectErrors_;
276+ ResetCounterTimeout resetHeaders_ ;
208277 folly::HHWheelTimer* timer_{nullptr };
209278 HTTPSessionStats* httpSessionStats_{nullptr };
210279};
0 commit comments