,
{RECT_CONFIG, "proxy.config.http2.max_priority_frames_per_minute", RECD_INT, "120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.http2.max_rst_stream_frames_per_minute", RECD_INT, "200", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ ,
{RECT_CONFIG, "proxy.config.http2.min_avg_window_update", RECD_FLOAT, "2560.0", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http2.header_table_size_limit", RECD_INT, "65536", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
"proxy.process.http2.max_ping_frames_per_minute_exceeded";
static const char *const HTTP2_STAT_MAX_PRIORITY_FRAMES_PER_MINUTE_EXCEEDED_NAME =
"proxy.process.http2.max_priority_frames_per_minute_exceeded";
+static const char *const HTTP2_STAT_MAX_RST_STREAM_FRAMES_PER_MINUTE_EXCEEDED_NAME =
+ "proxy.process.http2.max_rst_stream_frames_per_minute_exceeded";
static const char *const HTTP2_STAT_INSUFFICIENT_AVG_WINDOW_UPDATE_NAME = "proxy.process.http2.insufficient_avg_window_update";
union byte_pointer {
}
// Initialize this subsystem with librecords configs (for now)
-uint32_t Http2::max_concurrent_streams_in = 100;
-uint32_t Http2::min_concurrent_streams_in = 10;
-uint32_t Http2::max_active_streams_in = 0;
-bool Http2::throttling = false;
-uint32_t Http2::stream_priority_enabled = 0;
-uint32_t Http2::initial_window_size = 65535;
-uint32_t Http2::max_frame_size = 16384;
-uint32_t Http2::header_table_size = 4096;
-uint32_t Http2::max_header_list_size = 4294967295;
-uint32_t Http2::accept_no_activity_timeout = 120;
-uint32_t Http2::no_activity_timeout_in = 120;
-uint32_t Http2::active_timeout_in = 0;
-uint32_t Http2::push_diary_size = 256;
-uint32_t Http2::zombie_timeout_in = 0;
-float Http2::stream_error_rate_threshold = 0.1;
-uint32_t Http2::max_settings_per_frame = 7;
-uint32_t Http2::max_settings_per_minute = 14;
-uint32_t Http2::max_settings_frames_per_minute = 14;
-uint32_t Http2::max_ping_frames_per_minute = 60;
-uint32_t Http2::max_priority_frames_per_minute = 120;
-float Http2::min_avg_window_update = 2560.0;
-uint32_t Http2::con_slow_log_threshold = 0;
-uint32_t Http2::stream_slow_log_threshold = 0;
-uint32_t Http2::header_table_size_limit = 65536;
+uint32_t Http2::max_concurrent_streams_in = 100;
+uint32_t Http2::min_concurrent_streams_in = 10;
+uint32_t Http2::max_active_streams_in = 0;
+bool Http2::throttling = false;
+uint32_t Http2::stream_priority_enabled = 0;
+uint32_t Http2::initial_window_size = 65535;
+uint32_t Http2::max_frame_size = 16384;
+uint32_t Http2::header_table_size = 4096;
+uint32_t Http2::max_header_list_size = 4294967295;
+uint32_t Http2::accept_no_activity_timeout = 120;
+uint32_t Http2::no_activity_timeout_in = 120;
+uint32_t Http2::active_timeout_in = 0;
+uint32_t Http2::push_diary_size = 256;
+uint32_t Http2::zombie_timeout_in = 0;
+float Http2::stream_error_rate_threshold = 0.1;
+uint32_t Http2::max_settings_per_frame = 7;
+uint32_t Http2::max_settings_per_minute = 14;
+uint32_t Http2::max_settings_frames_per_minute = 14;
+uint32_t Http2::max_ping_frames_per_minute = 60;
+uint32_t Http2::max_priority_frames_per_minute = 120;
+uint32_t Http2::max_rst_stream_frames_per_minute = 200;
+float Http2::min_avg_window_update = 2560.0;
+uint32_t Http2::con_slow_log_threshold = 0;
+uint32_t Http2::stream_slow_log_threshold = 0;
+uint32_t Http2::header_table_size_limit = 65536;
void
Http2::init()
REC_EstablishStaticConfigInt32U(max_settings_frames_per_minute, "proxy.config.http2.max_settings_frames_per_minute");
REC_EstablishStaticConfigInt32U(max_ping_frames_per_minute, "proxy.config.http2.max_ping_frames_per_minute");
REC_EstablishStaticConfigInt32U(max_priority_frames_per_minute, "proxy.config.http2.max_priority_frames_per_minute");
+ REC_EstablishStaticConfigInt32U(max_rst_stream_frames_per_minute, "proxy.config.http2.max_rst_stream_frames_per_minute");
REC_EstablishStaticConfigFloat(min_avg_window_update, "proxy.config.http2.min_avg_window_update");
REC_EstablishStaticConfigInt32U(con_slow_log_threshold, "proxy.config.http2.connection.slow.log.threshold");
REC_EstablishStaticConfigInt32U(stream_slow_log_threshold, "proxy.config.http2.stream.slow.log.threshold");
static_cast<int>(HTTP2_STAT_MAX_PING_FRAMES_PER_MINUTE_EXCEEDED), RecRawStatSyncSum);
RecRegisterRawStat(http2_rsb, RECT_PROCESS, HTTP2_STAT_MAX_PRIORITY_FRAMES_PER_MINUTE_EXCEEDED_NAME, RECD_INT, RECP_PERSISTENT,
static_cast<int>(HTTP2_STAT_MAX_PRIORITY_FRAMES_PER_MINUTE_EXCEEDED), RecRawStatSyncSum);
+ RecRegisterRawStat(http2_rsb, RECT_PROCESS, HTTP2_STAT_MAX_RST_STREAM_FRAMES_PER_MINUTE_EXCEEDED_NAME, RECD_INT, RECP_PERSISTENT,
+ static_cast<int>(HTTP2_STAT_MAX_RST_STREAM_FRAMES_PER_MINUTE_EXCEEDED), RecRawStatSyncSum);
RecRegisterRawStat(http2_rsb, RECT_PROCESS, HTTP2_STAT_INSUFFICIENT_AVG_WINDOW_UPDATE_NAME, RECD_INT, RECP_PERSISTENT,
static_cast<int>(HTTP2_STAT_INSUFFICIENT_AVG_WINDOW_UPDATE), RecRawStatSyncSum);
}
"reset frame wrong length");
}
+ // Update RST_STREAM frame count per minute
+ cstate.increment_received_rst_stream_frame_count();
+ // Close this connection if its RST_STREAM frame count exceeds a limit
+ if (cstate.get_received_rst_stream_frame_count() > Http2::max_rst_stream_frames_per_minute) {
+ HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_MAX_RST_STREAM_FRAMES_PER_MINUTE_EXCEEDED, this_ethread());
+ Http2StreamDebug(cstate.ua_session, stream_id, "Observed too frequent RST_STREAM frames: %u frames within a last minute",
+ cstate.get_received_settings_frame_count());
+ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+ "reset too frequent RST_STREAM frames");
+ }
+
if (stream == nullptr || !stream->change_state(frame.header().type, frame.header().flags)) {
// If a RST_STREAM frame identifying an idle stream is received, the
// recipient MUST treat this as a connection error of type PROTOCOL_ERROR.
return this->_received_priority_frame_counter.get_count();
}
+void
+Http2ConnectionState::increment_received_rst_stream_frame_count()
+{
+ this->_received_rst_stream_frame_counter.increment();
+}
+
+uint32_t
+Http2ConnectionState::get_received_rst_stream_frame_count()
+{
+ return this->_received_rst_stream_frame_counter.get_count();
+}
+
// Return min_concurrent_streams_in when current client streams number is larger than max_active_streams_in.
// Main purpose of this is preventing DDoS Attacks.
unsigned