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.