diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 24d015b1..8d89e48d 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -179,24 +179,11 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, } const auto &req = downstream->request(); auto input = downstream->get_request_buf(); - auto nread = input->remove(buf, length); - auto input_empty = input->rleft() == 0; - if (nread > 0) { - // This is important because it will handle flow control - // stuff. - if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, - nread) != 0) { - // In this case, downstream may be deleted. - return NGHTTP2_ERR_CALLBACK_FAILURE; - } + auto nread = std::min(input->rleft(), length); + auto input_empty = input->rleft() == nread; - // Check dconn is still alive because Upstream::resume_read() - // may delete downstream which will delete dconn. - if (sd->dconn == nullptr) { - return NGHTTP2_ERR_DEFERRED; - } - } + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; if (input_empty && downstream->get_request_state() == Downstream::MSG_COMPLETE && @@ -229,12 +216,6 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, } } - if (!input_empty) { - downstream->reset_downstream_wtimer(); - } else { - downstream->disable_downstream_wtimer(); - } - if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) { downstream->disable_downstream_wtimer(); diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index f241996e..f33ac031 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -1362,6 +1362,54 @@ int on_frame_not_send_callback(nghttp2_session *session, } } // namespace +namespace { +constexpr auto PADDING = std::array{}; +} // namespace + +namespace { +int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, void *user_data) { + auto http2session = static_cast(user_data); + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + auto dconn = sd->dconn; + auto downstream = dconn->get_downstream(); + auto input = downstream->get_request_buf(); + auto wb = http2session->get_request_buf(); + + size_t padlen = 0; + + wb->append(framehd, 9); + if (frame->data.padlen > 0) { + padlen = frame->data.padlen - 1; + wb->append(static_cast(padlen)); + } + + input->remove(*wb, length); + + wb->append(PADDING.data(), padlen); + + downstream->reset_downstream_wtimer(); + + if (length > 0) { + // This is important because it will handle flow control + // stuff. + if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, + length) != 0) { + // In this case, downstream may be deleted. + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + // Here sd->dconn could be nullptr, because + // Upstream::resume_read() may delete downstream which will delete + // dconn. Is this still really true? + } + + return 0; +} +} // namespace + nghttp2_session_callbacks *create_http2_downstream_callbacks() { int rv; nghttp2_session_callbacks *callbacks; @@ -1393,6 +1441,9 @@ nghttp2_session_callbacks *create_http2_downstream_callbacks() { nghttp2_session_callbacks_set_on_begin_headers_callback( callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_send_data_callback(callbacks, + send_data_callback); + if (get_config()->padding) { nghttp2_session_callbacks_set_select_padding_callback( callbacks, http::select_padding_callback); @@ -2115,4 +2166,6 @@ void Http2Session::exclude_from_scheduling() { freelist_zone_ = FREELIST_ZONE_GONE; } +DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; } + } // namespace shrpx diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h index 37a9d150..239ccd45 100644 --- a/src/shrpx_http2_session.h +++ b/src/shrpx_http2_session.h @@ -195,6 +195,8 @@ public: // server initiated concurrency limit. bool max_concurrency_reached(size_t extra = 0) const; + DefaultMemchunks *get_request_buf(); + enum { // Disconnected DISCONNECTED,