This is an automated email from the ASF dual-hosted git repository.
eze pushed a commit to branch 9.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/9.2.x by this push:
new 864dbbd2f6 Add http2.incomplete_header_timeout_in (#11354) (#11973)
864dbbd2f6 is described below
commit 864dbbd2f6579f4dae6073ecef334e83ef3fa1ea
Author: Masaori Koshiba <[email protected]>
AuthorDate: Wed Jan 22 02:22:52 2025 +0900
Add http2.incomplete_header_timeout_in (#11354) (#11973)
(cherry picked from commit b46f5d061af455dc75956346da3308788381b30d)
Conflicts:
doc/admin-guide/files/records.config.en.rst
proxy/http2/HTTP2.h
proxy/http2/Http2CommonSession.h
proxy/http2/Http2ConnectionState.cc
proxy/http2/Http2Stream.cc
src/proxy/http2/HTTP2.cc
---
doc/admin-guide/files/records.config.en.rst | 7 +++++++
mgmt/RecordsConfig.cc | 2 ++
proxy/http2/HTTP2.cc | 2 ++
proxy/http2/HTTP2.h | 1 +
proxy/http2/Http2CommonSession.h | 1 +
proxy/http2/Http2ConnectionState.cc | 26 ++++++++++++++++++++++++++
proxy/http2/Http2Stream.cc | 29 ++++++++++++++++++++++++++++-
7 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/doc/admin-guide/files/records.config.en.rst
b/doc/admin-guide/files/records.config.en.rst
index 15f31c08c8..b81510db69 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -4232,6 +4232,13 @@ HTTP/2 Configuration
misconfigured or misbehaving clients are opening a large number of
connections without submitting requests.
+.. 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/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 8940ab2263..ff7fdc0e3c 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1377,6 +1377,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}
diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc
index a3a5a0ac78..4da0ec6507 100644
--- a/proxy/http2/HTTP2.cc
+++ b/proxy/http2/HTTP2.cc
@@ -812,6 +812,7 @@ 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;
float Http2::stream_error_rate_threshold = 0.1;
@@ -846,6 +847,7 @@ Http2::init()
REC_EstablishStaticConfigInt32U(accept_no_activity_timeout,
"proxy.config.http2.accept_no_activity_timeout");
REC_EstablishStaticConfigInt32U(no_activity_timeout_in,
"proxy.config.http2.no_activity_timeout_in");
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/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h
index 857b199c05..815326ec07 100644
--- a/proxy/http2/HTTP2.h
+++ b/proxy/http2/HTTP2.h
@@ -395,6 +395,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;
static float stream_error_rate_threshold;
diff --git a/proxy/http2/Http2CommonSession.h b/proxy/http2/Http2CommonSession.h
index c59a5d75ee..b3a6fc3970 100644
--- a/proxy/http2/Http2CommonSession.h
+++ b/proxy/http2/Http2CommonSession.h
@@ -41,6 +41,7 @@
#define HTTP2_SESSION_EVENT_SHUTDOWN_INIT (HTTP2_SESSION_EVENTS_START + 5)
#define HTTP2_SESSION_EVENT_SHUTDOWN_CONT (HTTP2_SESSION_EVENTS_START + 6)
#define HTTP2_SESSION_EVENT_REENABLE (HTTP2_SESSION_EVENTS_START + 7)
+#define HTTP2_SESSION_EVENT_ERROR (HTTP2_SESSION_EVENTS_START + 10)
enum class Http2SessionCod : int {
NOT_PROVIDED,
diff --git a/proxy/http2/Http2ConnectionState.cc
b/proxy/http2/Http2ConnectionState.cc
index 63d3ab9caa..1999011659 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -30,6 +30,9 @@
#include "HttpDebugNames.h"
#include "TLSSNISupport.h"
+#include "tscore/ink_assert.h"
+#include "tscore/ink_hrtime.h"
+#include "tscore/ink_memory.h"
#include "tscpp/util/PostScript.h"
#include "tscpp/util/LocalBuffer.h"
@@ -403,6 +406,7 @@ rcv_headers_frame(Http2ConnectionState &cstate, const
Http2Frame &frame)
if (!empty_request) {
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_request(cstate);
@@ -1294,6 +1298,23 @@ Http2ConnectionState::main_event_handler(int event, void
*edata)
_scheduled = false;
} 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);
@@ -1447,6 +1468,11 @@ Http2ConnectionState::create_stream(Http2StreamId
new_id, Http2Error &error)
new_stream->is_first_transaction_flag = get_stream_requests() == 0;
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));
+
return new_stream;
}
diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc
index d019d44ef4..6a4d29584a 100644
--- a/proxy/http2/Http2Stream.cc
+++ b/proxy/http2/Http2Stream.cc
@@ -27,6 +27,7 @@
#include "Http2ClientSession.h"
#include "HttpDebugNames.h"
#include "HttpSM.h"
+#include "tscore/Diags.h"
#include <numeric>
@@ -173,10 +174,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 = static_cast<Http2ClientSession
*>(_proxy_ssn)->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: