This is an automated email from the ASF dual-hosted git repository.
masaori 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 b46f5d061a Add http2.incomplete_header_timeout_in (#11354)
b46f5d061a is described below
commit b46f5d061af455dc75956346da3308788381b30d
Author: Masaori Koshiba <[email protected]>
AuthorDate: Fri May 24 08:00:11 2024 +0900
Add http2.incomplete_header_timeout_in (#11354)
---
doc/admin-guide/files/records.yaml.en.rst | 7 +++++++
include/proxy/http2/HTTP2.h | 1 +
include/proxy/http2/Http2CommonSession.h | 1 +
src/proxy/http2/HTTP2.cc | 33 +++++++++++++++++--------------
src/proxy/http2/Http2ConnectionState.cc | 24 ++++++++++++++++++++++
src/proxy/http2/Http2Stream.cc | 29 ++++++++++++++++++++++++++-
src/records/RecordsConfig.cc | 2 ++
7 files changed, 81 insertions(+), 16 deletions(-)
diff --git a/doc/admin-guide/files/records.yaml.en.rst
b/doc/admin-guide/files/records.yaml.en.rst
index ba94d190b7..00fde245e1 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -4500,6 +4500,13 @@ HTTP/2 Configuration
Specifies how long |TS| keeps connections to origins open if a
transaction stalls.
+.. ts:cv:: CONFIG proxy.config.http2.incomplete_header_timeout_in INT 10
+ :reloadable:
+ :units: seconds
+
+ Specifies how long |TS| keeps streams to clients open after they start
sending HTTP headers. If a client doesn't send all
+ headers within this time, the stream and connection will be closed.
+
.. ts:cv:: CONFIG proxy.config.http2.zombie_debug_timeout_in INT 0
:reloadable:
diff --git a/include/proxy/http2/HTTP2.h b/include/proxy/http2/HTTP2.h
index 5cabf2b2cd..11c60ec35f 100644
--- a/include/proxy/http2/HTTP2.h
+++ b/include/proxy/http2/HTTP2.h
@@ -410,6 +410,7 @@ public:
static uint32_t accept_no_activity_timeout;
static uint32_t no_activity_timeout_in;
static uint32_t active_timeout_in;
+ static uint32_t incomplete_header_timeout_in;
static uint32_t push_diary_size;
static uint32_t zombie_timeout_in;
diff --git a/include/proxy/http2/Http2CommonSession.h
b/include/proxy/http2/Http2CommonSession.h
index 5507a3a07f..cb61107fe9 100644
--- a/include/proxy/http2/Http2CommonSession.h
+++ b/include/proxy/http2/Http2CommonSession.h
@@ -47,6 +47,7 @@
#define HTTP2_SESSION_EVENT_SHUTDOWN_INIT (HTTP2_SESSION_EVENTS_START + 7)
#define HTTP2_SESSION_EVENT_SHUTDOWN_CONT (HTTP2_SESSION_EVENTS_START + 8)
#define HTTP2_SESSION_EVENT_REENABLE (HTTP2_SESSION_EVENTS_START + 9)
+#define HTTP2_SESSION_EVENT_ERROR (HTTP2_SESSION_EVENTS_START + 10)
enum class Http2SessionCod : int {
NOT_PROVIDED,
diff --git a/src/proxy/http2/HTTP2.cc b/src/proxy/http2/HTTP2.cc
index 3de6bbab04..646da22050 100644
--- a/src/proxy/http2/HTTP2.cc
+++ b/src/proxy/http2/HTTP2.cc
@@ -459,21 +459,23 @@ http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t
*buf_start, const uint32_
}
// 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_in = 65535;
-Http2FlowControlPolicy Http2::flow_control_policy_in =
Http2FlowControlPolicy::STATIC_SESSION_AND_STATIC_STREAM;
-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;
+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_in = 65535;
+Http2FlowControlPolicy Http2::flow_control_policy_in =
Http2FlowControlPolicy::STATIC_SESSION_AND_STATIC_STREAM;
+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::incomplete_header_timeout_in = 10;
+uint32_t Http2::push_diary_size = 256;
+uint32_t Http2::zombie_timeout_in = 0;
uint32_t Http2::max_concurrent_streams_out = 100;
uint32_t Http2::min_concurrent_streams_out = 10;
@@ -537,6 +539,7 @@ Http2::init()
REC_EstablishStaticConfigInt32U(no_activity_timeout_in,
"proxy.config.http2.no_activity_timeout_in");
REC_EstablishStaticConfigInt32U(no_activity_timeout_out,
"proxy.config.http2.no_activity_timeout_out");
REC_EstablishStaticConfigInt32U(active_timeout_in,
"proxy.config.http2.active_timeout_in");
+ REC_EstablishStaticConfigInt32U(incomplete_header_timeout_in,
"proxy.config.http2.incomplete_header_timeout_in");
REC_EstablishStaticConfigInt32U(push_diary_size,
"proxy.config.http2.push_diary_size");
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");
diff --git a/src/proxy/http2/Http2ConnectionState.cc
b/src/proxy/http2/Http2ConnectionState.cc
index 4baaa52b52..ae739e9c3a 100644
--- a/src/proxy/http2/Http2ConnectionState.cc
+++ b/src/proxy/http2/Http2ConnectionState.cc
@@ -36,6 +36,7 @@
#include "iocore/net/TLSSNISupport.h"
#include "tscore/ink_assert.h"
+#include "tscore/ink_hrtime.h"
#include "tscore/ink_memory.h"
#include "tsutil/PostScript.h"
#include "tsutil/LocalBuffer.h"
@@ -493,6 +494,7 @@ Http2ConnectionState::rcv_headers_frame(const Http2Frame
&frame)
if (!stream->is_outbound_connection() &&
!stream->trailing_header_is_possible()) {
SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread());
stream->mark_milestone(Http2StreamMilestone::START_TXN);
+ stream->cancel_active_timeout();
stream->new_transaction(frame.is_from_early_data());
// Send request header to SM
stream->send_headers(*this);
@@ -1518,6 +1520,23 @@ Http2ConnectionState::main_event_handler(int event, void
*edata)
this->session->flush();
} break;
+ case HTTP2_SESSION_EVENT_ERROR: {
+ REMEMBER(event, this->recursion);
+
+ Http2ErrorCode error_code = Http2ErrorCode::HTTP2_ERROR_INTERNAL_ERROR;
+ if (edata != nullptr) {
+ Http2Error *error = static_cast<Http2Error *>(edata);
+ error_code = error->code;
+ }
+
+ SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
+ this->send_goaway_frame(this->latest_streamid_in, error_code);
+ this->session->set_half_close_local_flag(true);
+ if (fini_event == nullptr) {
+ this->fini_event =
this_ethread()->schedule_imm_local(static_cast<Continuation *>(this),
HTTP2_SESSION_EVENT_FINI);
+ }
+ } break;
+
// Initiate a graceful shutdown
case HTTP2_SESSION_EVENT_SHUTDOWN_INIT: {
REMEMBER(event, this->recursion);
@@ -1820,6 +1839,11 @@ Http2ConnectionState::create_stream(Http2StreamId
new_id, Http2Error &error)
}
increment_stream_requests();
+ // Set incomplete header timeout
+ // Client should send END_HEADERS flag within the
http2.incomplete_header_timeout_in.
+ // The active timeout of this stream will be reset by HttpSM with
http.transction_active_timeout_in when a HTTP TXN is started
+
new_stream->set_active_timeout(HRTIME_SECONDS(Http2::incomplete_header_timeout_in));
+
// Clear the session timeout. Let the transaction timeouts reign
session->get_proxy_session()->cancel_inactivity_timeout();
session->get_proxy_session()->set_session_active();
diff --git a/src/proxy/http2/Http2Stream.cc b/src/proxy/http2/Http2Stream.cc
index 2540b7b2b2..44d50e703b 100644
--- a/src/proxy/http2/Http2Stream.cc
+++ b/src/proxy/http2/Http2Stream.cc
@@ -28,6 +28,7 @@
#include "proxy/http2/Http2ServerSession.h"
#include "proxy/http/HttpDebugNames.h"
#include "proxy/http/HttpSM.h"
+#include "tscore/Diags.h"
#include "tscore/HTTPVersion.h"
#include "tscore/ink_assert.h"
@@ -196,10 +197,36 @@ Http2Stream::main_event_handler(int event, void *edata)
switch (event) {
case VC_EVENT_ACTIVE_TIMEOUT:
case VC_EVENT_INACTIVITY_TIMEOUT:
- if (_sm && read_vio.ntodo() > 0) {
+ if (_sm == nullptr && closed != true) {
+ // TIMEOUT without HttpSM - assuming incomplete header timeout
+ Http2StreamDebug("timeout event=%d", event);
+
+ ip_port_text_buffer ipb;
+ const char *remote_ip =
ats_ip_ntop(this->_proxy_ssn->get_remote_addr(), ipb, sizeof(ipb));
+
+ Error("HTTP/2 stream error timeout remote_ip=%s session_id=%" PRId64 "
stream_id=%u event=%d", remote_ip,
+ this->_proxy_ssn->connection_id(), this->_id, event);
+
+ // Close stream
+ do_io_close();
+ terminate_stream = true;
+
+ // Close connection because this stream doesn't read
HEADERS/CONTINUATION frames anymore that makes HPACK dynamic table
+ // out of sync.
+ Http2ConnectionState &connection_state = get_connection_state();
+ {
+ SCOPED_MUTEX_LOCK(lock, connection_state.mutex, this_ethread());
+ Http2Error error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION,
Http2ErrorCode::HTTP2_ERROR_COMPRESSION_ERROR,
+ "stream timeout");
+ connection_state.handleEvent(HTTP2_SESSION_EVENT_ERROR, &error);
+ }
+ } else if (_sm && read_vio.ntodo() > 0) {
this->signal_read_event(event);
} else if (_sm && write_vio.ntodo() > 0) {
this->signal_write_event(event);
+ } else {
+ Warning("HTTP/2 unknown case of %d event - session_id=%" PRId64 "
stream_id=%u", event, this->_proxy_ssn->connection_id(),
+ this->_id);
}
break;
case VC_EVENT_WRITE_READY:
diff --git a/src/records/RecordsConfig.cc b/src/records/RecordsConfig.cc
index d80af3aff1..4673cdebc6 100644
--- a/src/records/RecordsConfig.cc
+++ b/src/records/RecordsConfig.cc
@@ -1294,6 +1294,8 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.http2.active_timeout_in", RECD_INT, "0",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.http2.incomplete_header_timeout_in", RECD_INT,
"10", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+ ,
{RECT_CONFIG, "proxy.config.http2.push_diary_size", RECD_INT, "256",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http2.zombie_debug_timeout_in", RECD_INT, "0",
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}