diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 2e156f1d..f65b8002 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -101,14 +101,10 @@ int main(int argc, char *argv[]) { shrpx::test_http2_get_pure_path_component) || !CU_add_test(pSuite, "http2_construct_push_component", shrpx::test_http2_construct_push_component) || - !CU_add_test(pSuite, "downstream_index_request_headers", - shrpx::test_downstream_index_request_headers) || - !CU_add_test(pSuite, "downstream_index_response_headers", - shrpx::test_downstream_index_response_headers) || - !CU_add_test(pSuite, "downstream_get_request_header", - shrpx::test_downstream_get_request_header) || - !CU_add_test(pSuite, "downstream_get_response_header", - shrpx::test_downstream_get_response_header) || + !CU_add_test(pSuite, "downstream_field_store_index_headers", + shrpx::test_downstream_field_store_index_headers) || + !CU_add_test(pSuite, "downstream_field_store_header", + shrpx::test_downstream_field_store_header) || !CU_add_test(pSuite, "downstream_crumble_request_cookie", shrpx::test_downstream_crumble_request_cookie) || !CU_add_test(pSuite, "downstream_assemble_request_cookie", diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index b6efe4d7..7e7a750d 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -629,30 +629,30 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { auto &groups = get_config()->downstream_addr_groups; auto catch_all = get_config()->downstream_addr_group_catch_all; + const auto &req = downstream->request(); + // Fast path. If we have one group, it must be catch-all group. // HTTP/2 and client proxy modes fall in this case. if (groups.size() == 1) { group = 0; - } else if (downstream->get_request_method() == HTTP_CONNECT) { + } else if (req.method == HTTP_CONNECT) { // We don't know how to treat CONNECT request in host-path // mapping. It most likely appears in proxy scenario. Since we // have dealt with proxy case already, just use catch-all group. group = catch_all; } else { auto &router = get_config()->router; - if (!downstream->get_request_http2_authority().empty()) { - group = match_downstream_addr_group( - router, downstream->get_request_http2_authority(), - downstream->get_request_path(), groups, catch_all); + if (!req.authority.empty()) { + group = match_downstream_addr_group(router, req.authority, req.path, + groups, catch_all); } else { - auto h = downstream->get_request_header(http2::HD_HOST); + auto h = req.fs.header(http2::HD_HOST); if (h) { - group = match_downstream_addr_group(router, h->value, - downstream->get_request_path(), - groups, catch_all); + group = match_downstream_addr_group(router, h->value, req.path, groups, + catch_all); } else { - group = match_downstream_addr_group( - router, "", downstream->get_request_path(), groups, catch_all); + group = match_downstream_addr_group(router, "", req.path, groups, + catch_all); } } } @@ -783,28 +783,27 @@ void ClientHandler::start_immediate_shutdown() { } namespace { -// Construct absolute request URI from |downstream|, mainly to log +// Construct absolute request URI from |Request|, mainly to log // request URI for proxy request (HTTP/2 proxy or client proxy). This // is mostly same routine found in // HttpDownstreamConnection::push_request_headers(), but vastly // simplified since we only care about absolute URI. -std::string construct_absolute_request_uri(Downstream *downstream) { - auto &authority = downstream->get_request_http2_authority(); - if (authority.empty()) { - return downstream->get_request_path(); +std::string construct_absolute_request_uri(const Request &req) { + if (req.authority.empty()) { + return req.path; } + std::string uri; - auto &scheme = downstream->get_request_http2_scheme(); - if (scheme.empty()) { + if (req.scheme.empty()) { // We may have to log the request which lacks scheme (e.g., // http/1.1 with origin form). uri += "http://"; } else { - uri += scheme; + uri += req.scheme; uri += "://"; } - uri += authority; - uri += downstream->get_request_path(); + uri += req.authority; + uri += req.path; return uri; } @@ -812,22 +811,20 @@ std::string construct_absolute_request_uri(Downstream *downstream) { void ClientHandler::write_accesslog(Downstream *downstream) { nghttp2::ssl::TLSSessionInfo tls_info; + const auto &req = downstream->request(); + const auto &resp = downstream->response(); upstream_accesslog( get_config()->accesslog_format, LogSpec{ - downstream, ipaddr_.c_str(), - http2::to_method_string(downstream->get_request_method()), + downstream, ipaddr_.c_str(), http2::to_method_string(req.method), - downstream->get_request_method() == HTTP_CONNECT - ? downstream->get_request_http2_authority().c_str() + req.method == HTTP_CONNECT + ? req.authority.c_str() : (get_config()->http2_proxy || get_config()->client_proxy) - ? construct_absolute_request_uri(downstream).c_str() - : downstream->get_request_path().empty() - ? downstream->get_request_method() == HTTP_OPTIONS - ? "*" - : "-" - : downstream->get_request_path().c_str(), + ? construct_absolute_request_uri(req).c_str() + : req.path.empty() ? req.method == HTTP_OPTIONS ? "*" : "-" + : req.path.c_str(), alpn_.c_str(), nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl), @@ -836,8 +833,7 @@ void ClientHandler::write_accesslog(Downstream *downstream) { downstream->get_request_start_time(), // request_start_time std::chrono::high_resolution_clock::now(), // request_end_time - downstream->get_request_major(), downstream->get_request_minor(), - downstream->get_response_http_status(), + req.http_major, req.http_minor, resp.http_status, downstream->get_response_sent_bodylen(), port_.c_str(), get_config()->port, get_config()->pid, }); diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index eb284aa7..45b522b2 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -116,22 +116,14 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, : dlnext(nullptr), dlprev(nullptr), request_start_time_(std::chrono::high_resolution_clock::now()), request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0), - response_bodylen_(0), response_sent_bodylen_(0), - request_content_length_(-1), response_content_length_(-1), - upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0), - response_headers_sum_(0), request_datalen_(0), response_datalen_(0), + response_bodylen_(0), response_sent_bodylen_(0), upstream_(upstream), + blocked_link_(nullptr), request_datalen_(0), response_datalen_(0), num_retry_(0), stream_id_(stream_id), priority_(priority), downstream_stream_id_(-1), - response_rst_stream_error_code_(NGHTTP2_NO_ERROR), request_method_(-1), - request_state_(INITIAL), request_major_(1), request_minor_(1), - response_state_(INITIAL), response_http_status_(0), response_major_(1), - response_minor_(1), dispatch_state_(DISPATCH_NONE), - upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false), - chunked_request_(false), request_connection_close_(false), - request_header_key_prev_(false), request_trailer_key_prev_(false), - request_http2_expect_body_(false), chunked_response_(false), - response_connection_close_(false), response_header_key_prev_(false), - response_trailer_key_prev_(false), expect_final_response_(false), + response_rst_stream_error_code_(NGHTTP2_NO_ERROR), + request_state_(INITIAL), response_state_(INITIAL), + dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false), + chunked_response_(false), expect_final_response_(false), request_pending_(false) { ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., @@ -147,12 +139,6 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, upstream_wtimer_.data = this; downstream_rtimer_.data = this; downstream_wtimer_.data = this; - - http2::init_hdidx(request_hdidx_); - http2::init_hdidx(response_hdidx_); - - request_headers_.reserve(16); - response_headers_.reserve(32); } Downstream::~Downstream() { @@ -252,16 +238,10 @@ const Headers::value_type *get_header_linear(const Headers &headers, } } // namespace -const Headers &Downstream::get_request_headers() const { - return request_headers_; -} - -Headers &Downstream::get_request_headers() { return request_headers_; } - -void Downstream::assemble_request_cookie() { - std::string &cookie = assembled_request_cookie_; +std::string Downstream::assemble_request_cookie() const { + std::string cookie; cookie = ""; - for (auto &kv : request_headers_) { + for (auto &kv : req_.fs.headers()) { if (kv.name.size() != 6 || kv.name[5] != 'e' || !util::streq_l("cooki", kv.name.c_str(), 5)) { continue; @@ -278,11 +258,13 @@ void Downstream::assemble_request_cookie() { if (cookie.size() >= 2) { cookie.erase(cookie.size() - 2); } + + return cookie; } size_t Downstream::count_crumble_request_cookie() { size_t n = 0; - for (auto &kv : request_headers_) { + for (auto &kv : req_.fs.headers()) { if (kv.name.size() != 6 || kv.name[5] != 'e' || !util::streq_l("cooki", kv.name.c_str(), 5)) { continue; @@ -307,7 +289,7 @@ size_t Downstream::count_crumble_request_cookie() { } void Downstream::crumble_request_cookie(std::vector &nva) { - for (auto &kv : request_headers_) { + for (auto &kv : req_.fs.headers()) { if (kv.name.size() != 6 || kv.name[5] != 'e' || !util::streq_l("cooki", kv.name.c_str(), 5)) { continue; @@ -335,10 +317,6 @@ void Downstream::crumble_request_cookie(std::vector &nva) { } } -const std::string &Downstream::get_assembled_request_cookie() const { - return assembled_request_cookie_; -} - namespace { void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name, std::string value) { @@ -361,7 +339,7 @@ void add_header(size_t &sum, Headers &headers, const uint8_t *name, } // namespace namespace { -void append_last_header_key(bool key_prev, size_t &sum, Headers &headers, +void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers, const char *data, size_t len) { assert(key_prev); sum += len; @@ -371,33 +349,21 @@ void append_last_header_key(bool key_prev, size_t &sum, Headers &headers, } // namespace namespace { -void append_last_header_value(bool key_prev, size_t &sum, Headers &headers, +void append_last_header_value(bool &key_prev, size_t &sum, Headers &headers, const char *data, size_t len) { - assert(!key_prev); + key_prev = false; sum += len; auto &item = headers.back(); item.value.append(data, len); } } // namespace -namespace { -void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers, - const char *data, size_t len) { - key_prev = false; - sum += len; - auto &item = headers.back(); - item.value.assign(data, len); -} -} // namespace - -namespace { -int index_headers(http2::HeaderIndex &hdidx, Headers &headers, - int64_t &content_length) { - http2::init_hdidx(hdidx); +int FieldStore::index_headers() { + http2::init_hdidx(hdidx_); content_length = -1; - for (size_t i = 0; i < headers.size(); ++i) { - auto &kv = headers[i]; + for (size_t i = 0; i < headers_.size(); ++i) { + auto &kv = headers_[i]; util::inp_strlower(kv.name); auto token = http2::lookup_token( @@ -407,7 +373,7 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers, } kv.token = token; - http2::index_header(hdidx, token, i); + http2::index_header(hdidx_, token, i); if (token == http2::HD_CONTENT_LENGTH) { auto len = util::parse_uint(kv.value); @@ -422,124 +388,76 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers, } return 0; } -} // namespace -int Downstream::index_request_headers() { - return index_headers(request_hdidx_, request_headers_, - request_content_length_); +const Headers::value_type *FieldStore::header(int16_t token) const { + return http2::get_header(hdidx_, token, headers_); } -const Headers::value_type *Downstream::get_request_header(int16_t token) const { - return http2::get_header(request_hdidx_, token, request_headers_); +Headers::value_type *FieldStore::header(int16_t token) { + return http2::get_header(hdidx_, token, headers_); } -const Headers::value_type * -Downstream::get_request_header(const std::string &name) const { - return get_header_linear(request_headers_, name); +const Headers::value_type *FieldStore::header(const std::string &name) const { + return get_header_linear(headers_, name); } -void Downstream::add_request_header(std::string name, std::string value) { - add_header(request_header_key_prev_, request_headers_sum_, request_headers_, - std::move(name), std::move(value)); +void FieldStore::add_header(std::string name, std::string value) { + shrpx::add_header(header_key_prev_, buffer_size_, headers_, std::move(name), + std::move(value)); } -void Downstream::set_last_request_header_value(const char *data, size_t len) { - set_last_header_value(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); +void FieldStore::add_header(std::string name, std::string value, + int16_t token) { + http2::index_header(hdidx_, token, headers_.size()); + buffer_size_ += name.size() + value.size(); + headers_.emplace_back(std::move(name), std::move(value), false, token); } -void Downstream::add_request_header(std::string name, std::string value, - int16_t token) { - http2::index_header(request_hdidx_, token, request_headers_.size()); - request_headers_sum_ += name.size() + value.size(); - request_headers_.emplace_back(std::move(name), std::move(value), false, - token); +void FieldStore::add_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + http2::index_header(hdidx_, token, headers_.size()); + shrpx::add_header(buffer_size_, headers_, name, namelen, value, valuelen, + no_index, token); } -void Downstream::add_request_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - http2::index_header(request_hdidx_, token, request_headers_.size()); - add_header(request_headers_sum_, request_headers_, name, namelen, value, - valuelen, no_index, token); +void FieldStore::append_last_header_key(const char *data, size_t len) { + shrpx::append_last_header_key(header_key_prev_, buffer_size_, headers_, data, + len); } -bool Downstream::get_request_header_key_prev() const { - return request_header_key_prev_; +void FieldStore::append_last_header_value(const char *data, size_t len) { + shrpx::append_last_header_value(header_key_prev_, buffer_size_, headers_, + data, len); } -void Downstream::append_last_request_header_key(const char *data, size_t len) { - append_last_header_key(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); +void FieldStore::clear_headers() { + headers_.clear(); + http2::init_hdidx(hdidx_); } -void Downstream::append_last_request_header_value(const char *data, - size_t len) { - append_last_header_value(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); +void FieldStore::add_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + // we never index trailer fields. Header size limit should be + // applied to all header and trailer fields combined. + shrpx::add_header(buffer_size_, trailers_, name, namelen, value, valuelen, + no_index, -1); } -void Downstream::clear_request_headers() { - Headers().swap(request_headers_); - http2::init_hdidx(request_hdidx_); +void FieldStore::add_trailer(std::string name, std::string value) { + shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, std::move(name), + std::move(value)); } -size_t Downstream::get_request_headers_sum() const { - return request_headers_sum_; +void FieldStore::append_last_trailer_key(const char *data, size_t len) { + shrpx::append_last_header_key(trailer_key_prev_, buffer_size_, trailers_, + data, len); } -void Downstream::add_request_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - // we never index trailer part. Header size limit should be applied - // to all request header fields combined. - add_header(request_headers_sum_, request_trailers_, name, namelen, value, - valuelen, no_index, -1); -} - -const Headers &Downstream::get_request_trailers() const { - return request_trailers_; -} - -void Downstream::add_request_trailer(std::string name, std::string value) { - add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_, - std::move(name), std::move(value)); -} - -void Downstream::set_last_request_trailer_value(const char *data, size_t len) { - set_last_header_value(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -bool Downstream::get_request_trailer_key_prev() const { - return request_trailer_key_prev_; -} - -void Downstream::append_last_request_trailer_key(const char *data, size_t len) { - append_last_header_key(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -void Downstream::append_last_request_trailer_value(const char *data, - size_t len) { - append_last_header_value(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -void Downstream::set_request_method(int method) { request_method_ = method; } - -int Downstream::get_request_method() const { return request_method_; } - -void Downstream::set_request_path(std::string path) { - request_path_ = std::move(path); -} - -void Downstream::append_request_path(const char *data, size_t len) { - request_path_.append(data, len); -} - -const std::string &Downstream::get_request_path() const { - return request_path_; +void FieldStore::append_last_trailer_value(const char *data, size_t len) { + shrpx::append_last_header_value(trailer_key_prev_, buffer_size_, trailers_, + data, len); } void Downstream::set_request_start_time( @@ -552,34 +470,6 @@ Downstream::get_request_start_time() const { return request_start_time_; } -const std::string &Downstream::get_request_http2_scheme() const { - return request_http2_scheme_; -} - -void Downstream::set_request_http2_scheme(std::string scheme) { - request_http2_scheme_ = std::move(scheme); -} - -const std::string &Downstream::get_request_http2_authority() const { - return request_http2_authority_; -} - -void Downstream::set_request_http2_authority(std::string authority) { - request_http2_authority_ = std::move(authority); -} - -void Downstream::append_request_http2_authority(const char *data, size_t len) { - request_http2_authority_.append(data, len); -} - -void Downstream::set_request_major(int major) { request_major_ = major; } - -void Downstream::set_request_minor(int minor) { request_minor_ = minor; } - -int Downstream::get_request_major() const { return request_major_; } - -int Downstream::get_request_minor() const { return request_minor_; } - void Downstream::reset_upstream(Upstream *upstream) { upstream_ = upstream; if (dconn_) { @@ -601,22 +491,6 @@ bool Downstream::get_chunked_request() const { return chunked_request_; } void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } -bool Downstream::get_request_connection_close() const { - return request_connection_close_; -} - -void Downstream::set_request_connection_close(bool f) { - request_connection_close_ = f; -} - -bool Downstream::get_request_http2_expect_body() const { - return request_http2_expect_body_; -} - -void Downstream::set_request_http2_expect_body(bool f) { - request_http2_expect_body_ = f; -} - bool Downstream::request_buf_full() { if (dconn_) { return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; @@ -662,30 +536,9 @@ int Downstream::end_upload_data() { return dconn_->end_upload_data(); } -const Headers &Downstream::get_response_headers() const { - return response_headers_; -} - -Headers &Downstream::get_response_headers() { return response_headers_; } - -int Downstream::index_response_headers() { - return index_headers(response_hdidx_, response_headers_, - response_content_length_); -} - -const Headers::value_type * -Downstream::get_response_header(int16_t token) const { - return http2::get_header(response_hdidx_, token, response_headers_); -} - -Headers::value_type *Downstream::get_response_header(int16_t token) { - return http2::get_header(response_hdidx_, token, response_headers_); -} - void Downstream::rewrite_location_response_header( const std::string &upstream_scheme) { - auto hd = - http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_); + auto hd = resp_.fs.header(http2::HD_LOCATION); if (!hd) { return; } @@ -696,14 +549,13 @@ void Downstream::rewrite_location_response_header( return; } std::string new_uri; - if (get_config()->no_host_rewrite || request_method_ == HTTP_CONNECT) { - if (!request_http2_authority_.empty()) { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_http2_authority_, request_http2_authority_, - upstream_scheme); + if (get_config()->no_host_rewrite || req_.method == HTTP_CONNECT) { + if (!req_.authority.empty()) { + new_uri = http2::rewrite_location_uri((*hd).value, u, req_.authority, + req_.authority, upstream_scheme); } if (new_uri.empty()) { - auto host = get_request_header(http2::HD_HOST); + auto host = req_.fs.header(http2::HD_HOST); if (host) { new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value, (*host).value, upstream_scheme); @@ -718,12 +570,12 @@ void Downstream::rewrite_location_response_header( if (request_downstream_host_.empty()) { return; } - if (!request_http2_authority_.empty()) { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_downstream_host_, request_http2_authority_, - upstream_scheme); + if (!req_.authority.empty()) { + new_uri = + http2::rewrite_location_uri((*hd).value, u, request_downstream_host_, + req_.authority, upstream_scheme); } else { - auto host = get_request_header(http2::HD_HOST); + auto host = req_.fs.header(http2::HD_HOST); if (host) { new_uri = http2::rewrite_location_uri((*hd).value, u, request_downstream_host_, @@ -734,131 +586,18 @@ void Downstream::rewrite_location_response_header( } } } - if (!new_uri.empty()) { - auto idx = response_hdidx_[http2::HD_LOCATION]; - response_headers_[idx].value = std::move(new_uri); + + if (new_uri.empty()) { + return; } -} -void Downstream::add_response_header(std::string name, std::string value) { - add_header(response_header_key_prev_, response_headers_sum_, - response_headers_, std::move(name), std::move(value)); -} - -void Downstream::set_last_response_header_value(const char *data, size_t len) { - set_last_header_value(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::add_response_header(std::string name, std::string value, - int16_t token) { - http2::index_header(response_hdidx_, token, response_headers_.size()); - response_headers_sum_ += name.size() + value.size(); - response_headers_.emplace_back(std::move(name), std::move(value), false, - token); -} - -void Downstream::add_response_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - http2::index_header(response_hdidx_, token, response_headers_.size()); - add_header(response_headers_sum_, response_headers_, name, namelen, value, - valuelen, no_index, token); -} - -bool Downstream::get_response_header_key_prev() const { - return response_header_key_prev_; -} - -void Downstream::append_last_response_header_key(const char *data, size_t len) { - append_last_header_key(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::append_last_response_header_value(const char *data, - size_t len) { - append_last_header_value(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::clear_response_headers() { - Headers().swap(response_headers_); - http2::init_hdidx(response_hdidx_); -} - -size_t Downstream::get_response_headers_sum() const { - return response_headers_sum_; -} - -const Headers &Downstream::get_response_trailers() const { - return response_trailers_; -} - -void Downstream::add_response_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - add_header(response_headers_sum_, response_trailers_, name, namelen, value, - valuelen, no_index, -1); -} - -unsigned int Downstream::get_response_http_status() const { - return response_http_status_; -} - -void Downstream::add_response_trailer(std::string name, std::string value) { - add_header(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, std::move(name), std::move(value)); -} - -void Downstream::set_last_response_trailer_value(const char *data, size_t len) { - set_last_header_value(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -bool Downstream::get_response_trailer_key_prev() const { - return response_trailer_key_prev_; -} - -void Downstream::append_last_response_trailer_key(const char *data, - size_t len) { - append_last_header_key(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -void Downstream::append_last_response_trailer_value(const char *data, - size_t len) { - append_last_header_value(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -void Downstream::set_response_http_status(unsigned int status) { - response_http_status_ = status; -} - -void Downstream::set_response_major(int major) { response_major_ = major; } - -void Downstream::set_response_minor(int minor) { response_minor_ = minor; } - -int Downstream::get_response_major() const { return response_major_; } - -int Downstream::get_response_minor() const { return response_minor_; } - -int Downstream::get_response_version() const { - return response_major_ * 100 + response_minor_; + (*hd).value = std::move(new_uri); } bool Downstream::get_chunked_response() const { return chunked_response_; } void Downstream::set_chunked_response(bool f) { chunked_response_ = f; } -bool Downstream::get_response_connection_close() const { - return response_connection_close_; -} - -void Downstream::set_response_connection_close(bool f) { - response_connection_close_ = f; -} - int Downstream::on_read() { if (!dconn_) { DLOG(INFO, this) << "dconn_ is NULL"; @@ -904,31 +643,15 @@ int64_t Downstream::get_response_sent_bodylen() const { return response_sent_bodylen_; } -int64_t Downstream::get_response_content_length() const { - return response_content_length_; -} - -void Downstream::set_response_content_length(int64_t len) { - response_content_length_ = len; -} - -int64_t Downstream::get_request_content_length() const { - return request_content_length_; -} - -void Downstream::set_request_content_length(int64_t len) { - request_content_length_ = len; -} - bool Downstream::validate_request_bodylen() const { - if (request_content_length_ == -1) { + if (req_.fs.content_length == -1) { return true; } - if (request_content_length_ != request_bodylen_) { + if (req_.fs.content_length != request_bodylen_) { if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "request invalid bodylen: content-length=" - << request_content_length_ + << req_.fs.content_length << ", received=" << request_bodylen_; } return false; @@ -938,14 +661,14 @@ bool Downstream::validate_request_bodylen() const { } bool Downstream::validate_response_bodylen() const { - if (!expect_response_body() || response_content_length_ == -1) { + if (!expect_response_body() || resp_.fs.content_length == -1) { return true; } - if (response_content_length_ != response_bodylen_) { + if (resp_.fs.content_length != response_bodylen_) { if (LOG_ENABLED(INFO)) { DLOG(INFO, this) << "response invalid bodylen: content-length=" - << response_content_length_ + << resp_.fs.content_length << ", received=" << response_bodylen_; } return false; @@ -959,80 +682,77 @@ void Downstream::set_priority(int32_t pri) { priority_ = pri; } int32_t Downstream::get_priority() const { return priority_; } void Downstream::check_upgrade_fulfilled() { - if (request_method_ == HTTP_CONNECT) { - upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; + if (req_.method == HTTP_CONNECT) { + upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300; return; } - if (response_http_status_ == 101) { + if (resp_.http_status == 101) { // TODO Do more strict checking for upgrade headers - upgraded_ = upgrade_request_; + upgraded_ = req_.upgrade_request; return; } } void Downstream::inspect_http2_request() { - if (request_method_ == HTTP_CONNECT) { - upgrade_request_ = true; + if (req_.method == HTTP_CONNECT) { + req_.upgrade_request = true; } } void Downstream::inspect_http1_request() { - if (request_method_ == HTTP_CONNECT) { - upgrade_request_ = true; + if (req_.method == HTTP_CONNECT) { + req_.upgrade_request = true; } - if (!upgrade_request_) { - auto idx = request_hdidx_[http2::HD_UPGRADE]; - if (idx != -1) { - auto &val = request_headers_[idx].value; + if (!req_.upgrade_request) { + auto upgrade = req_.fs.header(http2::HD_UPGRADE); + if (upgrade) { + const auto &val = upgrade->value; // TODO Perform more strict checking for upgrade headers if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(), val.size())) { - http2_upgrade_seen_ = true; + req_.http2_upgrade_seen = true; } else { - upgrade_request_ = true; + req_.upgrade_request = true; } } } - auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING]; - if (idx != -1) { - request_content_length_ = -1; - if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) { + auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING); + if (transfer_encoding) { + req_.fs.content_length = -1; + if (util::strifind(transfer_encoding->value.c_str(), "chunked")) { chunked_request_ = true; } } } void Downstream::inspect_http1_response() { - auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING]; - if (idx != -1) { - response_content_length_ = -1; - if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) { + auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING); + if (transfer_encoding) { + resp_.fs.content_length = -1; + if (util::strifind(transfer_encoding->value.c_str(), "chunked")) { chunked_response_ = true; } } } void Downstream::reset_response() { - response_http_status_ = 0; - response_major_ = 1; - response_minor_ = 1; + resp_.http_status = 0; + resp_.http_major = 1; + resp_.http_minor = 1; } bool Downstream::get_non_final_response() const { - return !upgraded_ && response_http_status_ / 100 == 1; + return !upgraded_ && resp_.http_status / 100 == 1; } bool Downstream::get_upgraded() const { return upgraded_; } -bool Downstream::get_upgrade_request() const { return upgrade_request_; } - bool Downstream::get_http2_upgrade_request() const { - return http2_upgrade_seen_ && - request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1 && + return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) && response_state_ == INITIAL; } @@ -1041,11 +761,11 @@ const std::string EMPTY; } // namespace const std::string &Downstream::get_http2_settings() const { - auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS]; - if (idx == -1) { + auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS); + if (!http2_settings) { return EMPTY; } - return request_headers_[idx].value; + return http2_settings->value; } void Downstream::set_downstream_stream_id(int32_t stream_id) { @@ -1093,31 +813,7 @@ size_t Downstream::get_response_datalen() const { return response_datalen_; } void Downstream::reset_response_datalen() { response_datalen_ = 0; } bool Downstream::expect_response_body() const { - return http2::expect_response_body(request_method_, response_http_status_); -} - -namespace { -bool pseudo_header_allowed(const Headers &headers) { - if (headers.empty()) { - return true; - } - - return headers.back().name.c_str()[0] == ':'; -} -} // namespace - -bool Downstream::request_pseudo_header_allowed(int16_t token) const { - if (!pseudo_header_allowed(request_headers_)) { - return false; - } - return http2::check_http2_request_pseudo_header(request_hdidx_, token); -} - -bool Downstream::response_pseudo_header_allowed(int16_t token) const { - if (!pseudo_header_allowed(response_headers_)) { - return false; - } - return http2::check_http2_response_pseudo_header(response_hdidx_, token); + return http2::expect_response_body(req_.method, resp_.http_status); } namespace { @@ -1232,7 +928,7 @@ void Downstream::disable_downstream_wtimer() { disable_timer(loop, &downstream_wtimer_); } -bool Downstream::accesslog_ready() const { return response_http_status_ > 0; } +bool Downstream::accesslog_ready() const { return resp_.http_status > 0; } void Downstream::add_retry() { ++num_retry_; } @@ -1269,14 +965,10 @@ BlockedLink *Downstream::detach_blocked_link() { return link; } -void Downstream::add_request_headers_sum(size_t amount) { - request_headers_sum_ += amount; -} - bool Downstream::can_detach_downstream_connection() const { return dconn_ && response_state_ == Downstream::MSG_COMPLETE && request_state_ == Downstream::MSG_COMPLETE && !upgraded_ && - !response_connection_close_; + !resp_.connection_close; } DefaultMemchunks Downstream::pop_response_buf() { diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index ad659637..3bfc73a2 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -49,6 +49,126 @@ class Upstream; class DownstreamConnection; struct BlockedLink; +class FieldStore { +public: + FieldStore(size_t headers_initial_capacity) + : content_length(-1), buffer_size_(0), header_key_prev_(false), + trailer_key_prev_(false) { + http2::init_hdidx(hdidx_); + headers_.reserve(headers_initial_capacity); + } + + const Headers &headers() const { return headers_; } + const Headers &trailers() const { return trailers_; } + + Headers &headers() { return headers_; } + + const void add_extra_buffer_size(size_t n) { buffer_size_ += n; } + size_t buffer_size() const { return buffer_size_; } + + size_t num_fields() const { return headers_.size() + trailers_.size(); } + + // Returns pointer to the header field with the name |name|. If + // multiple header have |name| as name, return last occurrence from + // the beginning. If no such header is found, returns nullptr. + // This function must be called after headers are indexed + const Headers::value_type *header(int16_t token) const; + Headers::value_type *header(int16_t token); + // Returns pointer to the header field with the name |name|. If no + // such header is found, returns nullptr. + const Headers::value_type *header(const std::string &name) const; + + void add_header(std::string name, std::string value); + void add_header(std::string name, std::string value, int16_t token); + void add_header(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen, bool no_index, int16_t token); + + void append_last_header_key(const char *data, size_t len); + void append_last_header_value(const char *data, size_t len); + + bool header_key_prev() const { return header_key_prev_; } + + // Lower the header field names and indexes header fields. If there + // is any invalid headers (e.g., multiple Content-Length having + // different values), returns -1. + int index_headers(); + + // Empties headers. + void clear_headers(); + + void add_trailer(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen, bool no_index, int16_t token); + void add_trailer(std::string name, std::string value); + + void append_last_trailer_key(const char *data, size_t len); + void append_last_trailer_value(const char *data, size_t len); + + bool trailer_key_prev() const { return trailer_key_prev_; } + + // content-length, -1 if it is unknown. + int64_t content_length; + +private: + Headers headers_; + // trailer fields. For HTTP/1.1, trailer fields are only included + // with chunked encoding. For HTTP/2, there is no such limit. + Headers trailers_; + http2::HeaderIndex hdidx_; + // Sum of the length of name and value in headers_ and trailers_. + // This could also be increased by add_extra_buffer_size() to take + // into account for request URI in case of HTTP/1.x request. + size_t buffer_size_; + bool header_key_prev_; + bool trailer_key_prev_; +}; + +struct Request { + Request() + : fs(16), method(-1), http_major(1), http_minor(1), + upgrade_request(false), http2_upgrade_seen(false), + connection_close(false), http2_expect_body(false) {} + + FieldStore fs; + // Request scheme. For HTTP/2, this is :scheme header field value. + // For HTTP/1.1, this is deduced from URI or connection. + std::string scheme; + // Request authority. This is HTTP/2 :authority header field value + // or host header field value. We may deduce it from absolute-form + // HTTP/1 request. We also store authority-form HTTP/1 request. + // This could be empty if request comes from HTTP/1.0 without Host + // header field and origin-form. + std::string authority; + // Request path, including query component. For HTTP/1.1, this is + // request-target. For HTTP/2, this is :path header field value. + // For CONNECT request, this is empty. + std::string path; + int method; + // HTTP major and minor version + int http_major, http_minor; + // Returns true if the request is HTTP upgrade (HTTP Upgrade or + // CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2 + // Upgrade, check get_http2_upgrade_request(). + bool upgrade_request; + // true if h2c is seen in Upgrade header field. + bool http2_upgrade_seen; + bool connection_close; + // true if this is HTTP/2, and request body is expected. Note that + // we don't take into account HTTP method here. + bool http2_expect_body; +}; + +struct Response { + Response() + : fs(32), http_status(0), http_major(1), http_minor(1), + connection_close(false) {} + + FieldStore fs; + // HTTP status code + unsigned int http_status; + int http_major, http_minor; + bool connection_close; +}; + class Downstream { public: Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, @@ -79,9 +199,6 @@ public: // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded. // This should not depend on inspect_http1_response(). void check_upgrade_fulfilled(); - // Returns true if the request is upgrade. Upgrade to HTTP/2 is - // excluded. For HTTP/2 Upgrade, check get_http2_upgrade_request(). - bool get_upgrade_request() const; // Returns true if the upgrade is succeded as a result of the call // check_upgrade_fulfilled(). HTTP/2 Upgrade is excluded. bool get_upgraded() const; @@ -94,88 +211,27 @@ public: bool get_http2_upgrade_request() const; // Returns the value of HTTP2-Settings request header field. const std::string &get_http2_settings() const; + // downstream request API - const Headers &get_request_headers() const; - Headers &get_request_headers(); + const Request &request() const { return req_; } + Request &request() { return req_; } + // Count number of crumbled cookies size_t count_crumble_request_cookie(); // Crumbles (split cookie by ";") in request_headers_ and adds them // in |nva|. Headers::no_index is inherited. void crumble_request_cookie(std::vector &nva); - void assemble_request_cookie(); - const std::string &get_assembled_request_cookie() const; - // Lower the request header field names and indexes request headers. - // If there is any invalid headers (e.g., multiple Content-Length - // having different values), returns -1. - int index_request_headers(); - // Returns pointer to the request header with the name |name|. If - // multiple header have |name| as name, return last occurrence from - // the beginning. If no such header is found, returns nullptr. - // This function must be called after headers are indexed - const Headers::value_type *get_request_header(int16_t token) const; - // Returns pointer to the request header with the name |name|. If - // no such header is found, returns nullptr. - const Headers::value_type *get_request_header(const std::string &name) const; - void add_request_header(std::string name, std::string value); - void set_last_request_header_value(const char *data, size_t len); + // Assembles request cookies. The opposite operation against + // crumble_request_cookie(). + std::string assemble_request_cookie() const; - void add_request_header(std::string name, std::string value, int16_t token); - void add_request_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - - bool get_request_header_key_prev() const; - void append_last_request_header_key(const char *data, size_t len); - void append_last_request_header_value(const char *data, size_t len); - // Empties request headers. - void clear_request_headers(); - - size_t get_request_headers_sum() const; - - const Headers &get_request_trailers() const; - void add_request_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - void add_request_trailer(std::string name, std::string value); - void set_last_request_trailer_value(const char *data, size_t len); - bool get_request_trailer_key_prev() const; - void append_last_request_trailer_key(const char *data, size_t len); - void append_last_request_trailer_value(const char *data, size_t len); - - void set_request_method(int method); - int get_request_method() const; - void set_request_path(std::string path); - void add_request_headers_sum(size_t amount); void set_request_start_time(std::chrono::high_resolution_clock::time_point time); const std::chrono::high_resolution_clock::time_point & get_request_start_time() const; - void append_request_path(const char *data, size_t len); - // Returns request path. For HTTP/1.1, this is request-target. For - // HTTP/2, this is :path header field value. For CONNECT request, - // this is empty. - const std::string &get_request_path() const; - // Returns HTTP/2 :scheme header field value. - const std::string &get_request_http2_scheme() const; - void set_request_http2_scheme(std::string scheme); - // Returns :authority or host header field value. We may deduce it - // from absolute-form HTTP/1 request. We also store authority-form - // HTTP/1 request. This could be empty if request comes from - // HTTP/1.0 without Host header field and origin-form. - const std::string &get_request_http2_authority() const; - void set_request_http2_authority(std::string authority); - void append_request_http2_authority(const char *data, size_t len); - void set_request_major(int major); - void set_request_minor(int minor); - int get_request_major() const; - int get_request_minor() const; int push_request_headers(); bool get_chunked_request() const; void set_chunked_request(bool f); - bool get_request_connection_close() const; - void set_request_connection_close(bool f); - bool get_request_http2_expect_body() const; - void set_request_http2_expect_body(bool f); int push_upload_data_chunk(const uint8_t *data, size_t datalen); int end_upload_data(); size_t get_request_datalen() const; @@ -184,9 +240,6 @@ public: // Validates that received request body length and content-length // matches. bool validate_request_bodylen() const; - int64_t get_request_content_length() const; - void set_request_content_length(int64_t len); - bool request_pseudo_header_allowed(int16_t token) const; void set_request_downstream_host(std::string host); bool expect_response_body() const; enum { @@ -212,58 +265,17 @@ public: bool get_request_pending() const; // Returns true if request is ready to be submitted to downstream. bool request_submission_ready() const; + // downstream response API - const Headers &get_response_headers() const; - Headers &get_response_headers(); - // Lower the response header field names and indexes response - // headers. If there are invalid headers (e.g., multiple - // Content-Length with different values), returns -1. - int index_response_headers(); - // Returns pointer to the response header with the name |name|. If - // multiple header have |name| as name, return last occurrence from - // the beginning. If no such header is found, returns nullptr. - // This function must be called after response headers are indexed. - const Headers::value_type *get_response_header(int16_t token) const; - Headers::value_type *get_response_header(int16_t token); + const Response &response() const { return resp_; } + Response &response() { return resp_; } + // Rewrites the location response header field. void rewrite_location_response_header(const std::string &upstream_scheme); - void add_response_header(std::string name, std::string value); - void set_last_response_header_value(const char *data, size_t len); - void add_response_header(std::string name, std::string value, int16_t token); - void add_response_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - - bool get_response_header_key_prev() const; - void append_last_response_header_key(const char *data, size_t len); - void append_last_response_header_value(const char *data, size_t len); - // Empties response headers. - void clear_response_headers(); - - size_t get_response_headers_sum() const; - - const Headers &get_response_trailers() const; - void add_response_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token); - void add_response_trailer(std::string name, std::string value); - void set_last_response_trailer_value(const char *data, size_t len); - bool get_response_trailer_key_prev() const; - void append_last_response_trailer_key(const char *data, size_t len); - void append_last_response_trailer_value(const char *data, size_t len); - - unsigned int get_response_http_status() const; - void set_response_http_status(unsigned int status); - void set_response_major(int major); - void set_response_minor(int minor); - int get_response_major() const; - int get_response_minor() const; - int get_response_version() const; bool get_chunked_response() const; void set_chunked_response(bool f); - bool get_response_connection_close() const; - void set_response_connection_close(bool f); + void set_response_state(int state); int get_response_state() const; DefaultMemchunks *get_response_buf(); @@ -272,8 +284,6 @@ public: int64_t get_response_bodylen() const; void add_response_sent_bodylen(size_t amount); int64_t get_response_sent_bodylen() const; - int64_t get_response_content_length() const; - void set_response_content_length(int64_t len); // Validates that received response body length and content-length // matches. bool validate_response_bodylen() const; @@ -292,7 +302,6 @@ public: void dec_response_datalen(size_t len); size_t get_response_datalen() const; void reset_response_datalen(); - bool response_pseudo_header_allowed(int16_t token) const; // Call this method when there is incoming data in downstream // connection. @@ -362,24 +371,15 @@ public: Downstream *dlnext, *dlprev; private: - Headers request_headers_; - Headers response_headers_; - - // trailer part. For HTTP/1.1, trailer part is only included with - // chunked encoding. For HTTP/2, there is no such limit. - Headers request_trailers_; - Headers response_trailers_; + Request req_; + Response resp_; std::chrono::high_resolution_clock::time_point request_start_time_; - std::string request_path_; - std::string request_http2_scheme_; - std::string request_http2_authority_; // host we requested to downstream. This is used to rewrite // location header field to decide the location should be rewritten // or not. std::string request_downstream_host_; - std::string assembled_request_cookie_; DefaultMemchunks request_buf_; DefaultMemchunks response_buf_; @@ -398,20 +398,12 @@ private: // the length of response body sent to upstream client int64_t response_sent_bodylen_; - // content-length of request body, -1 if it is unknown. - int64_t request_content_length_; - // content-length of response body, -1 if it is unknown. - int64_t response_content_length_; - Upstream *upstream_; std::unique_ptr dconn_; // only used by HTTP/2 or SPDY upstream BlockedLink *blocked_link_; - size_t request_headers_sum_; - size_t response_headers_sum_; - // The number of bytes not consumed by the application yet. size_t request_datalen_; size_t response_datalen_; @@ -426,40 +418,18 @@ private: // RST_STREAM error_code from downstream HTTP2 connection uint32_t response_rst_stream_error_code_; - int request_method_; int request_state_; - int request_major_; - int request_minor_; - int response_state_; - unsigned int response_http_status_; - int response_major_; - int response_minor_; // only used by HTTP/2 or SPDY upstream int dispatch_state_; - http2::HeaderIndex request_hdidx_; - http2::HeaderIndex response_hdidx_; - - // true if the request contains upgrade token (HTTP Upgrade or - // CONNECT) - bool upgrade_request_; // true if the connection is upgraded (HTTP Upgrade or CONNECT) bool upgraded_; - bool http2_upgrade_seen_; - bool chunked_request_; - bool request_connection_close_; - bool request_header_key_prev_; - bool request_trailer_key_prev_; - bool request_http2_expect_body_; - bool chunked_response_; - bool response_connection_close_; - bool response_header_key_prev_; - bool response_trailer_key_prev_; + bool expect_final_response_; // true if downstream request is pending because backend connection // has not been established or should be checked before use; diff --git a/src/shrpx_downstream_queue.cc b/src/shrpx_downstream_queue.cc index 02fd0356..893effb9 100644 --- a/src/shrpx_downstream_queue.cc +++ b/src/shrpx_downstream_queue.cc @@ -79,7 +79,7 @@ DownstreamQueue::make_host_key(const std::string &host) const { const std::string & DownstreamQueue::make_host_key(Downstream *downstream) const { - return make_host_key(downstream->get_request_http2_authority()); + return make_host_key(downstream->request().authority); } void DownstreamQueue::mark_active(Downstream *downstream) { diff --git a/src/shrpx_downstream_test.cc b/src/shrpx_downstream_test.cc index f74647ea..683bce21 100644 --- a/src/shrpx_downstream_test.cc +++ b/src/shrpx_downstream_test.cc @@ -32,17 +32,17 @@ namespace shrpx { -void test_downstream_index_request_headers(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header("1", "0"); - d.add_request_header("2", "1"); - d.add_request_header("Charlie", "2"); - d.add_request_header("Alpha", "3"); - d.add_request_header("Delta", "4"); - d.add_request_header("BravO", "5"); - d.add_request_header(":method", "6"); - d.add_request_header(":authority", "7"); - d.index_request_headers(); +void test_downstream_field_store_index_headers(void) { + FieldStore fs(0); + fs.add_header("1", "0"); + fs.add_header("2", "1"); + fs.add_header("Charlie", "2"); + fs.add_header("Alpha", "3"); + fs.add_header("Delta", "4"); + fs.add_header("BravO", "5"); + fs.add_header(":method", "6"); + fs.add_header(":authority", "7"); + fs.index_headers(); auto ans = Headers{{"1", "0"}, {"2", "1"}, @@ -52,62 +52,36 @@ void test_downstream_index_request_headers(void) { {"bravo", "5"}, {":method", "6"}, {":authority", "7"}}; - CU_ASSERT(ans == d.get_request_headers()); + CU_ASSERT(ans == fs.headers()); } -void test_downstream_index_response_headers(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_response_header("Charlie", "0"); - d.add_response_header("Alpha", "1"); - d.add_response_header("Delta", "2"); - d.add_response_header("BravO", "3"); - d.index_response_headers(); - - auto ans = - Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}}; - CU_ASSERT(ans == d.get_response_headers()); -} - -void test_downstream_get_request_header(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header("alpha", "0"); - d.add_request_header(":authority", "1"); - d.add_request_header("content-length", "2"); - d.index_request_headers(); +void test_downstream_field_store_header(void) { + FieldStore fs(0); + fs.add_header("alpha", "0"); + fs.add_header(":authority", "1"); + fs.add_header("content-length", "2"); + fs.index_headers(); // By token - CU_ASSERT(Header(":authority", "1") == - *d.get_request_header(http2::HD__AUTHORITY)); - CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD)); + CU_ASSERT(Header(":authority", "1") == *fs.header(http2::HD__AUTHORITY)); + CU_ASSERT(nullptr == fs.header(http2::HD__METHOD)); // By name - CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha")); - CU_ASSERT(nullptr == d.get_request_header("bravo")); -} - -void test_downstream_get_response_header(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_response_header("alpha", "0"); - d.add_response_header(":status", "1"); - d.add_response_header("content-length", "2"); - d.index_response_headers(); - - // By token - CU_ASSERT(Header(":status", "1") == - *d.get_response_header(http2::HD__STATUS)); - CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD)); + CU_ASSERT(Header("alpha", "0") == *fs.header("alpha")); + CU_ASSERT(nullptr == fs.header("bravo")); } void test_downstream_crumble_request_cookie(void) { Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header(":method", "get"); - d.add_request_header(":path", "/"); + auto &req = d.request(); + req.fs.add_header(":method", "get"); + req.fs.add_header(":path", "/"); auto val = "alpha; bravo; ; ;; charlie;;"; - d.add_request_header( + req.fs.add_header( reinterpret_cast("cookie"), sizeof("cookie") - 1, reinterpret_cast(val), strlen(val), true, -1); - d.add_request_header("cookie", ";delta"); - d.add_request_header("cookie", "echo"); + req.fs.add_header("cookie", ";delta"); + req.fs.add_header("cookie", "echo"); std::vector nva; d.crumble_request_cookie(nva); @@ -139,36 +113,40 @@ void test_downstream_crumble_request_cookie(void) { void test_downstream_assemble_request_cookie(void) { Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header(":method", "get"); - d.add_request_header(":path", "/"); - d.add_request_header("cookie", "alpha"); - d.add_request_header("cookie", "bravo;"); - d.add_request_header("cookie", "charlie; "); - d.add_request_header("cookie", "delta;;"); - d.assemble_request_cookie(); - CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie()); + auto &req = d.request(); + req.fs.add_header(":method", "get"); + req.fs.add_header(":path", "/"); + req.fs.add_header("cookie", "alpha"); + req.fs.add_header("cookie", "bravo;"); + req.fs.add_header("cookie", "charlie; "); + req.fs.add_header("cookie", "delta;;"); + CU_ASSERT("alpha; bravo; charlie; delta" == d.assemble_request_cookie()); } void test_downstream_rewrite_location_response_header(void) { { Downstream d(nullptr, nullptr, 0, 0); + auto &req = d.request(); + auto &resp = d.response(); d.set_request_downstream_host("localhost:3000"); - d.add_request_header("host", "localhost"); - d.add_response_header("location", "http://localhost:3000/"); - d.index_request_headers(); - d.index_response_headers(); + req.fs.add_header("host", "localhost"); + resp.fs.add_header("location", "http://localhost:3000/"); + req.fs.index_headers(); + resp.fs.index_headers(); d.rewrite_location_response_header("https"); - auto location = d.get_response_header(http2::HD_LOCATION); + auto location = resp.fs.header(http2::HD_LOCATION); CU_ASSERT("https://localhost/" == (*location).value); } { Downstream d(nullptr, nullptr, 0, 0); + auto &req = d.request(); + auto &resp = d.response(); d.set_request_downstream_host("localhost"); - d.set_request_http2_authority("localhost"); - d.add_response_header("location", "http://localhost:3000/"); - d.index_response_headers(); + req.authority = "localhost"; + resp.fs.add_header("location", "http://localhost:3000/"); + resp.fs.index_headers(); d.rewrite_location_response_header("https"); - auto location = d.get_response_header(http2::HD_LOCATION); + auto location = resp.fs.header(http2::HD_LOCATION); CU_ASSERT("https://localhost/" == (*location).value); } } diff --git a/src/shrpx_downstream_test.h b/src/shrpx_downstream_test.h index 67e8f6bd..13b33744 100644 --- a/src/shrpx_downstream_test.h +++ b/src/shrpx_downstream_test.h @@ -31,10 +31,8 @@ namespace shrpx { -void test_downstream_index_request_headers(void); -void test_downstream_index_response_headers(void); -void test_downstream_get_request_header(void); -void test_downstream_get_response_header(void); +void test_downstream_field_store_index_headers(void); +void test_downstream_field_store_header(void); void test_downstream_crumble_request_cookie(void); void test_downstream_assemble_request_cookie(void); void test_downstream_rewrite_location_response_header(void); diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index d5674f73..e41d585d 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -165,6 +165,7 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, // on the priority, DATA frame may come first. return NGHTTP2_ERR_DEFERRED; } + const auto &req = downstream->request(); auto input = downstream->get_request_buf(); auto nread = input->remove(buf, length); auto input_empty = input->rleft() == 0; @@ -190,13 +191,13 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, // If connection is upgraded, don't set EOF flag, since HTTP/1 // will set MSG_COMPLETE to request state after upgrade response // header is seen. - (!downstream->get_upgrade_request() || + (!req.upgrade_request || (downstream->get_response_state() == Downstream::HEADER_COMPLETE && !downstream->get_upgraded()))) { *data_flags |= NGHTTP2_DATA_FLAG_EOF; - auto &trailers = downstream->get_request_trailers(); + const auto &trailers = req.fs.trailers(); if (!trailers.empty()) { std::vector nva; nva.reserve(trailers.size()); @@ -248,10 +249,11 @@ int Http2DownstreamConnection::push_request_headers() { downstream_->set_request_pending(false); - auto method = downstream_->get_request_method(); - auto no_host_rewrite = get_config()->no_host_rewrite || - get_config()->http2_proxy || - get_config()->client_proxy || method == HTTP_CONNECT; + const auto &req = downstream_->request(); + + auto no_host_rewrite = + get_config()->no_host_rewrite || get_config()->http2_proxy || + get_config()->client_proxy || req.method == HTTP_CONNECT; // http2session_ has already in CONNECTED state, so we can get // addr_idx here. @@ -265,15 +267,12 @@ int Http2DownstreamConnection::push_request_headers() { // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. const char *authority = downstream_hostport; - auto &req_authority = downstream_->get_request_http2_authority(); - if (no_host_rewrite && !req_authority.empty()) { - authority = req_authority.c_str(); + if (no_host_rewrite && !req.authority.empty()) { + authority = req.authority.c_str(); } downstream_->set_request_downstream_host(authority); - auto nheader = downstream_->get_request_headers().size(); - size_t num_cookies = 0; if (!get_config()->http2_no_cookie_crumbling) { num_cookies = downstream_->count_crumble_request_cookie(); @@ -289,34 +288,30 @@ int Http2DownstreamConnection::push_request_headers() { // 7. x-forwarded-proto (optional) // 8. te (optional) auto nva = std::vector(); - nva.reserve(nheader + 8 + num_cookies + + nva.reserve(req.fs.headers().size() + 8 + num_cookies + get_config()->add_request_headers.size()); nva.push_back( - http2::make_nv_lc_nocopy(":method", http2::to_method_string(method))); - - auto &scheme = downstream_->get_request_http2_scheme(); + http2::make_nv_lc_nocopy(":method", http2::to_method_string(req.method))); nva.push_back(http2::make_nv_lc_nocopy(":authority", authority)); - if (method != HTTP_CONNECT) { - assert(!scheme.empty()); + if (req.method != HTTP_CONNECT) { + assert(!req.scheme.empty()); - nva.push_back(http2::make_nv_ls_nocopy(":scheme", scheme)); + nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme)); - auto &path = downstream_->get_request_path(); - if (method == HTTP_OPTIONS && path.empty()) { + if (req.method == HTTP_OPTIONS && req.path.empty()) { nva.push_back(http2::make_nv_ll(":path", "*")); } else { - nva.push_back(http2::make_nv_ls_nocopy(":path", path)); + nva.push_back(http2::make_nv_ls_nocopy(":path", req.path)); } } - http2::copy_headers_to_nva_nocopy(nva, downstream_->get_request_headers()); + http2::copy_headers_to_nva_nocopy(nva, req.fs.headers()); bool chunked_encoding = false; - auto transfer_encoding = - downstream_->get_request_header(http2::HD_TRANSFER_ENCODING); + auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING); if (transfer_encoding && util::strieq_l("chunked", (*transfer_encoding).value)) { chunked_encoding = true; @@ -327,7 +322,7 @@ int Http2DownstreamConnection::push_request_headers() { } std::string xff_value; - auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + auto xff = req.fs.header(http2::HD_X_FORWARDED_FOR); if (get_config()->add_x_forwarded_for) { if (xff && !get_config()->strip_incoming_x_forwarded_for) { xff_value = (*xff).value; @@ -341,13 +336,13 @@ int Http2DownstreamConnection::push_request_headers() { } if (!get_config()->http2_proxy && !get_config()->client_proxy && - downstream_->get_request_method() != HTTP_CONNECT) { + req.method != HTTP_CONNECT) { // We use same protocol with :scheme header field - nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", scheme)); + nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme)); } std::string via_value; - auto via = downstream_->get_request_header(http2::HD_VIA); + auto via = req.fs.header(http2::HD_VIA); if (get_config()->no_via) { if (via) { nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value)); @@ -357,12 +352,11 @@ int Http2DownstreamConnection::push_request_headers() { via_value = (*via).value; via_value += ", "; } - via_value += http::create_via_header_value( - downstream_->get_request_major(), downstream_->get_request_minor()); + via_value += http::create_via_header_value(req.http_major, req.http_minor); nva.push_back(http2::make_nv_ls("via", via_value)); } - auto te = downstream_->get_request_header(http2::HD_TE); + auto te = req.fs.header(http2::HD_TE); // HTTP/1 upstream request can contain keyword other than // "trailers". We just forward "trailers". // TODO more strict handling required here. @@ -382,12 +376,11 @@ int Http2DownstreamConnection::push_request_headers() { DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); } - auto content_length = - downstream_->get_request_header(http2::HD_CONTENT_LENGTH); + auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH); // TODO check content-length: 0 case - if (downstream_->get_request_method() == HTTP_CONNECT || chunked_encoding || - content_length || downstream_->get_request_http2_expect_body()) { + if (req.method == HTTP_CONNECT || chunked_encoding || content_length || + req.http2_expect_body) { // Request-body is expected. nghttp2_data_provider data_prd; data_prd.source.ptr = this; diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 7ab65ed8..477aeda2 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -752,6 +752,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, return 0; } + auto &resp = downstream->response(); + switch (frame->hd.type) { case NGHTTP2_HEADERS: { auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS && @@ -759,15 +761,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, if (trailer) { // just store header fields for trailer part - downstream->add_response_trailer(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + resp.fs.add_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); return 0; } auto token = http2::lookup_token(name, namelen); - downstream->add_response_header(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + resp.fs.add_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); return 0; } case NGHTTP2_PUSH_PROMISE: { @@ -783,10 +785,11 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, assert(promised_downstream); + auto &promised_req = promised_downstream->request(); + auto token = http2::lookup_token(name, namelen); - promised_downstream->add_request_header(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, - token); + promised_req.fs.add_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); return 0; } } @@ -855,18 +858,20 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, int rv; auto upstream = downstream->get_upstream(); + const auto &req = downstream->request(); + auto &resp = downstream->response(); - auto &nva = downstream->get_response_headers(); + auto &nva = resp.fs.headers(); downstream->set_expect_final_response(false); - auto status = downstream->get_response_header(http2::HD__STATUS); + auto status = resp.fs.header(http2::HD__STATUS); // libnghttp2 guarantees this exists and can be parsed auto status_code = http2::parse_http_status_code(status->value); - downstream->set_response_http_status(status_code); - downstream->set_response_major(2); - downstream->set_response_minor(0); + resp.http_status = status_code; + resp.http_major = 2; + resp.http_minor = 0; if (LOG_ENABLED(INFO)) { std::stringstream ss; @@ -902,7 +907,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, downstream->check_upgrade_fulfilled(); if (downstream->get_upgraded()) { - downstream->set_response_connection_close(true); + resp.connection_close = true; // On upgrade sucess, both ends can send data if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { // If resume_read fails, just drop connection. Not ideal. @@ -915,29 +920,24 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, << "HTTP upgrade success. stream_id=" << frame->hd.stream_id; } } else { - auto content_length = - downstream->get_response_header(http2::HD_CONTENT_LENGTH); + auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH); if (content_length) { // libnghttp2 guarantees this can be parsed - auto len = util::parse_uint(content_length->value); - downstream->set_response_content_length(len); + resp.fs.content_length = util::parse_uint(content_length->value); } - if (downstream->get_response_content_length() == -1 && - downstream->expect_response_body()) { + if (resp.fs.content_length == -1 && downstream->expect_response_body()) { // Here we have response body but Content-Length is not known in // advance. - if (downstream->get_request_major() <= 0 || - (downstream->get_request_major() == 1 && - downstream->get_request_minor() == 0)) { + if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) { // We simply close connection for pre-HTTP/1.1 in this case. - downstream->set_response_connection_close(true); + resp.connection_close = true; } else { // Otherwise, use chunked encoding to keep upstream connection // open. In HTTP2, we are supporsed not to receive // transfer-encoding. - downstream->add_response_header("transfer-encoding", "chunked", - http2::HD_TRANSFER_ENCODING); + resp.fs.add_header("transfer-encoding", "chunked", + http2::HD_TRANSFER_ENCODING); downstream->set_chunked_response(true); } } @@ -1892,14 +1892,15 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream, int Http2Session::handle_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { - auto authority = - promised_downstream->get_request_header(http2::HD__AUTHORITY); - auto path = promised_downstream->get_request_header(http2::HD__PATH); - auto method = promised_downstream->get_request_header(http2::HD__METHOD); - auto scheme = promised_downstream->get_request_header(http2::HD__SCHEME); + auto &promised_req = promised_downstream->request(); + + auto authority = promised_req.fs.header(http2::HD__AUTHORITY); + auto path = promised_req.fs.header(http2::HD__PATH); + auto method = promised_req.fs.header(http2::HD__METHOD); + auto scheme = promised_req.fs.header(http2::HD__SCHEME); if (!authority) { - authority = promised_downstream->get_request_header(http2::HD_HOST); + authority = promised_req.fs.header(http2::HD_HOST); } auto method_token = http2::lookup_method_token(method->value); @@ -1914,17 +1915,16 @@ int Http2Session::handle_downstream_push_promise_complete( // TODO Rewrite authority if we enabled rewrite host. But we // really don't know how to rewrite host. Should we use the same // host in associated stream? - promised_downstream->set_request_http2_authority( - http2::value_to_str(authority)); - promised_downstream->set_request_method(method_token); + promised_req.authority = http2::value_to_str(authority); + promised_req.method = method_token; // libnghttp2 ensures that we don't have CONNECT method in // PUSH_PROMISE, and guarantees that :scheme exists. - promised_downstream->set_request_http2_scheme(http2::value_to_str(scheme)); + promised_req.scheme = http2::value_to_str(scheme); // For server-wide OPTIONS request, path is empty. if (method_token != HTTP_OPTIONS || path->value != "*") { - promised_downstream->set_request_path(http2::rewrite_clean_path( - std::begin(path->value), std::end(path->value))); + promised_req.path = http2::rewrite_clean_path(std::begin(path->value), + std::end(path->value)); } promised_downstream->inspect_http2_request(); diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 2c590792..dc227613 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -111,7 +111,7 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { rv = nghttp2_session_upgrade2( session_, reinterpret_cast(settings_payload.c_str()), settings_payload.size(), - http->get_downstream()->get_request_method() == HTTP_HEAD, nullptr); + http->get_downstream()->request().method == HTTP_HEAD, nullptr); if (rv != 0) { if (LOG_ENABLED(INFO)) { ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " @@ -167,20 +167,19 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, return 0; } - if (downstream->get_request_headers_sum() + namelen + valuelen > + auto &req = downstream->request(); + + if (req.fs.buffer_size() + namelen + valuelen > get_config()->header_field_buffer || - downstream->get_request_headers().size() + - downstream->get_request_trailers().size() >= - get_config()->max_header_fields) { + req.fs.num_fields() >= get_config()->max_header_fields) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { return 0; } if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large or many header field size=" - << downstream->get_request_headers_sum() + namelen + - valuelen << ", num=" - << downstream->get_request_headers().size() + 1; + << req.fs.buffer_size() + namelen + valuelen + << ", num=" << req.fs.num_fields() + 1; } // just ignore header fields if this is trailer part. @@ -197,15 +196,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { // just store header fields for trailer part - downstream->add_request_trailer(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + req.fs.add_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); return 0; } auto token = http2::lookup_token(name, namelen); - downstream->add_request_header(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + req.fs.add_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); return 0; } } // namespace @@ -233,10 +232,12 @@ int on_begin_headers_callback(nghttp2_session *session, downstream->reset_upstream_rtimer(); + auto &req = downstream->request(); + // Although, we deprecated minor version from HTTP/2, we supply // minor version 0 to use via header field in a conventional way. - downstream->set_request_major(2); - downstream->set_request_minor(0); + req.http_major = 2; + req.http_minor = 0; upstream->add_pending_downstream(std::move(downstream)); @@ -250,7 +251,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream, return 0; } - auto &nva = downstream->get_request_headers(); + auto &req = downstream->request(); + auto &nva = req.fs.headers(); if (LOG_ENABLED(INFO)) { std::stringstream ss; @@ -265,20 +267,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream, http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); } - auto content_length = - downstream->get_request_header(http2::HD_CONTENT_LENGTH); + auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH); if (content_length) { // libnghttp2 guarantees this can be parsed - auto len = util::parse_uint(content_length->value); - downstream->set_request_content_length(len); + req.fs.content_length = util::parse_uint(content_length->value); } - auto authority = downstream->get_request_header(http2::HD__AUTHORITY); - auto path = downstream->get_request_header(http2::HD__PATH); - auto method = downstream->get_request_header(http2::HD__METHOD); - auto scheme = downstream->get_request_header(http2::HD__SCHEME); - // presence of mandatory header fields are guaranteed by libnghttp2. + auto authority = req.fs.header(http2::HD__AUTHORITY); + auto path = req.fs.header(http2::HD__PATH); + auto method = req.fs.header(http2::HD__METHOD); + auto scheme = req.fs.header(http2::HD__SCHEME); auto method_token = http2::lookup_method_token(method->value); if (method_token == -1) { @@ -294,28 +293,29 @@ int Http2Upstream::on_request_headers(Downstream *downstream, return 0; } - downstream->set_request_method(method_token); - downstream->set_request_http2_scheme(http2::value_to_str(scheme)); + req.method = method_token; + req.scheme = http2::value_to_str(scheme); + // nghttp2 library guarantees either :authority or host exist if (!authority) { - authority = downstream->get_request_header(http2::HD_HOST); + authority = req.fs.header(http2::HD_HOST); } - downstream->set_request_http2_authority(http2::value_to_str(authority)); + + req.authority = http2::value_to_str(authority); if (path) { if (method_token == HTTP_OPTIONS && path->value == "*") { // Server-wide OPTIONS request. Path is empty. } else if (get_config()->http2_proxy || get_config()->client_proxy) { - downstream->set_request_path(http2::value_to_str(path)); + req.path = http2::value_to_str(path); } else { - auto &value = path->value; - downstream->set_request_path( - http2::rewrite_clean_path(std::begin(value), std::end(value))); + const auto &value = path->value; + req.path = http2::rewrite_clean_path(std::begin(value), std::end(value)); } } if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - downstream->set_request_http2_expect_body(true); + req.http2_expect_body = true; } downstream->inspect_http2_request(); @@ -352,8 +352,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream, } void Http2Upstream::start_downstream(Downstream *downstream) { - if (downstream_queue_.can_activate( - downstream->get_request_http2_authority())) { + if (downstream_queue_.can_activate(downstream->request().authority)) { initiate_downstream(downstream); return; } @@ -553,6 +552,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, auto downstream = make_unique(upstream, handler->get_mcpool(), promised_stream_id, 0); + auto &req = downstream->request(); // As long as we use nghttp2_session_mem_send(), setting stream // user data here should not fail. This is because this callback @@ -563,33 +563,28 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, downstream->disable_upstream_rtimer(); - downstream->set_request_major(2); - downstream->set_request_minor(0); + req.http_major = 2; + req.http_minor = 0; for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { auto &nv = frame->push_promise.nva[i]; auto token = http2::lookup_token(nv.name, nv.namelen); switch (token) { case http2::HD__METHOD: - downstream->set_request_method( - http2::lookup_method_token(nv.value, nv.valuelen)); + req.method = http2::lookup_method_token(nv.value, nv.valuelen); break; case http2::HD__SCHEME: - downstream->set_request_http2_scheme( - {nv.value, nv.value + nv.valuelen}); + req.scheme.assign(nv.value, nv.value + nv.valuelen); break; case http2::HD__AUTHORITY: - downstream->set_request_http2_authority( - {nv.value, nv.value + nv.valuelen}); + req.authority.assign(nv.value, nv.value + nv.valuelen); break; case http2::HD__PATH: - downstream->set_request_path( - http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen)); + req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen); break; } - downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, - nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, - token); + req.fs.add_header(nv.name, nv.namelen, nv.value, nv.valuelen, + nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); } downstream->inspect_http2_request(); @@ -1247,6 +1242,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session, assert(body); auto dconn = downstream->get_downstream_connection(); + const auto &resp = downstream->response(); if (body->rleft() == 0 && dconn && downstream->get_response_state() != Downstream::MSG_COMPLETE) { @@ -1268,7 +1264,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session, *data_flags |= NGHTTP2_DATA_FLAG_EOF; if (!downstream->get_upgraded()) { - auto &trailers = downstream->get_response_trailers(); + const auto &trailers = resp.fs.trailers(); if (!trailers.empty()) { std::vector nva; nva.reserve(trailers.size()); @@ -1308,18 +1304,19 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body, data_prd_ptr = &data_prd; } - auto &headers = downstream->get_response_headers(); + const auto &resp = downstream->response(); + + const auto &headers = resp.fs.headers(); auto nva = std::vector(); // 2 for :status and server nva.reserve(2 + headers.size()); std::string status_code_str; - auto response_status_const = - http2::stringify_status(downstream->get_response_http_status()); + auto response_status_const = http2::stringify_status(resp.http_status); if (response_status_const) { nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const)); } else { - status_code_str = util::utos(downstream->get_response_http_status()); + status_code_str = util::utos(resp.http_status); nva.push_back(http2::make_nv_ls(":status", status_code_str)); } @@ -1339,7 +1336,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body, nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index)); } - if (!downstream->get_response_header(http2::HD_SERVER)) { + if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back( http2::make_nv_lc_nocopy("server", get_config()->server_name)); } @@ -1364,8 +1361,10 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body, int Http2Upstream::error_reply(Downstream *downstream, unsigned int status_code) { int rv; + auto &resp = downstream->response(); + auto html = http::create_error_html(status_code); - downstream->set_response_http_status(status_code); + resp.http_status = status_code; auto body = downstream->get_response_buf(); body->append(html.c_str(), html.size()); downstream->set_response_state(Downstream::MSG_COMPLETE); @@ -1433,6 +1432,9 @@ void Http2Upstream::remove_downstream(Downstream *downstream) { int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { int rv; + const auto &req = downstream->request(); + auto &resp = downstream->response(); + if (LOG_ENABLED(INFO)) { if (downstream->get_non_final_response()) { DLOG(INFO, downstream) << "HTTP non-final response header"; @@ -1443,8 +1445,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { if (!get_config()->http2_proxy && !get_config()->client_proxy && !get_config()->no_location_rewrite) { - downstream->rewrite_location_response_header( - downstream->get_request_http2_scheme()); + downstream->rewrite_location_response_header(req.scheme); } #ifdef HAVE_MRUBY @@ -1466,25 +1467,24 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { } #endif // HAVE_MRUBY - size_t nheader = downstream->get_response_headers().size(); auto nva = std::vector(); // 4 means :status and possible server, via and x-http2-push header // field. - nva.reserve(nheader + 4 + get_config()->add_response_headers.size()); + nva.reserve(resp.fs.headers().size() + 4 + + get_config()->add_response_headers.size()); std::string via_value; std::string response_status; - auto response_status_const = - http2::stringify_status(downstream->get_response_http_status()); + auto response_status_const = http2::stringify_status(resp.http_status); if (response_status_const) { nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const)); } else { - response_status = util::utos(downstream->get_response_http_status()); + response_status = util::utos(resp.http_status); nva.push_back(http2::make_nv_ls(":status", response_status)); } if (downstream->get_non_final_response()) { - http2::copy_headers_to_nva(nva, downstream->get_response_headers()); + http2::copy_headers_to_nva(nva, resp.fs.headers()); if (LOG_ENABLED(INFO)) { log_response_headers(downstream, nva); @@ -1494,7 +1494,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { downstream->get_stream_id(), nullptr, nva.data(), nva.size(), nullptr); - downstream->clear_response_headers(); + resp.fs.clear_headers(); if (rv != 0) { ULOG(FATAL, this) << "nghttp2_submit_headers() failed"; @@ -1504,19 +1504,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { return 0; } - http2::copy_headers_to_nva_nocopy(nva, downstream->get_response_headers()); + http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers()); if (!get_config()->http2_proxy && !get_config()->client_proxy) { nva.push_back( http2::make_nv_lc_nocopy("server", get_config()->server_name)); } else { - auto server = downstream->get_response_header(http2::HD_SERVER); + auto server = resp.fs.header(http2::HD_SERVER); if (server) { nva.push_back(http2::make_nv_ls_nocopy("server", (*server).value)); } } - auto via = downstream->get_response_header(http2::HD_VIA); + auto via = resp.fs.header(http2::HD_VIA); if (get_config()->no_via) { if (via) { nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value)); @@ -1526,8 +1526,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { via_value = (*via).value; via_value += ", "; } - via_value += http::create_via_header_value( - downstream->get_response_major(), downstream->get_response_minor()); + via_value += + http::create_via_header_value(resp.http_major, resp.http_minor); nva.push_back(http2::make_nv_ls("via", via_value)); } @@ -1579,11 +1579,9 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { nghttp2_session_get_remote_settings(session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 && !get_config()->http2_proxy && !get_config()->client_proxy && - (downstream->get_stream_id() % 2) && - downstream->get_response_header(http2::HD_LINK) && - downstream->get_response_http_status() == 200 && - (downstream->get_request_method() == HTTP_GET || - downstream->get_request_method() == HTTP_POST)) { + (downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) && + resp.http_status == 200 && + (req.method == HTTP_GET || req.method == HTTP_POST)) { if (prepare_push_promise(downstream) != 0) { // Continue to send response even if push was failed. @@ -1624,9 +1622,11 @@ int Http2Upstream::on_downstream_body_complete(Downstream *downstream) { DLOG(INFO, downstream) << "HTTP response completed"; } + auto &resp = downstream->response(); + if (!downstream->validate_response_bodylen()) { rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); - downstream->set_response_connection_close(true); + resp.connection_close = true; return 0; } @@ -1766,13 +1766,15 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) { const char *base; size_t baselen; - rv = http2::get_pure_path_component(&base, &baselen, - downstream->get_request_path()); + const auto &req = downstream->request(); + const auto &resp = downstream->response(); + + rv = http2::get_pure_path_component(&base, &baselen, req.path); if (rv != 0) { return 0; } - for (auto &kv : downstream->get_response_headers()) { + for (auto &kv : resp.fs.headers()) { if (kv.token != http2::HD_LINK) { continue; } @@ -1782,6 +1784,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) { auto uri = link.uri.first; auto len = link.uri.second - link.uri.first; + const std::string *scheme_ptr, *authority_ptr; std::string scheme, authority, path; rv = http2::construct_push_component(scheme, authority, path, base, @@ -1791,14 +1794,18 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) { } if (scheme.empty()) { - scheme = downstream->get_request_http2_scheme(); + scheme_ptr = &req.scheme; + } else { + scheme_ptr = &scheme; } if (authority.empty()) { - authority = downstream->get_request_http2_authority(); + authority_ptr = &req.authority; + } else { + authority_ptr = &authority; } - rv = submit_push_promise(scheme, authority, path, downstream); + rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream); if (rv != 0) { return -1; } @@ -1811,9 +1818,11 @@ int Http2Upstream::submit_push_promise(const std::string &scheme, const std::string &authority, const std::string &path, Downstream *downstream) { + const auto &req = downstream->request(); + std::vector nva; // 4 for :method, :scheme, :path and :authority - nva.reserve(4 + downstream->get_request_headers().size()); + nva.reserve(4 + req.fs.headers().size()); // juse use "GET" for now nva.push_back(http2::make_nv_ll(":method", "GET")); @@ -1821,7 +1830,7 @@ int Http2Upstream::submit_push_promise(const std::string &scheme, nva.push_back(http2::make_nv_ls(":path", path)); nva.push_back(http2::make_nv_ls(":authority", authority)); - for (auto &kv : downstream->get_request_headers()) { + for (auto &kv : req.fs.headers()) { switch (kv.token) { // TODO generate referer case http2::HD__AUTHORITY: @@ -1884,12 +1893,14 @@ int Http2Upstream::initiate_push(Downstream *downstream, const char *uri, const char *base; size_t baselen; - rv = http2::get_pure_path_component(&base, &baselen, - downstream->get_request_path()); + const auto &req = downstream->request(); + + rv = http2::get_pure_path_component(&base, &baselen, req.path); if (rv != 0) { return -1; } + const std::string *scheme_ptr, *authority_ptr; std::string scheme, authority, path; rv = http2::construct_push_component(scheme, authority, path, base, baselen, @@ -1899,14 +1910,18 @@ int Http2Upstream::initiate_push(Downstream *downstream, const char *uri, } if (scheme.empty()) { - scheme = downstream->get_request_http2_scheme(); + scheme_ptr = &req.scheme; + } else { + scheme_ptr = &scheme; } if (authority.empty()) { - authority = downstream->get_request_http2_authority(); + authority_ptr = &req.authority; + } else { + authority_ptr = &authority; } - rv = submit_push_promise(scheme, authority, path, downstream); + rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream); if (rv != 0) { return -1; @@ -1939,12 +1954,14 @@ Http2Upstream::on_downstream_push_promise(Downstream *downstream, // frontend. auto promised_downstream = make_unique(this, handler_->get_mcpool(), 0, 0); + auto &promised_req = promised_downstream->request(); + promised_downstream->set_downstream_stream_id(promised_stream_id); promised_downstream->disable_upstream_rtimer(); - promised_downstream->set_request_major(2); - promised_downstream->set_request_minor(0); + promised_req.http_major = 2; + promised_req.http_minor = 0; auto ptr = promised_downstream.get(); add_pending_downstream(std::move(promised_downstream)); @@ -1957,7 +1974,8 @@ int Http2Upstream::on_downstream_push_promise_complete( Downstream *downstream, Downstream *promised_downstream) { std::vector nva; - auto &headers = promised_downstream->get_request_headers(); + const auto &promised_req = promised_downstream->request(); + const auto &headers = promised_req.fs.headers(); nva.reserve(headers.size()); diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index bab1c902..7d985fb6 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -54,9 +54,10 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { auto downstream = dconn->get_downstream(); auto upstream = downstream->get_upstream(); auto handler = upstream->get_client_handler(); + auto &resp = downstream->response(); // Do this so that dconn is not pooled - downstream->set_response_connection_close(true); + resp.connection_close = true; if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) { delete handler; @@ -214,85 +215,80 @@ int HttpDownstreamConnection::push_request_headers() { ->downstream_addr_groups[group_] .addrs[addr_idx_] .hostport.get(); - auto method = downstream_->get_request_method(); - auto connect_method = method == HTTP_CONNECT; + const auto &req = downstream_->request(); + + auto connect_method = req.method == HTTP_CONNECT; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. const char *authority = downstream_hostport; - auto &req_authority = downstream_->get_request_http2_authority(); auto no_host_rewrite = get_config()->no_host_rewrite || get_config()->http2_proxy || get_config()->client_proxy || connect_method; - if (no_host_rewrite && !req_authority.empty()) { - authority = req_authority.c_str(); + if (no_host_rewrite && !req.authority.empty()) { + authority = req.authority.c_str(); } auto authoritylen = strlen(authority); downstream_->set_request_downstream_host(authority); - downstream_->assemble_request_cookie(); - auto buf = downstream_->get_request_buf(); // Assume that method and request path do not contain \r\n. - auto meth = http2::to_method_string(method); + auto meth = http2::to_method_string(req.method); buf->append(meth, strlen(meth)); buf->append(" "); - auto &scheme = downstream_->get_request_http2_scheme(); - auto &path = downstream_->get_request_path(); - if (connect_method) { buf->append(authority, authoritylen); } else if (get_config()->http2_proxy || get_config()->client_proxy) { // Construct absolute-form request target because we are going to // send a request to a HTTP/1 proxy. - assert(!scheme.empty()); - buf->append(scheme); + assert(!req.scheme.empty()); + buf->append(req.scheme); buf->append("://"); buf->append(authority, authoritylen); - buf->append(path); - } else if (method == HTTP_OPTIONS && path.empty()) { + buf->append(req.path); + } else if (req.method == HTTP_OPTIONS && req.path.empty()) { // Server-wide OPTIONS buf->append("*"); } else { - buf->append(path); + buf->append(req.path); } buf->append(" HTTP/1.1\r\nHost: "); buf->append(authority, authoritylen); buf->append("\r\n"); - http2::build_http1_headers_from_headers(buf, - downstream_->get_request_headers()); + http2::build_http1_headers_from_headers(buf, req.fs.headers()); - if (!downstream_->get_assembled_request_cookie().empty()) { + auto cookie = downstream_->assemble_request_cookie(); + if (!cookie.empty()) { buf->append("Cookie: "); - buf->append(downstream_->get_assembled_request_cookie()); + buf->append(cookie); buf->append("\r\n"); } - if (!connect_method && downstream_->get_request_http2_expect_body() && - !downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) { + if (!connect_method && req.http2_expect_body && + !req.fs.header(http2::HD_CONTENT_LENGTH)) { downstream_->set_chunked_request(true); buf->append("Transfer-Encoding: chunked\r\n"); } - if (downstream_->get_request_connection_close()) { + if (req.connection_close) { buf->append("Connection: close\r\n"); } - if (!connect_method && downstream_->get_upgrade_request()) { - auto connection = downstream_->get_request_header(http2::HD_CONNECTION); + if (!connect_method && req.upgrade_request) { + auto connection = req.fs.header(http2::HD_CONNECTION); if (connection) { buf->append("Connection: "); buf->append((*connection).value); buf->append("\r\n"); } - auto upgrade = downstream_->get_request_header(http2::HD_UPGRADE); + auto upgrade = req.fs.header(http2::HD_UPGRADE); if (upgrade) { buf->append("Upgrade: "); buf->append((*upgrade).value); @@ -300,7 +296,7 @@ int HttpDownstreamConnection::push_request_headers() { } } - auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + auto xff = req.fs.header(http2::HD_X_FORWARDED_FOR); if (get_config()->add_x_forwarded_for) { buf->append("X-Forwarded-For: "); if (xff && !get_config()->strip_incoming_x_forwarded_for) { @@ -317,11 +313,11 @@ int HttpDownstreamConnection::push_request_headers() { if (!get_config()->http2_proxy && !get_config()->client_proxy && !connect_method) { buf->append("X-Forwarded-Proto: "); - assert(!scheme.empty()); - buf->append(scheme); + assert(!req.scheme.empty()); + buf->append(req.scheme); buf->append("\r\n"); } - auto via = downstream_->get_request_header(http2::HD_VIA); + auto via = req.fs.header(http2::HD_VIA); if (get_config()->no_via) { if (via) { buf->append("Via: "); @@ -334,8 +330,7 @@ int HttpDownstreamConnection::push_request_headers() { buf->append((*via).value); buf->append(", "); } - buf->append(http::create_via_header_value( - downstream_->get_request_major(), downstream_->get_request_minor())); + buf->append(http::create_via_header_value(req.http_major, req.http_minor)); buf->append("\r\n"); } @@ -392,8 +387,10 @@ int HttpDownstreamConnection::end_upload_data() { return 0; } + const auto &req = downstream_->request(); + auto output = downstream_->get_request_buf(); - auto &trailers = downstream_->get_request_trailers(); + const auto &trailers = req.fs.trailers(); if (trailers.empty()) { output->append("0\r\n\r\n"); } else { @@ -484,13 +481,15 @@ namespace { int htp_hdrs_completecb(http_parser *htp) { auto downstream = static_cast(htp->data); auto upstream = downstream->get_upstream(); + const auto &req = downstream->request(); + auto &resp = downstream->response(); int rv; - downstream->set_response_http_status(htp->status_code); - downstream->set_response_major(htp->http_major); - downstream->set_response_minor(htp->http_minor); + resp.http_status = htp->status_code; + resp.http_major = htp->http_major; + resp.http_minor = htp->http_minor; - if (downstream->index_response_headers() != 0) { + if (resp.fs.index_headers() != 0) { downstream->set_response_state(Downstream::MSG_BAD_HEADER); return -1; } @@ -502,7 +501,7 @@ int htp_hdrs_completecb(http_parser *htp) { if (downstream->get_non_final_response()) { // Reset content-length because we reuse same Downstream for the // next response. - downstream->set_response_content_length(-1); + resp.fs.content_length = -1; // For non-final response code, we just call // on_downstream_header_complete() without changing response // state. @@ -516,13 +515,13 @@ int htp_hdrs_completecb(http_parser *htp) { return 1; } - downstream->set_response_connection_close(!http_should_keep_alive(htp)); + resp.connection_close = !http_should_keep_alive(htp); downstream->set_response_state(Downstream::HEADER_COMPLETE); downstream->inspect_http1_response(); if (downstream->get_upgraded()) { // content-length must be ignored for upgraded connection. - downstream->set_response_content_length(-1); - downstream->set_response_connection_close(true); + resp.fs.content_length = -1; + resp.connection_close = true; // transfer-encoding not applied to upgraded connection downstream->set_chunked_response(false); } @@ -542,7 +541,7 @@ int htp_hdrs_completecb(http_parser *htp) { } } - unsigned int status = downstream->get_response_http_status(); + auto status = resp.http_status; // Ignore the response body. HEAD response may contain // Content-Length or Transfer-Encoding: chunked. Some server send // 304 status code with nonzero Content-Length, but without response @@ -551,9 +550,8 @@ int htp_hdrs_completecb(http_parser *htp) { // TODO It seems that the cases other than HEAD are handled by // http-parser. Need test. - return downstream->get_request_method() == HTTP_HEAD || - (100 <= status && status <= 199) || status == 204 || - status == 304 + return req.method == HTTP_HEAD || (100 <= status && status <= 199) || + status == 204 || status == 304 ? 1 : 0; } @@ -562,18 +560,20 @@ int htp_hdrs_completecb(http_parser *htp) { namespace { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { auto downstream = static_cast(htp->data); + auto &resp = downstream->response(); + if (downstream->get_response_state() == Downstream::INITIAL) { - if (downstream->get_response_header_key_prev()) { - downstream->append_last_response_header_key(data, len); + if (resp.fs.header_key_prev()) { + resp.fs.append_last_header_key(data, len); } else { - downstream->add_response_header(std::string(data, len), ""); + resp.fs.add_header(std::string(data, len), ""); } } else { // trailer part - if (downstream->get_response_trailer_key_prev()) { - downstream->append_last_response_trailer_key(data, len); + if (resp.fs.trailer_key_prev()) { + resp.fs.append_last_trailer_key(data, len); } else { - downstream->add_response_trailer(std::string(data, len), ""); + resp.fs.add_trailer(std::string(data, len), ""); } } return 0; @@ -583,18 +583,12 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { namespace { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { auto downstream = static_cast(htp->data); + auto &resp = downstream->response(); + if (downstream->get_response_state() == Downstream::INITIAL) { - if (downstream->get_response_header_key_prev()) { - downstream->set_last_response_header_value(data, len); - } else { - downstream->append_last_response_header_value(data, len); - } + resp.fs.append_last_header_value(data, len); } else { - if (downstream->get_response_trailer_key_prev()) { - downstream->set_last_response_trailer_value(data, len); - } else { - downstream->append_last_response_trailer_value(data, len); - } + resp.fs.append_last_trailer_value(data, len); } return 0; } diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 8f584749..2ca2fe8f 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -85,25 +85,27 @@ namespace { int htp_uricb(http_parser *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); + auto &req = downstream->request(); // We happen to have the same value for method token. - downstream->set_request_method(htp->method); + req.method = htp->method; - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { + if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large URI size=" - << downstream->get_request_headers_sum() + len; + << req.fs.buffer_size() + len; } assert(downstream->get_request_state() == Downstream::INITIAL); downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); return -1; } - downstream->add_request_headers_sum(len); - if (downstream->get_request_method() == HTTP_CONNECT) { - downstream->append_request_http2_authority(data, len); + + req.fs.add_extra_buffer_size(len); + + if (req.method == HTTP_CONNECT) { + req.authority.append(data, len); } else { - downstream->append_request_path(data, len); + req.path.append(data, len); } return 0; @@ -114,11 +116,12 @@ namespace { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { + auto &req = downstream->request(); + + if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" - << downstream->get_request_headers_sum() + len; + << req.fs.buffer_size() + len; } if (downstream->get_request_state() == Downstream::INITIAL) { downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); @@ -126,36 +129,33 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { return -1; } if (downstream->get_request_state() == Downstream::INITIAL) { - if (downstream->get_request_header_key_prev()) { - downstream->append_last_request_header_key(data, len); + if (req.fs.header_key_prev()) { + req.fs.append_last_header_key(data, len); } else { - if (downstream->get_request_headers().size() >= - get_config()->max_header_fields) { + if (req.fs.num_fields() >= get_config()->max_header_fields) { if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too many header field num=" - << downstream->get_request_headers().size() + 1; + ULOG(INFO, upstream) + << "Too many header field num=" << req.fs.num_fields() + 1; } downstream->set_request_state( Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); return -1; } - downstream->add_request_header(std::string(data, len), ""); + req.fs.add_header(std::string(data, len), ""); } } else { // trailer part - if (downstream->get_request_trailer_key_prev()) { - downstream->append_last_request_trailer_key(data, len); + if (req.fs.trailer_key_prev()) { + req.fs.append_last_trailer_key(data, len); } else { - if (downstream->get_request_headers().size() + - downstream->get_request_trailers().size() >= - get_config()->max_header_fields) { + if (req.fs.num_fields() >= get_config()->max_header_fields) { if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too many header field num=" - << downstream->get_request_headers().size() + 1; + ULOG(INFO, upstream) + << "Too many header field num=" << req.fs.num_fields() + 1; } return -1; } - downstream->add_request_trailer(std::string(data, len), ""); + req.fs.add_trailer(std::string(data, len), ""); } } return 0; @@ -166,11 +166,12 @@ namespace { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { + auto &req = downstream->request(); + + if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" - << downstream->get_request_headers_sum() + len; + << req.fs.buffer_size() + len; } if (downstream->get_request_state() == Downstream::INITIAL) { downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); @@ -178,30 +179,23 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { return -1; } if (downstream->get_request_state() == Downstream::INITIAL) { - if (downstream->get_request_header_key_prev()) { - downstream->set_last_request_header_value(data, len); - } else { - downstream->append_last_request_header_value(data, len); - } + req.fs.append_last_header_value(data, len); } else { - if (downstream->get_request_trailer_key_prev()) { - downstream->set_last_request_trailer_value(data, len); - } else { - downstream->append_last_request_trailer_value(data, len); - } + req.fs.append_last_trailer_value(data, len); } return 0; } } // namespace namespace { -void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, +void rewrite_request_host_path_from_uri(Request &req, const char *uri, http_parser_url &u) { assert(u.field_set & (1 << UF_HOST)); + auto &authority = req.authority; + authority.clear(); // As per https://tools.ietf.org/html/rfc7230#section-5.4, we // rewrite host header field with authority component. - std::string authority; http2::copy_url_component(authority, &u, UF_HOST, uri); // TODO properly check IPv6 numeric address if (authority.find(':') != std::string::npos) { @@ -212,23 +206,20 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, authority += ':'; authority += util::utos(u.port); } - downstream->set_request_http2_authority(authority); - std::string scheme; - http2::copy_url_component(scheme, &u, UF_SCHEMA, uri); - downstream->set_request_http2_scheme(std::move(scheme)); + http2::copy_url_component(req.scheme, &u, UF_SCHEMA, uri); std::string path; if (u.field_set & (1 << UF_PATH)) { http2::copy_url_component(path, &u, UF_PATH, uri); - } else if (downstream->get_request_method() == HTTP_OPTIONS) { + } else if (req.method == HTTP_OPTIONS) { // Server-wide OPTIONS takes following form in proxy request: // // OPTIONS http://example.org HTTP/1.1 // // Notice that no slash after authority. See // http://tools.ietf.org/html/rfc7230#section-5.3.4 - downstream->set_request_path(""); + req.path = ""; // we ignore query component here return; } else { @@ -240,10 +231,9 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, path.append(uri + fdata.off, fdata.len); } if (get_config()->http2_proxy || get_config()->client_proxy) { - downstream->set_request_path(std::move(path)); + req.path = std::move(path); } else { - downstream->set_request_path( - http2::rewrite_clean_path(std::begin(path), std::end(path))); + req.path = http2::rewrite_clean_path(std::begin(path), std::end(path)); } } } // namespace @@ -256,36 +246,35 @@ int htp_hdrs_completecb(http_parser *htp) { ULOG(INFO, upstream) << "HTTP request headers completed"; } auto downstream = upstream->get_downstream(); + auto &req = downstream->request(); + auto &resp = downstream->response(); - downstream->set_request_major(htp->http_major); - downstream->set_request_minor(htp->http_minor); + req.http_major = htp->http_major; + req.http_minor = htp->http_minor; - downstream->set_request_connection_close(!http_should_keep_alive(htp)); + req.connection_close = !http_should_keep_alive(htp); - auto method = downstream->get_request_method(); + auto method = req.method; if (LOG_ENABLED(INFO)) { std::stringstream ss; ss << http2::to_method_string(method) << " " - << (method == HTTP_CONNECT ? downstream->get_request_http2_authority() - : downstream->get_request_path()) << " " - << "HTTP/" << downstream->get_request_major() << "." - << downstream->get_request_minor() << "\n"; - const auto &headers = downstream->get_request_headers(); - for (size_t i = 0; i < headers.size(); ++i) { - ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": " - << headers[i].value << "\n"; + << (method == HTTP_CONNECT ? req.authority : req.path) << " " + << "HTTP/" << req.http_major << "." << req.http_minor << "\n"; + + for (const auto &kv : req.fs.headers()) { + ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n"; } + ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); } - if (downstream->index_request_headers() != 0) { + if (req.fs.index_headers() != 0) { return -1; } - if (downstream->get_request_major() == 1 && - downstream->get_request_minor() == 1 && - !downstream->get_request_header(http2::HD_HOST)) { + if (req.http_major == 1 && req.http_minor == 1 && + !req.fs.header(http2::HD_HOST)) { return -1; } @@ -295,7 +284,7 @@ int htp_hdrs_completecb(http_parser *htp) { http_parser_url u{}; // make a copy of request path, since we may set request path // while we are refering to original request path. - auto path = downstream->get_request_path(); + auto path = req.path; rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u); if (rv != 0) { // Expect to respond with 400 bad request @@ -309,24 +298,23 @@ int htp_hdrs_completecb(http_parser *htp) { } if (method == HTTP_OPTIONS && path == "*") { - downstream->set_request_path(""); + req.path = ""; } else { - downstream->set_request_path( - http2::rewrite_clean_path(std::begin(path), std::end(path))); + req.path = http2::rewrite_clean_path(std::begin(path), std::end(path)); } - auto host = downstream->get_request_header(http2::HD_HOST); + auto host = req.fs.header(http2::HD_HOST); if (host) { - downstream->set_request_http2_authority(host->value); + req.authority = host->value; } if (upstream->get_client_handler()->get_ssl()) { - downstream->set_request_http2_scheme("https"); + req.scheme = "https"; } else { - downstream->set_request_http2_scheme("http"); + req.scheme = "http"; } } else { - rewrite_request_host_path_from_uri(downstream, path.c_str(), u); + rewrite_request_host_path_from_uri(req, path.c_str(), u); } } @@ -338,7 +326,7 @@ int htp_hdrs_completecb(http_parser *htp) { auto mruby_ctx = worker->get_mruby_context(); if (mruby_ctx->run_on_request_proc(downstream) != 0) { - downstream->set_response_http_status(500); + resp.http_status = 500; return -1; } #endif // HAVE_MRUBY @@ -527,7 +515,7 @@ int HttpsUpstream::on_read() { if (htperr == HPE_INVALID_METHOD) { status_code = 501; } else if (downstream) { - status_code = downstream->get_response_http_status(); + status_code = downstream->response().http_status; if (status_code == 0) { if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { status_code = 503; @@ -571,6 +559,7 @@ int HttpsUpstream::on_write() { auto dconn = downstream->get_downstream_connection(); auto output = downstream->get_response_buf(); + const auto &resp = downstream->response(); if (output->rleft() == 0 && dconn && downstream->get_response_state() != Downstream::MSG_COMPLETE) { @@ -591,7 +580,7 @@ int HttpsUpstream::on_write() { // We need to postpone detachment until all data are sent so that // we can notify nghttp2 library all data consumed. if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - if (downstream->get_response_connection_close() || + if (resp.connection_close || downstream->get_request_state() != Downstream::MSG_COMPLETE) { // Connection close downstream->pop_downstream_connection(); @@ -766,32 +755,31 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) { int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body, size_t bodylen) { - auto major = downstream->get_request_major(); - auto minor = downstream->get_request_minor(); + const auto &req = downstream->request(); + auto &resp = downstream->response(); auto connection_close = false; - if (major <= 0 || (major == 1 && minor == 0)) { + if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) { connection_close = true; } else { - auto c = downstream->get_response_header(http2::HD_CONNECTION); + auto c = resp.fs.header(http2::HD_CONNECTION); if (c && util::strieq_l("close", c->value)) { connection_close = true; } } if (connection_close) { - downstream->set_response_connection_close(true); + resp.connection_close = true; handler_->set_should_close_after_write(true); } auto output = downstream->get_response_buf(); output->append("HTTP/1.1 "); - output->append( - http2::get_status_string(downstream->get_response_http_status())); + output->append(http2::get_status_string(resp.http_status)); output->append("\r\n"); - for (auto &kv : downstream->get_response_headers()) { + for (auto &kv : resp.fs.headers()) { if (kv.name.empty() || kv.name[0] == ':') { continue; } @@ -801,7 +789,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body, output->append("\r\n"); } - if (!downstream->get_response_header(http2::HD_SERVER)) { + if (!resp.fs.header(http2::HD_SERVER)) { output->append("Server: "); output->append(get_config()->server_name, strlen(get_config()->server_name)); @@ -828,10 +816,12 @@ void HttpsUpstream::error_reply(unsigned int status_code) { downstream = get_downstream(); } - downstream->set_response_http_status(status_code); + auto &resp = downstream->response(); + + resp.http_status = status_code; // we are going to close connection for both frontend and backend in // error condition. This is safest option. - downstream->set_response_connection_close(true); + resp.connection_close = true; handler_->set_should_close_after_write(true); auto output = downstream->get_response_buf(); @@ -896,6 +886,9 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } } + const auto &req = downstream->request(); + auto &resp = downstream->response(); + #ifdef HAVE_MRUBY if (!downstream->get_non_final_response()) { auto worker = handler_->get_worker(); @@ -912,16 +905,16 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } #endif // HAVE_MRUBY - auto connect_method = downstream->get_request_method() == HTTP_CONNECT; + auto connect_method = req.method == HTTP_CONNECT; auto buf = downstream->get_response_buf(); buf->append("HTTP/"); - buf->append(util::utos(downstream->get_request_major())); + buf->append(util::utos(req.http_major)); buf->append("."); - buf->append(util::utos(downstream->get_request_minor())); + buf->append(util::utos(req.http_minor)); buf->append(" "); - buf->append(http2::get_status_string(downstream->get_response_http_status())); + buf->append(http2::get_status_string(resp.http_status)); buf->append("\r\n"); if (!get_config()->http2_proxy && !get_config()->client_proxy && @@ -930,8 +923,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { get_client_handler()->get_upstream_scheme()); } - http2::build_http1_headers_from_headers(buf, - downstream->get_response_headers()); + http2::build_http1_headers_from_headers(buf, resp.fs.headers()); if (downstream->get_non_final_response()) { buf->append("\r\n"); @@ -940,7 +932,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { log_response_headers(buf); } - downstream->clear_response_headers(); + resp.fs.clear_headers(); return 0; } @@ -950,15 +942,13 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { // after graceful shutdown commenced, add connection: close header // field. if (worker->get_graceful_shutdown()) { - downstream->set_response_connection_close(true); + resp.connection_close = true; } // We check downstream->get_response_connection_close() in case when // the Content-Length is not available. - if (!downstream->get_request_connection_close() && - !downstream->get_response_connection_close()) { - if (downstream->get_request_major() <= 0 || - downstream->get_request_minor() <= 0) { + if (!req.connection_close && !resp.connection_close) { + if (req.http_major <= 0 || req.http_minor <= 0) { // We add this header for HTTP/1.0 or HTTP/0.9 clients buf->append("Connection: Keep-Alive\r\n"); } @@ -967,14 +957,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } if (!connect_method && downstream->get_upgraded()) { - auto connection = downstream->get_response_header(http2::HD_CONNECTION); + auto connection = resp.fs.header(http2::HD_CONNECTION); if (connection) { buf->append("Connection: "); buf->append((*connection).value); buf->append("\r\n"); } - auto upgrade = downstream->get_response_header(http2::HD_UPGRADE); + auto upgrade = resp.fs.header(http2::HD_UPGRADE); if (upgrade) { buf->append("Upgrade: "); buf->append((*upgrade).value); @@ -982,7 +972,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } } - if (!downstream->get_response_header(http2::HD_ALT_SVC)) { + if (!resp.fs.header(http2::HD_ALT_SVC)) { // We won't change or alter alt-svc from backend for now if (!get_config()->altsvcs.empty()) { buf->append("Alt-Svc: "); @@ -1002,7 +992,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { buf->append(get_config()->server_name, strlen(get_config()->server_name)); buf->append("\r\n"); } else { - auto server = downstream->get_response_header(http2::HD_SERVER); + auto server = resp.fs.header(http2::HD_SERVER); if (server) { buf->append("Server: "); buf->append((*server).value); @@ -1010,7 +1000,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } } - auto via = downstream->get_response_header(http2::HD_VIA); + auto via = resp.fs.header(http2::HD_VIA); if (get_config()->no_via) { if (via) { buf->append("Via: "); @@ -1023,8 +1013,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { buf->append((*via).value); buf->append(", "); } - buf->append(http::create_via_header_value( - downstream->get_response_major(), downstream->get_response_minor())); + buf->append( + http::create_via_header_value(resp.http_major, resp.http_minor)); buf->append("\r\n"); } @@ -1068,9 +1058,12 @@ int HttpsUpstream::on_downstream_body(Downstream *downstream, } int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { + const auto &req = downstream->request(); + auto &resp = downstream->response(); + if (downstream->get_chunked_response()) { auto output = downstream->get_response_buf(); - auto &trailers = downstream->get_response_trailers(); + const auto &trailers = resp.fs.trailers(); if (trailers.empty()) { output->append("0\r\n\r\n"); } else { @@ -1084,11 +1077,10 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { } if (!downstream->validate_response_bodylen()) { - downstream->set_response_connection_close(true); + resp.connection_close = true; } - if (downstream->get_request_connection_close() || - downstream->get_response_connection_close()) { + if (req.connection_close || resp.connection_close) { auto handler = get_client_handler(); handler->set_should_close_after_write(true); } diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc index 6055f3e9..10c398b4 100644 --- a/src/shrpx_log.cc +++ b/src/shrpx_log.cc @@ -220,6 +220,11 @@ void upstream_accesslog(const std::vector &lfv, auto downstream = lgsp.downstream; + const Request *req = nullptr; + if (downstream) { + req = &downstream->request(); + } + auto p = buf; auto avail = sizeof(buf) - 2; @@ -259,8 +264,8 @@ void upstream_accesslog(const std::vector &lfv, std::tie(p, avail) = copy(util::utos(lgsp.body_bytes_sent), avail, p); break; case SHRPX_LOGF_HTTP: - if (downstream) { - auto hd = downstream->get_request_header(lf.value.get()); + if (req) { + auto hd = req->fs.header(lf.value.get()); if (hd) { std::tie(p, avail) = copy((*hd).value, avail, p); break; @@ -271,10 +276,9 @@ void upstream_accesslog(const std::vector &lfv, break; case SHRPX_LOGF_AUTHORITY: - if (downstream) { - auto &authority = downstream->get_request_http2_authority(); - if (!authority.empty()) { - std::tie(p, avail) = copy(authority, avail, p); + if (req) { + if (!req->authority.empty()) { + std::tie(p, avail) = copy(req->authority, avail, p); break; } } diff --git a/src/shrpx_mruby.cc b/src/shrpx_mruby.cc index 7c6f5d40..5b1b3967 100644 --- a/src/shrpx_mruby.cc +++ b/src/shrpx_mruby.cc @@ -96,11 +96,11 @@ int MRubyContext::run_app(Downstream *downstream, int phase) { mrb_->ud = nullptr; if (data.request_headers_dirty) { - downstream->index_request_headers(); + downstream->request().fs.index_headers(); } if (data.response_headers_dirty) { - downstream->index_response_headers(); + downstream->response().fs.index_headers(); } return rv; diff --git a/src/shrpx_mruby_module_request.cc b/src/shrpx_mruby_module_request.cc index 10ef99ec..0b1ddb15 100644 --- a/src/shrpx_mruby_module_request.cc +++ b/src/shrpx_mruby_module_request.cc @@ -49,7 +49,8 @@ namespace { mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return mrb_fixnum_value(downstream->get_request_major()); + const auto &req = downstream->request(); + return mrb_fixnum_value(req.http_major); } } // namespace @@ -57,7 +58,8 @@ namespace { mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return mrb_fixnum_value(downstream->get_request_minor()); + const auto &req = downstream->request(); + return mrb_fixnum_value(req.http_minor); } } // namespace @@ -65,7 +67,8 @@ namespace { mrb_value request_get_method(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - auto method = http2::to_method_string(downstream->get_request_method()); + const auto &req = downstream->request(); + auto method = http2::to_method_string(req.method); return mrb_str_new_cstr(mrb, method); } @@ -75,6 +78,7 @@ namespace { mrb_value request_set_method(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); @@ -90,7 +94,7 @@ mrb_value request_set_method(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported"); } - downstream->set_request_method(token); + req.method = token; return self; } @@ -100,9 +104,9 @@ namespace { mrb_value request_get_authority(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - auto &authority = downstream->get_request_http2_authority(); + const auto &req = downstream->request(); - return mrb_str_new(mrb, authority.c_str(), authority.size()); + return mrb_str_new(mrb, req.authority.c_str(), req.authority.size()); } } // namespace @@ -110,6 +114,7 @@ namespace { mrb_value request_set_authority(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); @@ -120,7 +125,7 @@ mrb_value request_set_authority(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string"); } - downstream->set_request_http2_authority(std::string(authority, n)); + req.authority.assign(authority, n); return self; } @@ -130,9 +135,9 @@ namespace { mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - auto &scheme = downstream->get_request_http2_scheme(); + const auto &req = downstream->request(); - return mrb_str_new(mrb, scheme.c_str(), scheme.size()); + return mrb_str_new(mrb, req.scheme.c_str(), req.scheme.size()); } } // namespace @@ -140,6 +145,7 @@ namespace { mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); @@ -150,7 +156,7 @@ mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) { mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string"); } - downstream->set_request_http2_scheme(std::string(scheme, n)); + req.scheme.assign(scheme, n); return self; } @@ -160,9 +166,9 @@ namespace { mrb_value request_get_path(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - auto &path = downstream->get_request_path(); + const auto &req = downstream->request(); - return mrb_str_new(mrb, path.c_str(), path.size()); + return mrb_str_new(mrb, req.path.c_str(), req.path.size()); } } // namespace @@ -170,6 +176,7 @@ namespace { mrb_value request_set_path(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); @@ -177,7 +184,7 @@ mrb_value request_set_path(mrb_state *mrb, mrb_value self) { mrb_int pathlen; mrb_get_args(mrb, "s", &path, &pathlen); - downstream->set_request_path(std::string(path, pathlen)); + req.path.assign(path, pathlen); return self; } @@ -187,7 +194,8 @@ namespace { mrb_value request_get_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return create_headers_hash(mrb, downstream->get_request_headers()); + const auto &req = downstream->request(); + return create_headers_hash(mrb, req.fs.headers()); } } // namespace @@ -195,6 +203,7 @@ namespace { mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); @@ -209,7 +218,7 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) { if (repl) { size_t p = 0; - auto &headers = downstream->get_request_headers(); + auto &headers = req.fs.headers(); for (size_t i = 0; i < headers.size(); ++i) { auto &hd = headers[i]; if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key), @@ -227,14 +236,12 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto n = mrb_ary_len(mrb, values); for (int i = 0; i < n; ++i) { auto value = mrb_ary_entry(values, i); - downstream->add_request_header( - std::string(RSTRING_PTR(key), RSTRING_LEN(key)), - std::string(RSTRING_PTR(value), RSTRING_LEN(value))); + req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), + std::string(RSTRING_PTR(value), RSTRING_LEN(value))); } } else if (!mrb_nil_p(values)) { - downstream->add_request_header( - std::string(RSTRING_PTR(key), RSTRING_LEN(key)), - std::string(RSTRING_PTR(values), RSTRING_LEN(values))); + req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), + std::string(RSTRING_PTR(values), RSTRING_LEN(values))); } data->request_headers_dirty = true; @@ -259,10 +266,11 @@ namespace { mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &req = downstream->request(); check_phase(mrb, data->phase, PHASE_REQUEST); - downstream->clear_request_headers(); + req.fs.clear_headers(); return mrb_nil_value(); } diff --git a/src/shrpx_mruby_module_response.cc b/src/shrpx_mruby_module_response.cc index 63cc422e..5b88f879 100644 --- a/src/shrpx_mruby_module_response.cc +++ b/src/shrpx_mruby_module_response.cc @@ -49,7 +49,8 @@ namespace { mrb_value response_get_http_version_major(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return mrb_fixnum_value(downstream->get_response_major()); + const auto &resp = downstream->response(); + return mrb_fixnum_value(resp.http_major); } } // namespace @@ -57,7 +58,8 @@ namespace { mrb_value response_get_http_version_minor(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return mrb_fixnum_value(downstream->get_response_minor()); + const auto &resp = downstream->response(); + return mrb_fixnum_value(resp.http_minor); } } // namespace @@ -65,8 +67,8 @@ namespace { mrb_value response_get_status(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - - return mrb_fixnum_value(downstream->get_response_http_status()); + const auto &resp = downstream->response(); + return mrb_fixnum_value(resp.http_status); } } // namespace @@ -74,6 +76,7 @@ namespace { mrb_value response_set_status(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &resp = downstream->response(); mrb_int status; mrb_get_args(mrb, "i", &status); @@ -83,7 +86,7 @@ mrb_value response_set_status(mrb_state *mrb, mrb_value self) { "invalid status; it should be [200, 999], inclusive"); } - downstream->set_response_http_status(status); + resp.http_status = status; return self; } @@ -93,7 +96,9 @@ namespace { mrb_value response_get_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return create_headers_hash(mrb, downstream->get_response_headers()); + const auto &resp = downstream->response(); + + return create_headers_hash(mrb, resp.fs.headers()); } } // namespace @@ -101,6 +106,7 @@ namespace { mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &resp = downstream->response(); mrb_value key, values; mrb_get_args(mrb, "oo", &key, &values); @@ -113,7 +119,7 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) { if (repl) { size_t p = 0; - auto &headers = downstream->get_response_headers(); + auto &headers = resp.fs.headers(); for (size_t i = 0; i < headers.size(); ++i) { auto &hd = headers[i]; if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key), @@ -131,14 +137,12 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) { auto n = mrb_ary_len(mrb, values); for (int i = 0; i < n; ++i) { auto value = mrb_ary_entry(values, i); - downstream->add_response_header( - std::string(RSTRING_PTR(key), RSTRING_LEN(key)), - std::string(RSTRING_PTR(value), RSTRING_LEN(value))); + resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), + std::string(RSTRING_PTR(value), RSTRING_LEN(value))); } } else if (!mrb_nil_p(values)) { - downstream->add_response_header( - std::string(RSTRING_PTR(key), RSTRING_LEN(key)), - std::string(RSTRING_PTR(values), RSTRING_LEN(values))); + resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)), + std::string(RSTRING_PTR(values), RSTRING_LEN(values))); } data->response_headers_dirty = true; @@ -163,8 +167,9 @@ namespace { mrb_value response_clear_headers(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &resp = downstream->response(); - downstream->clear_response_headers(); + resp.fs.clear_headers(); return mrb_nil_value(); } @@ -174,6 +179,7 @@ namespace { mrb_value response_return(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; + auto &resp = downstream->response(); int rv; if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { @@ -187,12 +193,12 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) { const uint8_t *body = nullptr; size_t bodylen = 0; - if (downstream->get_response_http_status() == 0) { - downstream->set_response_http_status(200); + if (resp.http_status == 0) { + resp.http_status = 200; } if (data->response_headers_dirty) { - downstream->index_response_headers(); + resp.fs.index_headers(); data->response_headers_dirty = false; } @@ -201,21 +207,20 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) { bodylen = vallen; } - auto cl = downstream->get_response_header(http2::HD_CONTENT_LENGTH); + auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH); if (cl) { cl->value = util::utos(bodylen); } else { - downstream->add_response_header("content-length", util::utos(bodylen), - http2::HD_CONTENT_LENGTH); + resp.fs.add_header("content-length", util::utos(bodylen), + http2::HD_CONTENT_LENGTH); } - downstream->set_response_content_length(bodylen); + resp.fs.content_length = bodylen; - auto date = downstream->get_response_header(http2::HD_DATE); + auto date = resp.fs.header(http2::HD_DATE); if (!date) { auto lgconf = log_config(); lgconf->update_tstamp(std::chrono::system_clock::now()); - downstream->add_response_header("date", lgconf->time_http_str, - http2::HD_DATE); + resp.fs.add_header("date", lgconf->time_http_str, http2::HD_DATE); } auto upstream = downstream->get_upstream(); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 604fb612..0fb7fd59 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -147,6 +147,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, auto downstream = upstream->add_pending_downstream( frame->syn_stream.stream_id, frame->syn_stream.pri); + auto &req = downstream->request(); + downstream->reset_upstream_rtimer(); auto nv = frame->syn_stream.nv; @@ -178,20 +180,20 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, } for (size_t i = 0; nv[i]; i += 2) { - downstream->add_request_header(nv[i], nv[i + 1]); + req.fs.add_header(nv[i], nv[i + 1]); } - if (downstream->index_request_headers() != 0) { + if (req.fs.index_headers() != 0) { if (upstream->error_reply(downstream, 400) != 0) { ULOG(FATAL, upstream) << "error_reply failed"; } return; } - auto path = downstream->get_request_header(http2::HD__PATH); - auto scheme = downstream->get_request_header(http2::HD__SCHEME); - auto host = downstream->get_request_header(http2::HD__HOST); - auto method = downstream->get_request_header(http2::HD__METHOD); + auto path = req.fs.header(http2::HD__PATH); + auto scheme = req.fs.header(http2::HD__SCHEME); + auto host = req.fs.header(http2::HD__HOST); + auto method = req.fs.header(http2::HD__METHOD); if (!method) { upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); @@ -222,24 +224,24 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, return; } - downstream->set_request_method(method_token); + req.method = method_token; if (is_connect) { - downstream->set_request_http2_authority(path->value); + req.authority = path->value; } else { - downstream->set_request_http2_scheme(scheme->value); - downstream->set_request_http2_authority(host->value); + req.scheme = scheme->value; + req.authority = host->value; if (get_config()->http2_proxy || get_config()->client_proxy) { - downstream->set_request_path(path->value); + req.path = path->value; } else if (method_token == HTTP_OPTIONS && path->value == "*") { // Server-wide OPTIONS request. Path is empty. } else { - downstream->set_request_path(http2::rewrite_clean_path( - std::begin(path->value), std::end(path->value))); + req.path = http2::rewrite_clean_path(std::begin(path->value), + std::end(path->value)); } } if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { - downstream->set_request_http2_expect_body(true); + req.http2_expect_body = true; } downstream->inspect_http2_request(); @@ -285,8 +287,7 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, } // namespace void SpdyUpstream::start_downstream(Downstream *downstream) { - if (downstream_queue_.can_activate( - downstream->get_request_http2_authority())) { + if (downstream_queue_.can_activate(downstream->request().authority)) { initiate_downstream(downstream); return; } @@ -819,10 +820,11 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body, data_prd_ptr = &data_prd; } - auto status_string = - http2::get_status_string(downstream->get_response_http_status()); + const auto &resp = downstream->response(); - auto &headers = downstream->get_response_headers(); + auto status_string = http2::get_status_string(resp.http_status); + + const auto &headers = resp.fs.headers(); auto nva = std::vector(); // 3 for :status, :version and server @@ -848,7 +850,7 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body, nva.push_back(kv.value.c_str()); } - if (!downstream->get_response_header(http2::HD_SERVER)) { + if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back("server"); nva.push_back(get_config()->server_name); } @@ -875,8 +877,10 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body, int SpdyUpstream::error_reply(Downstream *downstream, unsigned int status_code) { int rv; + auto &resp = downstream->response(); + auto html = http::create_error_html(status_code); - downstream->set_response_http_status(status_code); + resp.http_status = status_code; auto body = downstream->get_response_buf(); body->append(html.c_str(), html.size()); downstream->set_response_state(Downstream::MSG_COMPLETE); @@ -937,15 +941,19 @@ void SpdyUpstream::remove_downstream(Downstream *downstream) { // WARNING: Never call directly or indirectly spdylay_session_send or // spdylay_session_recv. These calls may delete downstream. int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { + auto &resp = downstream->response(); + if (downstream->get_non_final_response()) { // SPDY does not support non-final response. We could send it // with HEADERS and final response in SYN_REPLY, but it is not // official way. - downstream->clear_response_headers(); + resp.fs.clear_headers(); return 0; } + const auto &req = downstream->request(); + #ifdef HAVE_MRUBY auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); @@ -969,23 +977,22 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { if (!get_config()->http2_proxy && !get_config()->client_proxy && !get_config()->no_location_rewrite) { - downstream->rewrite_location_response_header( - downstream->get_request_http2_scheme()); + downstream->rewrite_location_response_header(req.scheme); } - size_t nheader = downstream->get_response_headers().size(); + // 8 means server, :status, :version and possible via header field. auto nv = make_unique( - nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1); + resp.fs.headers().size() * 2 + 8 + + get_config()->add_response_headers.size() * 2 + 1); size_t hdidx = 0; std::string via_value; - std::string status_string = - http2::get_status_string(downstream->get_response_http_status()); + auto status_string = http2::get_status_string(resp.http_status); nv[hdidx++] = ":status"; nv[hdidx++] = status_string.c_str(); nv[hdidx++] = ":version"; nv[hdidx++] = "HTTP/1.1"; - for (auto &hd : downstream->get_response_headers()) { + for (auto &hd : resp.fs.headers()) { if (hd.name.empty() || hd.name.c_str()[0] == ':') { continue; } @@ -1007,14 +1014,14 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { nv[hdidx++] = "server"; nv[hdidx++] = get_config()->server_name; } else { - auto server = downstream->get_response_header(http2::HD_SERVER); + auto server = resp.fs.header(http2::HD_SERVER); if (server) { nv[hdidx++] = "server"; nv[hdidx++] = server->value.c_str(); } } - auto via = downstream->get_response_header(http2::HD_VIA); + auto via = resp.fs.header(http2::HD_VIA); if (get_config()->no_via) { if (via) { nv[hdidx++] = "via"; @@ -1025,8 +1032,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { via_value = via->value; via_value += ", "; } - via_value += http::create_via_header_value( - downstream->get_response_major(), downstream->get_response_minor()); + via_value += + http::create_via_header_value(resp.http_major, resp.http_minor); nv[hdidx++] = "via"; nv[hdidx++] = via_value.c_str(); } @@ -1084,9 +1091,11 @@ int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) { DLOG(INFO, downstream) << "HTTP response completed"; } + auto &resp = downstream->response(); + if (!downstream->validate_response_bodylen()) { rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); - downstream->set_response_connection_close(true); + resp.connection_close = true; return 0; }