From ca29cd20a2788a454387cca0c33b19c552ab4443 Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Tue, 22 May 2018 13:22:19 -0500 Subject: [PATCH] Add H2 session level inactivity timer --- proxy/http2/Http2ClientSession.cc | 47 +++++++++++++++++++++++++++++ proxy/http2/Http2ClientSession.h | 8 +++++ proxy/http2/Http2ConnectionState.cc | 16 ++++++++++ 3 files changed, 71 insertions(+) diff --git a/proxy/http2/Http2ClientSession.cc b/proxy/http2/Http2ClientSession.cc index 277c1df2cfb..6064b7c5836 100644 --- a/proxy/http2/Http2ClientSession.cc +++ b/proxy/http2/Http2ClientSession.cc @@ -66,6 +66,12 @@ Http2ClientSession::destroy() if (!in_destroy) { in_destroy = true; Http2SsnDebug("session destroy"); + + // Clear the H2 level inactivity tracking + // before going in the session callbacks with + // their different handlers + this->clear_inactive_timer(); + // Let everyone know we are going down do_api_callout(TS_HTTP_SSN_CLOSE_HOOK); } @@ -155,6 +161,36 @@ Http2ClientSession::start() this->handleEvent(VC_EVENT_READ_READY, read_vio); } +void +Http2ClientSession::set_inactivity_timeout(ink_hrtime timeout_in) +{ + inactive_timeout = timeout_in; + if (inactive_timeout > 0) { + inactive_timeout_at = Thread::get_hrtime() + inactive_timeout; + if (!inactive_event) { + inactive_event = this_ethread()->schedule_every(this, HRTIME_SECONDS(1)); + } else { + clear_inactive_timer(); + } + } +} + +void +Http2ClientSession::reset_inactivity_timeout() +{ + set_inactivity_timeout(inactive_timeout); +} + +void +Http2ClientSession::clear_inactive_timer() +{ + inactive_timeout_at = 0; + if (inactive_event) { + inactive_event->cancel(); + inactive_event = nullptr; + } +} + void Http2ClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOBufferReader *reader, bool backdoor) { @@ -169,6 +205,7 @@ Http2ClientSession::new_connection(NetVConnection *new_vc, MIOBuffer *iobuf, IOB this->con_id = ProxyClientSession::next_connection_id(); this->client_vc = new_vc; client_vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::accept_no_activity_timeout)); + this->set_inactivity_timeout(HRTIME_SECONDS(Http2::accept_no_activity_timeout)); this->schedule_event = nullptr; this->mutex = new_vc->mutex; this->in_destroy = false; @@ -303,6 +340,14 @@ Http2ClientSession::main_event_handler(int event, void *edata) Event *e = static_cast(edata); if (e == schedule_event) { schedule_event = nullptr; + } else if (e == inactive_event) { + if (inactive_timeout_at && inactive_timeout_at < Thread::get_hrtime()) { + event = VC_EVENT_INACTIVITY_TIMEOUT; + clear_inactive_timer(); + } else { + // Not timed out + event = VC_EVENT_NONE; + } } switch (event) { @@ -325,6 +370,7 @@ Http2ClientSession::main_event_handler(int event, void *edata) case VC_EVENT_INACTIVITY_TIMEOUT: case VC_EVENT_ERROR: case VC_EVENT_EOS: + this->set_dying_event(event); this->do_io_close(); retval = 0; break; @@ -391,6 +437,7 @@ Http2ClientSession::state_read_connection_preface(int event, void *edata) HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read); client_vc->set_inactivity_timeout(HRTIME_SECONDS(Http2::no_activity_timeout_in)); + this->set_inactivity_timeout(HRTIME_SECONDS(Http2::no_activity_timeout_in)); client_vc->set_active_timeout(HRTIME_SECONDS(Http2::active_timeout_in)); // XXX start the write VIO ... diff --git a/proxy/http2/Http2ClientSession.h b/proxy/http2/Http2ClientSession.h index 4f69dc93f68..2c521541238 100644 --- a/proxy/http2/Http2ClientSession.h +++ b/proxy/http2/Http2ClientSession.h @@ -322,6 +322,10 @@ class Http2ClientSession : public ProxyClientSession Http2ClientSession(Http2ClientSession &) = delete; Http2ClientSession &operator=(const Http2ClientSession &) = delete; + void set_inactivity_timeout(ink_hrtime timeout_in) override; + void clear_inactive_timer(); + void reset_inactivity_timeout(); + private: int main_event_handler(int, void *); @@ -356,6 +360,10 @@ class Http2ClientSession : public ProxyClientSession bool half_close_local = false; int recursion = 0; + ink_hrtime inactive_timeout = 0; + ink_hrtime inactive_timeout_at = 0; + Event *inactive_event = nullptr; + InkHashTable *h2_pushed_urls = nullptr; uint32_t h2_pushed_urls_size = 0; }; diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc index 4191a90ffcb..b09208dfe92 100644 --- a/proxy/http2/Http2ConnectionState.cc +++ b/proxy/http2/Http2ConnectionState.cc @@ -126,6 +126,8 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame) } } + cstate.ua_session->reset_inactivity_timeout(); + // If Data length is 0, do nothing. if (payload_length == 0) { return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); @@ -225,6 +227,8 @@ rcv_headers_frame(Http2ConnectionState &cstate, const Http2Frame &frame) } } + cstate.ua_session->reset_inactivity_timeout(); + // keep track of how many bytes we get in the frame stream->request_header_length += payload_length; if (stream->request_header_length > Http2::max_request_header_size) { @@ -363,6 +367,8 @@ rcv_priority_frame(Http2ConnectionState &cstate, const Http2Frame &frame) Http2StreamDebug(cstate.ua_session, stream_id, "Received PRIORITY frame"); + cstate.ua_session->reset_inactivity_timeout(); + // If a PRIORITY frame is received with a stream identifier of 0x0, the // recipient MUST respond with a connection error of type PROTOCOL_ERROR. if (stream_id == 0) { @@ -488,6 +494,8 @@ rcv_settings_frame(Http2ConnectionState &cstate, const Http2Frame &frame) Http2StreamDebug(cstate.ua_session, stream_id, "Received SETTINGS frame"); + cstate.ua_session->reset_inactivity_timeout(); + // [RFC 7540] 6.5. The stream identifier for a SETTINGS frame MUST be zero. // If an endpoint receives a SETTINGS frame whose stream identifier field is // anything other than 0x0, the endpoint MUST respond with a connection @@ -575,6 +583,8 @@ rcv_ping_frame(Http2ConnectionState &cstate, const Http2Frame &frame) Http2StreamDebug(cstate.ua_session, stream_id, "Received PING frame"); + // Not resetting the inactivity timer for client initiated ping + // If a PING frame is received with a stream identifier field value other // than 0x0, the recipient MUST respond with a connection error of type // PROTOCOL_ERROR. @@ -644,6 +654,8 @@ rcv_window_update_frame(Http2ConnectionState &cstate, const Http2Frame &frame) uint32_t size; const Http2StreamId stream_id = frame.header().streamid; + cstate.ua_session->reset_inactivity_timeout(); + // A WINDOW_UPDATE frame with a length other than 4 octets MUST be // treated as a connection error of type FRAME_SIZE_ERROR. if (frame.header().length != HTTP2_WINDOW_UPDATE_LEN) { @@ -746,6 +758,8 @@ rcv_continuation_frame(Http2ConnectionState &cstate, const Http2Frame &frame) "continuation bad client id"); } + cstate.ua_session->reset_inactivity_timeout(); + // Find opened stream // CONTINUATION frames MUST be associated with a stream. If a // CONTINUATION frame is received whose stream identifier field is 0x0, @@ -1297,6 +1311,7 @@ Http2ConnectionState::send_data_frames_depends_on_priority() break; } + ua_session->reset_inactivity_timeout(); this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_XMIT); return; } @@ -1367,6 +1382,7 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len // xmit event SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); + ua_session->reset_inactivity_timeout(); this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data); if (flags & HTTP2_FLAGS_DATA_END_STREAM) {