From 919f08eb3836215cbfed3ccdfafb92a8dda9aeea Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 13 Jan 2016 22:45:52 +0900 Subject: [PATCH] nghttpx: Extract request related fields to Request struct Header field related functions are now gathered into FieldStore class. This commit only handles request. Subsequent commit will do the same thing for response. --- src/shrpx_client_handler.cc | 60 ++-- src/shrpx_downstream.cc | 336 +++++++++-------------- src/shrpx_downstream.h | 209 +++++++------- src/shrpx_downstream_queue.cc | 2 +- src/shrpx_downstream_test.cc | 73 ++--- src/shrpx_http2_downstream_connection.cc | 63 ++--- src/shrpx_http2_session.cc | 36 +-- src/shrpx_http2_upstream.cc | 146 +++++----- src/shrpx_http_downstream_connection.cc | 61 ++-- src/shrpx_https_upstream.cc | 161 ++++++----- src/shrpx_log.cc | 16 +- src/shrpx_mruby.cc | 2 +- src/shrpx_mruby_module_request.cc | 52 ++-- src/shrpx_spdy_upstream.cc | 38 +-- 14 files changed, 599 insertions(+), 656 deletions(-) diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index b6efe4d7..1bea4e1b 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,19 @@ std::string construct_absolute_request_uri(Downstream *downstream) { void ClientHandler::write_accesslog(Downstream *downstream) { nghttp2::ssl::TLSSessionInfo tls_info; + const auto &req = downstream->request(); 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,7 +832,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(), + req.http_major, req.http_minor, downstream->get_response_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..64508033 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -117,22 +117,17 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, 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_content_length_(-1), upstream_(upstream), blocked_link_(nullptr), response_headers_sum_(0), 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), - request_pending_(false) { + response_rst_stream_error_code_(NGHTTP2_NO_ERROR), + request_state_(INITIAL), response_state_(INITIAL), + response_http_status_(0), response_major_(1), response_minor_(1), + dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false), + chunked_response_(false), response_connection_close_(false), + response_header_key_prev_(false), response_trailer_key_prev_(false), + expect_final_response_(false), request_pending_(false) { ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., get_config()->stream_read_timeout); @@ -148,10 +143,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, 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); } @@ -252,16 +245,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_; 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; @@ -282,7 +269,7 @@ void Downstream::assemble_request_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 +294,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; @@ -424,122 +411,112 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers, } } // namespace -int Downstream::index_request_headers() { - return index_headers(request_hdidx_, request_headers_, - request_content_length_); +int FieldStore::index_headers() { + http2::init_hdidx(hdidx_); + content_length = -1; + + for (size_t i = 0; i < headers_.size(); ++i) { + auto &kv = headers_[i]; + util::inp_strlower(kv.name); + + auto token = http2::lookup_token( + reinterpret_cast(kv.name.c_str()), kv.name.size()); + if (token < 0) { + continue; + } + + kv.token = token; + http2::index_header(hdidx_, token, i); + + if (token == http2::HD_CONTENT_LENGTH) { + auto len = util::parse_uint(kv.value); + if (len == -1) { + return -1; + } + if (content_length != -1) { + return -1; + } + content_length = len; + } + } + return 0; } -const Headers::value_type *Downstream::get_request_header(int16_t token) const { - return http2::get_header(request_hdidx_, token, request_headers_); +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(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::set_last_header_value(const char *data, size_t len) { + shrpx::set_last_header_value(header_key_prev_, buffer_size_, headers_, data, + len); } -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(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(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::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); } -bool Downstream::get_request_header_key_prev() const { - return request_header_key_prev_; +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); } -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::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_value(const char *data, - size_t len) { - append_last_header_value(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); +void FieldStore::clear_headers() { + headers_.clear(); + http2::init_hdidx(hdidx_); } -void Downstream::clear_request_headers() { - Headers().swap(request_headers_); - http2::init_hdidx(request_hdidx_); +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); } -size_t Downstream::get_request_headers_sum() const { - return request_headers_sum_; +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)); } -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); +void FieldStore::set_last_trailer_value(const char *data, size_t len) { + shrpx::set_last_header_value(trailer_key_prev_, buffer_size_, trailers_, data, + len); } -const Headers &Downstream::get_request_trailers() const { - return request_trailers_; +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(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 +529,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 +550,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; @@ -696,14 +629,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 +650,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_, @@ -912,23 +844,15 @@ 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; @@ -959,7 +883,7 @@ 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) { + if (req_.method == HTTP_CONNECT) { upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; return; @@ -967,40 +891,40 @@ void Downstream::check_upgrade_fulfilled() { if (response_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; } } @@ -1028,11 +952,8 @@ bool Downstream::get_non_final_response() const { 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 +962,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,7 +1014,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_); + return http2::expect_response_body(req_.method, response_http_status_); } namespace { @@ -1106,13 +1027,6 @@ bool pseudo_header_allowed(const Headers &headers) { } } // 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; @@ -1269,10 +1183,6 @@ 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_ && diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index ad659637..b4d84d79 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -49,6 +49,116 @@ 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; + // 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); + void set_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); + + void set_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; +}; + class Downstream { public: Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, @@ -79,9 +189,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,9 +201,11 @@ 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 @@ -104,78 +213,14 @@ public: 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); - 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 +229,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 { @@ -362,19 +404,16 @@ public: Downstream *dlnext, *dlprev; private: - Headers request_headers_; + Request req_; + 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_; 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. @@ -398,8 +437,6 @@ 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_; @@ -409,7 +446,6 @@ private: // 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. @@ -426,10 +462,7 @@ 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_; @@ -439,22 +472,12 @@ private: // 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_; 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..e1673880 100644 --- a/src/shrpx_downstream_test.cc +++ b/src/shrpx_downstream_test.cc @@ -33,16 +33,16 @@ 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(); + Request req; + req.fs.add_header("1", "0"); + req.fs.add_header("2", "1"); + req.fs.add_header("Charlie", "2"); + req.fs.add_header("Alpha", "3"); + req.fs.add_header("Delta", "4"); + req.fs.add_header("BravO", "5"); + req.fs.add_header(":method", "6"); + req.fs.add_header(":authority", "7"); + req.fs.index_headers(); auto ans = Headers{{"1", "0"}, {"2", "1"}, @@ -52,7 +52,7 @@ void test_downstream_index_request_headers(void) { {"bravo", "5"}, {":method", "6"}, {":authority", "7"}}; - CU_ASSERT(ans == d.get_request_headers()); + CU_ASSERT(ans == req.fs.headers()); } void test_downstream_index_response_headers(void) { @@ -69,20 +69,19 @@ void test_downstream_index_response_headers(void) { } 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(); + Request req; + req.fs.add_header("alpha", "0"); + req.fs.add_header(":authority", "1"); + req.fs.add_header("content-length", "2"); + req.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") == *req.fs.header(http2::HD__AUTHORITY)); + CU_ASSERT(nullptr == req.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")); + CU_ASSERT(Header("alpha", "0") == *req.fs.header("alpha")); + CU_ASSERT(nullptr == req.fs.header("bravo")); } void test_downstream_get_response_header(void) { @@ -100,14 +99,15 @@ void test_downstream_get_response_header(void) { 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,12 +139,13 @@ 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;;"); + 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;;"); d.assemble_request_cookie(); CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie()); } @@ -152,10 +153,11 @@ void test_downstream_assemble_request_cookie(void) { void test_downstream_rewrite_location_response_header(void) { { Downstream d(nullptr, nullptr, 0, 0); + auto &req = d.request(); d.set_request_downstream_host("localhost:3000"); - d.add_request_header("host", "localhost"); + req.fs.add_header("host", "localhost"); d.add_response_header("location", "http://localhost:3000/"); - d.index_request_headers(); + req.fs.index_headers(); d.index_response_headers(); d.rewrite_location_response_header("https"); auto location = d.get_response_header(http2::HD_LOCATION); @@ -163,8 +165,9 @@ void test_downstream_rewrite_location_response_header(void) { } { Downstream d(nullptr, nullptr, 0, 0); + auto &req = d.request(); d.set_request_downstream_host("localhost"); - d.set_request_http2_authority("localhost"); + req.authority = "localhost"; d.add_response_header("location", "http://localhost:3000/"); d.index_response_headers(); d.rewrite_location_response_header("https"); 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..1ec18175 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -783,10 +783,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,6 +856,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, int rv; auto upstream = downstream->get_upstream(); + const auto &req = downstream->request(); auto &nva = downstream->get_response_headers(); @@ -927,9 +929,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, 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); } else { @@ -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..fd56841f 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(); @@ -1433,6 +1428,8 @@ void Http2Upstream::remove_downstream(Downstream *downstream) { int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { int rv; + const auto &req = downstream->request(); + if (LOG_ENABLED(INFO)) { if (downstream->get_non_final_response()) { DLOG(INFO, downstream) << "HTTP non-final response header"; @@ -1443,8 +1440,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 @@ -1582,8 +1578,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { (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)) { + (req.method == HTTP_GET || req.method == HTTP_POST)) { if (prepare_push_promise(downstream) != 0) { // Continue to send response even if push was failed. @@ -1766,8 +1761,9 @@ 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(); + + rv = http2::get_pure_path_component(&base, &baselen, req.path); if (rv != 0) { return 0; } @@ -1782,6 +1778,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 +1788,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 +1812,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 +1824,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 +1887,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 +1904,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 +1948,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 +1968,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..cac728b9 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -214,19 +214,19 @@ 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); @@ -237,35 +237,31 @@ int HttpDownstreamConnection::push_request_headers() { 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()) { buf->append("Cookie: "); @@ -273,26 +269,26 @@ int HttpDownstreamConnection::push_request_headers() { 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,6 +481,7 @@ namespace { int htp_hdrs_completecb(http_parser *htp) { auto downstream = static_cast(htp->data); auto upstream = downstream->get_upstream(); + const auto &req = downstream->request(); int rv; downstream->set_response_http_status(htp->status_code); @@ -551,9 +549,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; } diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 8f584749..772df751 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,16 +179,16 @@ 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); + if (req.fs.header_key_prev()) { + req.fs.set_last_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); + if (req.fs.trailer_key_prev()) { + req.fs.set_last_trailer_value(data, len); } else { - downstream->append_last_request_trailer_value(data, len); + req.fs.append_last_trailer_value(data, len); } } return 0; @@ -195,13 +196,14 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { } // 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 +214,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 +239,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 +254,34 @@ int htp_hdrs_completecb(http_parser *htp) { ULOG(INFO, upstream) << "HTTP request headers completed"; } auto downstream = upstream->get_downstream(); + auto &req = downstream->request(); - 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 +291,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 +305,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); } } @@ -766,11 +761,10 @@ 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 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); @@ -896,6 +890,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } } + const auto &req = downstream->request(); + #ifdef HAVE_MRUBY if (!downstream->get_non_final_response()) { auto worker = handler_->get_worker(); @@ -912,14 +908,14 @@ 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("\r\n"); @@ -955,10 +951,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { // 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 && !downstream->get_response_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"); } @@ -1068,6 +1062,8 @@ int HttpsUpstream::on_downstream_body(Downstream *downstream, } int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { + const auto &req = downstream->request(); + if (downstream->get_chunked_response()) { auto output = downstream->get_response_buf(); auto &trailers = downstream->get_response_trailers(); @@ -1087,8 +1083,7 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { downstream->set_response_connection_close(true); } - if (downstream->get_request_connection_close() || - downstream->get_response_connection_close()) { + if (req.connection_close || downstream->get_response_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..befd19a2 100644 --- a/src/shrpx_mruby.cc +++ b/src/shrpx_mruby.cc @@ -96,7 +96,7 @@ 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) { 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_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 604fb612..5f112991 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; } @@ -946,6 +947,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { return 0; } + const auto &req = downstream->request(); + #ifdef HAVE_MRUBY auto worker = handler_->get_worker(); auto mruby_ctx = worker->get_mruby_context(); @@ -969,8 +972,7 @@ 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.