This is an automated email from the ASF dual-hosted git repository. bcall pushed a commit to branch 8.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/8.0.x by this push: new 3387fa2 Bug fixes to h2 buffering 3387fa2 is described below commit 3387fa27e47f5e862e40c714edfacb64d1a0e34f Author: Masaori Koshiba <masa...@apache.org> AuthorDate: Thu Apr 9 13:33:03 2020 -0700 Bug fixes to h2 buffering --- doc/admin-guide/files/records.config.en.rst | 2 +- mgmt/RecordsConfig.cc | 2 +- proxy/http2/HTTP2.cc | 2 +- proxy/http2/Http2ClientSession.cc | 12 ++++-- proxy/http2/Http2ClientSession.h | 2 + proxy/http2/Http2ConnectionState.cc | 59 ++++++++++++++++++++--------- proxy/http2/Http2ConnectionState.h | 6 ++- proxy/http2/Http2Stream.cc | 30 +++++++++++++++ proxy/http2/Http2Stream.h | 11 +++--- 9 files changed, 95 insertions(+), 31 deletions(-) diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 3dc98a7..e51ec77 100644 --- a/doc/admin-guide/files/records.config.en.rst +++ b/doc/admin-guide/files/records.config.en.rst @@ -3450,7 +3450,7 @@ HTTP/2 Configuration :ts:cv:`proxy.config.http2.min_concurrent_streams_in`. To disable, set to zero (``0``). -.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 1048576 +.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 65535 :reloadable: The initial window size for inbound connections. diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index df38162..7bf85b9 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -1306,7 +1306,7 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.http2.max_active_streams_in", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} , - {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "1048576", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} , {RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} , diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc index a74415b..b81e1bd 100644 --- a/proxy/http2/HTTP2.cc +++ b/proxy/http2/HTTP2.cc @@ -725,7 +725,7 @@ 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 = 1048576; +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; diff --git a/proxy/http2/Http2ClientSession.cc b/proxy/http2/Http2ClientSession.cc index 8b68db3..1d72da7 100644 --- a/proxy/http2/Http2ClientSession.cc +++ b/proxy/http2/Http2ClientSession.cc @@ -360,11 +360,9 @@ Http2ClientSession::main_event_handler(int event, void *edata) break; case VC_EVENT_WRITE_READY: - retval = 0; - break; - case VC_EVENT_WRITE_COMPLETE: - // Seems as this is being closed already + this->connection_state.restart_streams(); + retval = 0; break; @@ -627,3 +625,9 @@ Http2ClientSession::_should_do_something_else() // Do something else every 128 incoming frames return (this->_n_frame_read & 0x7F) == 0; } + +int64_t +Http2ClientSession::write_avail() +{ + return this->write_buffer->write_avail(); +} diff --git a/proxy/http2/Http2ClientSession.h b/proxy/http2/Http2ClientSession.h index e8b43a5..21ce079 100644 --- a/proxy/http2/Http2ClientSession.h +++ b/proxy/http2/Http2ClientSession.h @@ -316,6 +316,8 @@ public: return write_buffer->max_read_avail(); } + int64_t write_avail(); + // noncopyable Http2ClientSession(Http2ClientSession &) = delete; Http2ClientSession &operator=(const Http2ClientSession &) = delete; diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc index 5fc3a9e..3eaa558 100644 --- a/proxy/http2/Http2ConnectionState.cc +++ b/proxy/http2/Http2ConnectionState.cc @@ -151,6 +151,12 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame) cstate.decrement_server_rwnd(payload_length); stream->decrement_server_rwnd(payload_length); + if (is_debug_tag_set("http2_con")) { + uint32_t rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); + Http2StreamDebug(cstate.ua_session, stream->get_id(), "Received DATA frame: rwnd con=%zd/%" PRId32 " stream=%zd/%" PRId32, + cstate.server_rwnd(), rwnd, stream->server_rwnd(), rwnd); + } + const uint32_t unpadded_length = payload_length - pad_length; // If we call write() multiple times, we must keep the same reader, so we can // update its offset via consume. Otherwise, we will read the same data on the @@ -172,21 +178,6 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame) } myreader->writer()->dealloc_reader(myreader); - uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); - uint32_t min_rwnd = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); - // Connection level WINDOW UPDATE - if (cstate.server_rwnd() <= min_rwnd) { - Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd(); - cstate.increment_server_rwnd(diff_size); - cstate.send_window_update_frame(0, diff_size); - } - // Stream level WINDOW UPDATE - if (stream->server_rwnd() <= min_rwnd) { - Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd(); - stream->increment_server_rwnd(diff_size); - cstate.send_window_update_frame(stream->get_id(), diff_size); - } - return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); } @@ -1217,6 +1208,35 @@ Http2ConnectionState::restart_streams() } void +Http2ConnectionState::restart_receiving(Http2Stream *stream) +{ + uint32_t initial_rwnd = this->server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); + uint32_t min_rwnd = std::min(initial_rwnd, this->server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); + + // Connection level WINDOW UPDATE + if (this->server_rwnd() < min_rwnd) { + Http2WindowSize diff_size = initial_rwnd - this->server_rwnd(); + this->increment_server_rwnd(diff_size); + this->send_window_update_frame(0, diff_size); + } + + // Stream level WINDOW UPDATE + if (stream == nullptr || stream->server_rwnd() >= min_rwnd) { + return; + } + + // If read_vio is buffering data, do not fully update window + int64_t data_size = stream->read_vio_read_avail(); + if (data_size >= initial_rwnd) { + return; + } + + Http2WindowSize diff_size = initial_rwnd - std::max(static_cast<int64_t>(stream->server_rwnd()), data_size); + stream->increment_server_rwnd(diff_size); + this->send_window_update_frame(stream->get_id(), diff_size); +} + +void Http2ConnectionState::cleanup_streams() { Http2Stream *s = stream_list.head; @@ -1434,6 +1454,12 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len return Http2SendDataFrameResult::ERROR; } + SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); + if (this->ua_session->write_avail() == 0) { + Http2StreamDebug(this->ua_session, stream->get_id(), "Not write avail"); + return Http2SendDataFrameResult::NOT_WRITE_AVAIL; + } + // Select appropriate payload length if (read_available_size > 0) { // We only need to check for window size when there is a payload @@ -1477,7 +1503,6 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len current_reader->consume(payload_length); // xmit event - SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data); if (flags & HTTP2_FLAGS_DATA_END_STREAM) { @@ -1865,7 +1890,7 @@ Http2ConnectionState::send_goaway_frame(Http2StreamId id, Http2ErrorCode ec) void Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size) { - Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame"); + Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame: size=%" PRIu32, size); // Create WINDOW_UPDATE frame Http2Frame window_update(HTTP2_FRAME_TYPE_WINDOW_UPDATE, id, 0x0); diff --git a/proxy/http2/Http2ConnectionState.h b/proxy/http2/Http2ConnectionState.h index e6f4ff9..aab27cb 100644 --- a/proxy/http2/Http2ConnectionState.h +++ b/proxy/http2/Http2ConnectionState.h @@ -36,6 +36,7 @@ enum class Http2SendDataFrameResult { NO_ERROR = 0, NO_WINDOW, NO_PAYLOAD, + NOT_WRITE_AVAIL, ERROR, DONE, }; @@ -130,6 +131,8 @@ public: void init() { + this->_server_rwnd = Http2::initial_window_size; + local_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); dependency_tree = new DependencyTree(Http2::max_concurrent_streams_in); @@ -173,6 +176,7 @@ public: void release_stream(Http2Stream *stream); void cleanup_streams(); + void restart_receiving(Http2Stream *stream); void update_initial_rwnd(Http2WindowSize new_size); Http2StreamId @@ -352,7 +356,7 @@ private: // Connection level window size ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE; - ssize_t _server_rwnd = Http2::initial_window_size; + ssize_t _server_rwnd = 0; std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; int _recent_rwnd_increment_index = 0; diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc index d1c37f6..a4ef29e 100644 --- a/proxy/http2/Http2Stream.cc +++ b/proxy/http2/Http2Stream.cc @@ -519,6 +519,19 @@ Http2Stream::update_read_request(int64_t read_len, bool call_update, bool check_ void Http2Stream::restart_sending() { + if (!this->response_header_done) { + return; + } + + IOBufferReader *reader = this->response_get_data_reader(); + if (reader && !reader->is_read_avail_more_than(0)) { + return; + } + + if (this->write_vio.mutex && this->write_vio.ntodo() == 0) { + return; + } + this->send_response_body(true); } @@ -690,6 +703,12 @@ Http2Stream::reenable(VIO *vio) SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); update_write_request(vio->get_reader(), INT64_MAX, true); } else if (vio->op == VIO::READ) { + Http2ClientSession *h2_proxy_ssn = static_cast<Http2ClientSession *>(this->get_parent()); + { + SCOPED_MUTEX_LOCK(lock, h2_proxy_ssn->connection_state.mutex, this_ethread()); + h2_proxy_ssn->connection_state.restart_receiving(this); + } + SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); update_read_request(INT64_MAX, true); } @@ -945,3 +964,14 @@ Http2Stream::_switch_thread_if_not_on_right_thread(int event, void *edata) } return true; } + +int64_t +Http2Stream::read_vio_read_avail() +{ + MIOBuffer *writer = this->read_vio.get_writer(); + if (writer) { + return writer->max_read_avail(); + } + + return 0; +} diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h index 3be1057..f46404e 100644 --- a/proxy/http2/Http2Stream.h +++ b/proxy/http2/Http2Stream.h @@ -38,10 +38,7 @@ class Http2Stream : public ProxyClientTransaction { public: typedef ProxyClientTransaction super; ///< Parent type. - Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd) - { - SET_HANDLER(&Http2Stream::main_event_handler); - } + Http2Stream() { SET_HANDLER(&Http2Stream::main_event_handler); } void init(Http2StreamId sid, ssize_t initial_rwnd) @@ -50,6 +47,7 @@ public: _start_time = Thread::get_hrtime(); _thread = this_ethread(); this->_client_rwnd = initial_rwnd; + this->_server_rwnd = Http2::initial_window_size; HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread); HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread); sm_reader = request_reader = request_buffer.alloc_reader(); @@ -160,6 +158,7 @@ public: ssize_t server_rwnd() const; Http2ErrorCode increment_server_rwnd(size_t amount); Http2ErrorCode decrement_server_rwnd(size_t amount); + int64_t read_vio_read_avail(); uint8_t *header_blocks = nullptr; uint32_t header_blocks_length = 0; // total length of header blocks (not include @@ -275,8 +274,8 @@ private: uint64_t data_length = 0; uint64_t bytes_sent = 0; - ssize_t _client_rwnd; - ssize_t _server_rwnd = Http2::initial_window_size; + ssize_t _client_rwnd = 0; + ssize_t _server_rwnd = 0; std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; int _recent_rwnd_increment_index = 0;