This is an automated email from the ASF dual-hosted git repository.
maskit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 21581ecbf1 Add HTTP/2 Empty CONTINUATION & DATA frame counter (#11345)
21581ecbf1 is described below
commit 21581ecbf1cc441cfb6fab4f935b326107963ced
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Wed Jul 17 14:04:34 2024 -0600
Add HTTP/2 Empty CONTINUATION & DATA frame counter (#11345)
* HTTP/2 Empty CONTINUATION & DATA frame counter
* Allow using 0 as a valid limit value and use negative numbers as unlimited
* Don't return early if CONTINUATION frame is empty
* Fix typos
* Add documentation about proxy.config.http2.max_empty_frames_per_minute
* Add documentation about http2_max_empty_frames_per_minute in sni.yaml
* Fix errors from rebasing
* Documentation
* Improve wording
---------
Co-authored-by: Masaori Koshiba <[email protected]>
---
doc/admin-guide/files/records.yaml.en.rst | 19 +++++++++
doc/admin-guide/files/sni.yaml.en.rst | 4 ++
include/iocore/net/TLSSNISupport.h | 10 ++---
include/proxy/http2/HTTP2.h | 16 ++++----
include/proxy/http2/Http2ConnectionState.h | 14 ++++---
src/proxy/http2/HTTP2.cc | 32 ++++++++-------
src/proxy/http2/Http2ConnectionState.cc | 66 +++++++++++++++++++++++-------
src/records/RecordsConfig.cc | 14 ++++---
8 files changed, 124 insertions(+), 51 deletions(-)
diff --git a/doc/admin-guide/files/records.yaml.en.rst
b/doc/admin-guide/files/records.yaml.en.rst
index 359f99dba8..4c202c6560 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -4552,6 +4552,7 @@ HTTP/2 Configuration
Specifies how many settings in an HTTP/2 SETTINGS frame |TS| accepts.
Clients exceeded this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of settings received.
.. ts:cv:: CONFIG proxy.config.http2.max_settings_per_minute INT 14
:reloadable:
@@ -4559,6 +4560,7 @@ HTTP/2 Configuration
Specifies how many settings in HTTP/2 SETTINGS frames |TS| accept for a
minute.
Clients exceeded this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of settings received.
.. ts:cv:: CONFIG proxy.config.http2.max_settings_frames_per_minute INT 14
:reloadable:
@@ -4566,6 +4568,7 @@ HTTP/2 Configuration
Specifies how many SETTINGS frames |TS| receives for a minute at maximum.
Clients exceeded this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of SETTINGS frames
received.
.. ts:cv:: CONFIG proxy.config.http2.max_ping_frames_per_minute INT 60
:reloadable:
@@ -4573,6 +4576,7 @@ HTTP/2 Configuration
Specifies how many number of PING frames |TS| receives for a minute at
maximum.
Clients exceeded this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of PING frames
received.
.. ts:cv:: CONFIG proxy.config.http2.max_priority_frames_per_minute INT 120
:reloadable:
@@ -4582,6 +4586,7 @@ HTTP/2 Configuration
code of ENHANCE_YOUR_CALM. If this is set to 0, the limit logic is disabled.
This limit only will be enforced if
:ts:cv:`proxy.config.http2.stream_priority_enabled`
is set to 1.
+ Any negative value configures no limit to the number of PRIORITY frames
received.
.. ts:cv:: CONFIG proxy.config.http2.max_rst_stream_frames_per_minute INT 200
:reloadable:
@@ -4589,6 +4594,7 @@ HTTP/2 Configuration
Specifies how many RST_STREAM frames |TS| receives per minute at maximum.
Clients exceeding this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of RST_STREAM frames
received.
.. ts:cv:: CONFIG proxy.config.http2.max_continuation_frames_per_minute INT 120
:reloadable:
@@ -4596,6 +4602,19 @@ HTTP/2 Configuration
Specifies how many CONTINUATION frames |TS| receives per minute at maximum.
Clients exceeding this limit will be immediately disconnected with an error
code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of CONTINUATION frames
received.
+
+.. ts:cv:: CONFIG proxy.config.http2.max_empty_frames_per_minute INT 0
+ :reloadable:
+
+ Specifies the maximum number of empty frames |TS| will receive per minute
before it will start closing connections.
+ In this context, an "empty frame" means either a DATA frame that does not
carry a payload
+ nor an END_STREAM flag, or a CONTINUATION frame that does not carry payload
+ nor an END_HEADERS flag.
+ Clients exceeding this limit will be immediately disconnected with an error
+ code of ENHANCE_YOUR_CALM.
+ Any negative value configures no limit to the number of empty frames
received.
+ ``0`` is the default configuration, meaning that no empty frames are
allowed.
.. ts:cv:: CONFIG proxy.config.http2.min_avg_window_update FLOAT 2560.0
:reloadable:
diff --git a/doc/admin-guide/files/sni.yaml.en.rst
b/doc/admin-guide/files/sni.yaml.en.rst
index 7be40de811..191fefb010 100644
--- a/doc/admin-guide/files/sni.yaml.en.rst
+++ b/doc/admin-guide/files/sni.yaml.en.rst
@@ -228,6 +228,10 @@ http2_max_continuation_frames_per_minute Inbound
Specifies how many CONTINUATI
By default this is
:ts:cv:`proxy.config.http2.max_continuation_frames_per_minute`.
NOTE: Connection coalescing
may prevent this from taking effect.
+http2_max_empty_frames_per_minute Inbound Specifies how many empty
frames |TS| receives per minute at maximum.
+ By default this is
:ts:cv:`proxy.config.http2.max_empty_frames_per_minute`.
+ NOTE: Connection coalescing
may prevent this from taking effect.
+
quic Inbound Indicates whether QUIC
connections should be accepted. The valid values are :code:`on` or
:code:`off`. Note that this
is a more specific setting to configure QUIC availability per server
name. More broadly, you
will also need to configure :ts:cv:`proxy.config.http.server_ports` to
diff --git a/include/iocore/net/TLSSNISupport.h
b/include/iocore/net/TLSSNISupport.h
index 060b60c826..1e6685276f 100644
--- a/include/iocore/net/TLSSNISupport.h
+++ b/include/iocore/net/TLSSNISupport.h
@@ -61,11 +61,11 @@ public:
std::optional<uint32_t> http2_buffer_water_mark;
std::optional<uint32_t> server_max_early_data;
std::optional<uint32_t> http2_initial_window_size_in;
- std::optional<uint32_t> http2_max_settings_frames_per_minute;
- std::optional<uint32_t> http2_max_ping_frames_per_minute;
- std::optional<uint32_t> http2_max_priority_frames_per_minute;
- std::optional<uint32_t> http2_max_rst_stream_frames_per_minute;
- std::optional<uint32_t> http2_max_continuation_frames_per_minute;
+ std::optional<int32_t> http2_max_settings_frames_per_minute;
+ std::optional<int32_t> http2_max_ping_frames_per_minute;
+ std::optional<int32_t> http2_max_priority_frames_per_minute;
+ std::optional<int32_t> http2_max_rst_stream_frames_per_minute;
+ std::optional<int32_t> http2_max_continuation_frames_per_minute;
std::optional<std::string_view> outbound_sni_policy;
} hints_from_sni;
diff --git a/include/proxy/http2/HTTP2.h b/include/proxy/http2/HTTP2.h
index 11c60ec35f..9f3c62b97e 100644
--- a/include/proxy/http2/HTTP2.h
+++ b/include/proxy/http2/HTTP2.h
@@ -104,6 +104,7 @@ struct Http2StatsBlock {
Metrics::Counter::AtomicType *max_priority_frames_per_minute_exceeded;
Metrics::Counter::AtomicType *max_rst_stream_frames_per_minute_exceeded;
Metrics::Counter::AtomicType *max_continuation_frames_per_minute_exceeded;
+ Metrics::Counter::AtomicType *max_empty_frames_per_minute_exceeded;
Metrics::Counter::AtomicType *insufficient_avg_window_update;
Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_in;
Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_out;
@@ -423,13 +424,14 @@ public:
static float stream_error_rate_threshold;
static uint32_t stream_error_sampling_threshold;
- static uint32_t max_settings_per_frame;
- static uint32_t max_settings_per_minute;
- static uint32_t max_settings_frames_per_minute;
- static uint32_t max_ping_frames_per_minute;
- static uint32_t max_priority_frames_per_minute;
- static uint32_t max_rst_stream_frames_per_minute;
- static uint32_t max_continuation_frames_per_minute;
+ static int32_t max_settings_per_frame;
+ static int32_t max_settings_per_minute;
+ static int32_t max_settings_frames_per_minute;
+ static int32_t max_ping_frames_per_minute;
+ static int32_t max_priority_frames_per_minute;
+ static int32_t max_rst_stream_frames_per_minute;
+ static int32_t max_continuation_frames_per_minute;
+ static int32_t max_empty_frames_per_minute;
static float min_avg_window_update;
static uint32_t con_slow_log_threshold;
static uint32_t stream_slow_log_threshold;
diff --git a/include/proxy/http2/Http2ConnectionState.h
b/include/proxy/http2/Http2ConnectionState.h
index 0681fddeed..289918754c 100644
--- a/include/proxy/http2/Http2ConnectionState.h
+++ b/include/proxy/http2/Http2ConnectionState.h
@@ -201,6 +201,8 @@ public:
uint32_t get_received_rst_stream_frame_count();
void increment_received_continuation_frame_count();
uint32_t get_received_continuation_frame_count();
+ void increment_received_empty_frame_count();
+ uint32_t get_received_empty_frame_count();
ssize_t get_peer_rwnd() const;
Http2ErrorCode increment_peer_rwnd(size_t amount);
@@ -336,6 +338,7 @@ private:
FrequencyCounter _received_priority_frame_counter;
FrequencyCounter _received_rst_stream_frame_counter;
FrequencyCounter _received_continuation_frame_counter;
+ FrequencyCounter _received_empty_frame_counter;
/** Records the various settings for each SETTINGS frame that we've sent.
*
@@ -405,11 +408,12 @@ private:
Event *_data_event = nullptr;
Event *retransmit_event = nullptr;
- uint32_t configured_max_settings_frames_per_minute = 0;
- uint32_t configured_max_ping_frames_per_minute = 0;
- uint32_t configured_max_priority_frames_per_minute = 0;
- uint32_t configured_max_rst_stream_frames_per_minute = 0;
- uint32_t configured_max_continuation_frames_per_minute = 0;
+ int32_t configured_max_settings_frames_per_minute = 0;
+ int32_t configured_max_ping_frames_per_minute = 0;
+ int32_t configured_max_priority_frames_per_minute = 0;
+ int32_t configured_max_rst_stream_frames_per_minute = 0;
+ int32_t configured_max_continuation_frames_per_minute = 0;
+ int32_t configured_max_empty_frames_per_minute = 0;
};
///////////////////////////////////////////////
diff --git a/src/proxy/http2/HTTP2.cc b/src/proxy/http2/HTTP2.cc
index a28284588d..8323b05352 100644
--- a/src/proxy/http2/HTTP2.cc
+++ b/src/proxy/http2/HTTP2.cc
@@ -486,13 +486,14 @@ uint32_t Http2::no_activity_timeout_out
= 120;
float Http2::stream_error_rate_threshold = 0.1;
uint32_t Http2::stream_error_sampling_threshold = 10;
-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;
-uint32_t Http2::max_continuation_frames_per_minute = 120;
+int32_t Http2::max_settings_per_frame = 7;
+int32_t Http2::max_settings_per_minute = 14;
+int32_t Http2::max_settings_frames_per_minute = 14;
+int32_t Http2::max_ping_frames_per_minute = 60;
+int32_t Http2::max_priority_frames_per_minute = 120;
+int32_t Http2::max_rst_stream_frames_per_minute = 200;
+int32_t Http2::max_continuation_frames_per_minute = 120;
+int32_t Http2::max_empty_frames_per_minute = 0;
float Http2::min_avg_window_update = 2560.0;
uint32_t Http2::con_slow_log_threshold = 0;
uint32_t Http2::stream_slow_log_threshold = 0;
@@ -544,13 +545,14 @@ Http2::init()
REC_EstablishStaticConfigInt32U(zombie_timeout_in,
"proxy.config.http2.zombie_debug_timeout_in");
REC_EstablishStaticConfigFloat(stream_error_rate_threshold,
"proxy.config.http2.stream_error_rate_threshold");
REC_EstablishStaticConfigInt32U(stream_error_sampling_threshold,
"proxy.config.http2.stream_error_sampling_threshold");
- REC_EstablishStaticConfigInt32U(max_settings_per_frame,
"proxy.config.http2.max_settings_per_frame");
- REC_EstablishStaticConfigInt32U(max_settings_per_minute,
"proxy.config.http2.max_settings_per_minute");
- 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_EstablishStaticConfigInt32U(max_continuation_frames_per_minute,
"proxy.config.http2.max_continuation_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_settings_per_frame,
"proxy.config.http2.max_settings_per_frame");
+ REC_EstablishStaticConfigInt32(max_settings_per_minute,
"proxy.config.http2.max_settings_per_minute");
+ REC_EstablishStaticConfigInt32(max_settings_frames_per_minute,
"proxy.config.http2.max_settings_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_ping_frames_per_minute,
"proxy.config.http2.max_ping_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_priority_frames_per_minute,
"proxy.config.http2.max_priority_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_rst_stream_frames_per_minute,
"proxy.config.http2.max_rst_stream_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_continuation_frames_per_minute,
"proxy.config.http2.max_continuation_frames_per_minute");
+ REC_EstablishStaticConfigInt32(max_empty_frames_per_minute,
"proxy.config.http2.max_empty_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");
@@ -607,6 +609,8 @@ Http2::init()
Metrics::Counter::createPtr("proxy.process.http2.max_rst_stream_frames_per_minute_exceeded");
http2_rsb.max_continuation_frames_per_minute_exceeded =
Metrics::Counter::createPtr("proxy.process.http2.max_continuation_frames_per_minute_exceeded");
+ http2_rsb.max_empty_frames_per_minute_exceeded =
+
Metrics::Counter::createPtr("proxy.process.http2.max_empty_frames_per_minute_exceeded");
http2_rsb.insufficient_avg_window_update =
Metrics::Counter::createPtr("proxy.process.http2.insufficient_avg_window_update");
http2_rsb.max_concurrent_streams_exceeded_in =
Metrics::Counter::createPtr("proxy.process.http2.max_concurrent_streams_exceeded_in");
diff --git a/src/proxy/http2/Http2ConnectionState.cc
b/src/proxy/http2/Http2ConnectionState.cc
index d4e1ac6f2d..affbf0b813 100644
--- a/src/proxy/http2/Http2ConnectionState.cc
+++ b/src/proxy/http2/Http2ConnectionState.cc
@@ -184,8 +184,19 @@ Http2ConnectionState::rcv_data_frame(const Http2Frame
&frame)
stream->set_trailing_header_is_possible();
}
- // If payload length is 0 without END_STREAM flag, do nothing
- if (payload_length == 0 && !stream->receive_end_stream) {
+ // If payload length is 0 without END_STREAM flag, just count it
+ const uint32_t unpadded_length = payload_length - pad_length;
+ if (unpadded_length == 0 && !stream->receive_end_stream) {
+ this->increment_received_empty_frame_count();
+ if (configured_max_empty_frames_per_minute >= 0 &&
+ this->get_received_empty_frame_count() >
static_cast<uint32_t>(configured_max_empty_frames_per_minute)) {
+
Metrics::Counter::increment(http2_rsb.max_empty_frames_per_minute_exceeded);
+ Http2StreamDebug(this->session, id, "Observed too many empty DATA
frames: %u within the last minute",
+ this->get_received_empty_frame_count());
+ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION,
Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+ "recv data too frequent empty frame");
+ }
+
return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
}
@@ -211,8 +222,7 @@ Http2ConnectionState::rcv_data_frame(const Http2Frame
&frame)
this->get_local_rwnd(), session_window,
stream->get_local_rwnd(), stream_window);
}
- const uint32_t unpadded_length = payload_length - pad_length;
- MIOBuffer *writer = stream->read_vio_writer();
+ MIOBuffer *writer = stream->read_vio_writer();
if (writer == nullptr) {
return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM,
Http2ErrorCode::HTTP2_ERROR_INTERNAL_ERROR, "no writer");
}
@@ -573,8 +583,8 @@ Http2ConnectionState::rcv_priority_frame(const Http2Frame
&frame)
// Update PRIORITY frame count per minute
this->increment_received_priority_frame_count();
// Close this connection if its priority frame count received exceeds a limit
- if (configured_max_priority_frames_per_minute != 0 &&
- this->get_received_priority_frame_count() >
configured_max_priority_frames_per_minute) {
+ if (configured_max_priority_frames_per_minute >= 0 &&
+ this->get_received_priority_frame_count() >
static_cast<uint32_t>(configured_max_priority_frames_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_priority_frames_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent priority
changes: %u priority changes within a last minute",
this->get_received_priority_frame_count());
@@ -648,8 +658,8 @@ Http2ConnectionState::rcv_rst_stream_frame(const Http2Frame
&frame)
// Update RST_STREAM frame count per minute
this->increment_received_rst_stream_frame_count();
// Close this connection if its RST_STREAM frame count exceeds a limit
- if (configured_max_rst_stream_frames_per_minute != 0 &&
- this->get_received_rst_stream_frame_count() >
configured_max_rst_stream_frames_per_minute) {
+ if (configured_max_rst_stream_frames_per_minute >= 0 &&
+ this->get_received_rst_stream_frame_count() >
static_cast<uint32_t>(configured_max_rst_stream_frames_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_rst_stream_frames_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent
RST_STREAM frames: %u frames within a last minute",
this->get_received_rst_stream_frame_count());
@@ -697,8 +707,8 @@ Http2ConnectionState::rcv_settings_frame(const Http2Frame
&frame)
// Update SETTINGS frame count per minute
this->increment_received_settings_frame_count();
// Close this connection if its SETTINGS frame count exceeds a limit
- if (configured_max_settings_frames_per_minute != 0 &&
- this->get_received_settings_frame_count() >
configured_max_settings_frames_per_minute) {
+ if (configured_max_settings_frames_per_minute >= 0 &&
+ this->get_received_settings_frame_count() >
static_cast<uint32_t>(configured_max_settings_frames_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_settings_frames_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent SETTINGS
frames: %u frames within a last minute",
this->get_received_settings_frame_count());
@@ -738,7 +748,7 @@ Http2ConnectionState::rcv_settings_frame(const Http2Frame
&frame)
uint32_t n_settings = 0;
while (nbytes < frame.header().length) {
- if (n_settings >= Http2::max_settings_per_frame) {
+ if (Http2::max_settings_per_frame >= 0 && n_settings >=
static_cast<uint32_t>(Http2::max_settings_per_frame)) {
Metrics::Counter::increment(http2_rsb.max_settings_per_frame_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too many settings
in a frame");
return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION,
Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
@@ -779,7 +789,8 @@ Http2ConnectionState::rcv_settings_frame(const Http2Frame
&frame)
// Update settings count per minute
this->increment_received_settings_count(n_settings);
// Close this connection if its settings count received exceeds a limit
- if (this->get_received_settings_count() > Http2::max_settings_per_minute) {
+ if (Http2::max_settings_per_frame >= 0 &&
+ this->get_received_settings_count() >
static_cast<uint32_t>(Http2::max_settings_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_settings_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent setting
changes: %u settings within a last minute",
this->get_received_settings_count());
@@ -833,7 +844,8 @@ Http2ConnectionState::rcv_ping_frame(const Http2Frame
&frame)
// Update PING frame count per minute
this->increment_received_ping_frame_count();
// Close this connection if its ping count received exceeds a limit
- if (configured_max_ping_frames_per_minute != 0 &&
this->get_received_ping_frame_count() > configured_max_ping_frames_per_minute) {
+ if (configured_max_ping_frames_per_minute >= 0 &&
+ this->get_received_ping_frame_count() >
static_cast<uint32_t>(configured_max_ping_frames_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_ping_frames_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent PING
frames: %u PING frames within a last minute",
this->get_received_ping_frame_count());
@@ -1002,6 +1014,18 @@ Http2ConnectionState::rcv_continuation_frame(const
Http2Frame &frame)
"continuation bad client id");
}
+ if (payload_length == 0 && (frame.header().flags &
HTTP2_FLAGS_HEADERS_END_HEADERS) == 0x0) {
+ this->increment_received_empty_frame_count();
+ if (configured_max_empty_frames_per_minute >= 0 &&
+ this->get_received_empty_frame_count() >
static_cast<uint32_t>(configured_max_empty_frames_per_minute)) {
+
Metrics::Counter::increment(http2_rsb.max_empty_frames_per_minute_exceeded);
+ Http2StreamDebug(this->session, stream_id, "Observed too many empty
CONTINUATION frames: %u within the last minute",
+ this->get_received_empty_frame_count());
+ return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION,
Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+ "recv continuation too frequent empty frame");
+ }
+ }
+
// Find opened stream
// CONTINUATION frames MUST be associated with a stream. If a
// CONTINUATION frame is received whose stream identifier field is 0x0,
@@ -1033,7 +1057,7 @@ Http2ConnectionState::rcv_continuation_frame(const
Http2Frame &frame)
this->increment_received_continuation_frame_count();
// Close this connection if its CONTINUATION frame count exceeds a limit.
if (configured_max_continuation_frames_per_minute != 0 &&
- this->get_received_continuation_frame_count() >
configured_max_continuation_frames_per_minute) {
+ this->get_received_continuation_frame_count() >
static_cast<uint32_t>(configured_max_continuation_frames_per_minute)) {
Metrics::Counter::increment(http2_rsb.max_continuation_frames_per_minute_exceeded);
Http2StreamDebug(this->session, stream_id, "Observed too frequent
CONTINUATION frames: %u frames within a last minute",
this->get_received_continuation_frame_count());
@@ -1287,6 +1311,8 @@ Http2ConnectionState::init(Http2CommonSession *ssn)
configured_max_priority_frames_per_minute =
Http2::max_priority_frames_per_minute;
configured_max_rst_stream_frames_per_minute =
Http2::max_rst_stream_frames_per_minute;
configured_max_continuation_frames_per_minute =
Http2::max_continuation_frames_per_minute;
+ configured_max_empty_frames_per_minute =
Http2::max_empty_frames_per_minute;
+
if (auto snis = session->get_netvc()->get_service<TLSSNISupport>(); snis) {
if (snis->hints_from_sni.http2_max_settings_frames_per_minute.has_value())
{
configured_max_settings_frames_per_minute =
snis->hints_from_sni.http2_max_settings_frames_per_minute.value();
@@ -2798,6 +2824,18 @@
Http2ConnectionState::get_received_continuation_frame_count()
return this->_received_continuation_frame_counter.get_count();
}
+void
+Http2ConnectionState::increment_received_empty_frame_count()
+{
+ this->_received_empty_frame_counter.increment();
+}
+
+uint32_t
+Http2ConnectionState::get_received_empty_frame_count()
+{
+ return this->_received_empty_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
diff --git a/src/records/RecordsConfig.cc b/src/records/RecordsConfig.cc
index 0ab8a09e02..0bf968740a 100644
--- a/src/records/RecordsConfig.cc
+++ b/src/records/RecordsConfig.cc
@@ -1306,17 +1306,19 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.http2.stream_error_sampling_threshold",
RECD_INT, "10", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.http2.max_settings_per_frame", RECD_INT, "7",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ {RECT_CONFIG, "proxy.config.http2.max_settings_per_frame", RECD_INT, "7",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.http2.max_settings_per_minute", RECD_INT, "14",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ {RECT_CONFIG, "proxy.config.http2.max_settings_per_minute", RECD_INT, "14",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.http2.max_settings_frames_per_minute", RECD_INT,
"14", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ {RECT_CONFIG, "proxy.config.http2.max_settings_frames_per_minute", RECD_INT,
"14", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.http2.max_ping_frames_per_minute", RECD_INT,
"60", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ {RECT_CONFIG, "proxy.config.http2.max_ping_frames_per_minute", RECD_INT,
"60", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
,
- {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_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.max_rst_stream_frames_per_minute",
RECD_INT, "200", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
+ ,
+ {RECT_CONFIG, "proxy.config.http2.max_empty_frames_per_minute", RECD_INT,
"0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http2.max_continuation_frames_per_minute",
RECD_INT, "120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,