nghttpx: Extract request related fields to Request struct

Header field related functions are now gathered into FieldStore class.
This commit only handles request.  Subsequent commit will do the same
thing for response.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-01-13 22:45:52 +09:00
parent 6cc5d71ffe
commit 919f08eb38
14 changed files with 599 additions and 656 deletions

View File

@ -629,30 +629,30 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
auto &groups = get_config()->downstream_addr_groups; auto &groups = get_config()->downstream_addr_groups;
auto catch_all = get_config()->downstream_addr_group_catch_all; 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. // Fast path. If we have one group, it must be catch-all group.
// HTTP/2 and client proxy modes fall in this case. // HTTP/2 and client proxy modes fall in this case.
if (groups.size() == 1) { if (groups.size() == 1) {
group = 0; 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 // We don't know how to treat CONNECT request in host-path
// mapping. It most likely appears in proxy scenario. Since we // mapping. It most likely appears in proxy scenario. Since we
// have dealt with proxy case already, just use catch-all group. // have dealt with proxy case already, just use catch-all group.
group = catch_all; group = catch_all;
} else { } else {
auto &router = get_config()->router; auto &router = get_config()->router;
if (!downstream->get_request_http2_authority().empty()) { if (!req.authority.empty()) {
group = match_downstream_addr_group( group = match_downstream_addr_group(router, req.authority, req.path,
router, downstream->get_request_http2_authority(),
downstream->get_request_path(), groups, catch_all);
} else {
auto h = downstream->get_request_header(http2::HD_HOST);
if (h) {
group = match_downstream_addr_group(router, h->value,
downstream->get_request_path(),
groups, catch_all); groups, catch_all);
} else { } else {
group = match_downstream_addr_group( auto h = req.fs.header(http2::HD_HOST);
router, "", downstream->get_request_path(), groups, catch_all); if (h) {
group = match_downstream_addr_group(router, h->value, req.path, groups,
catch_all);
} else {
group = match_downstream_addr_group(router, "", req.path, groups,
catch_all);
} }
} }
} }
@ -783,28 +783,27 @@ void ClientHandler::start_immediate_shutdown() {
} }
namespace { 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 // request URI for proxy request (HTTP/2 proxy or client proxy). This
// is mostly same routine found in // is mostly same routine found in
// HttpDownstreamConnection::push_request_headers(), but vastly // HttpDownstreamConnection::push_request_headers(), but vastly
// simplified since we only care about absolute URI. // simplified since we only care about absolute URI.
std::string construct_absolute_request_uri(Downstream *downstream) { std::string construct_absolute_request_uri(const Request &req) {
auto &authority = downstream->get_request_http2_authority(); if (req.authority.empty()) {
if (authority.empty()) { return req.path;
return downstream->get_request_path();
} }
std::string uri; std::string uri;
auto &scheme = downstream->get_request_http2_scheme(); if (req.scheme.empty()) {
if (scheme.empty()) {
// We may have to log the request which lacks scheme (e.g., // We may have to log the request which lacks scheme (e.g.,
// http/1.1 with origin form). // http/1.1 with origin form).
uri += "http://"; uri += "http://";
} else { } else {
uri += scheme; uri += req.scheme;
uri += "://"; uri += "://";
} }
uri += authority; uri += req.authority;
uri += downstream->get_request_path(); uri += req.path;
return uri; return uri;
} }
@ -812,22 +811,19 @@ std::string construct_absolute_request_uri(Downstream *downstream) {
void ClientHandler::write_accesslog(Downstream *downstream) { void ClientHandler::write_accesslog(Downstream *downstream) {
nghttp2::ssl::TLSSessionInfo tls_info; nghttp2::ssl::TLSSessionInfo tls_info;
const auto &req = downstream->request();
upstream_accesslog( upstream_accesslog(
get_config()->accesslog_format, get_config()->accesslog_format,
LogSpec{ LogSpec{
downstream, ipaddr_.c_str(), downstream, ipaddr_.c_str(), http2::to_method_string(req.method),
http2::to_method_string(downstream->get_request_method()),
downstream->get_request_method() == HTTP_CONNECT req.method == HTTP_CONNECT
? downstream->get_request_http2_authority().c_str() ? req.authority.c_str()
: (get_config()->http2_proxy || get_config()->client_proxy) : (get_config()->http2_proxy || get_config()->client_proxy)
? construct_absolute_request_uri(downstream).c_str() ? construct_absolute_request_uri(req).c_str()
: downstream->get_request_path().empty() : req.path.empty() ? req.method == HTTP_OPTIONS ? "*" : "-"
? downstream->get_request_method() == HTTP_OPTIONS : req.path.c_str(),
? "*"
: "-"
: downstream->get_request_path().c_str(),
alpn_.c_str(), alpn_.c_str(),
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl), 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 downstream->get_request_start_time(), // request_start_time
std::chrono::high_resolution_clock::now(), // request_end_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_http_status(),
downstream->get_response_sent_bodylen(), port_.c_str(), downstream->get_response_sent_bodylen(), port_.c_str(),
get_config()->port, get_config()->pid, get_config()->port, get_config()->pid,

View File

@ -117,22 +117,17 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
request_start_time_(std::chrono::high_resolution_clock::now()), request_start_time_(std::chrono::high_resolution_clock::now()),
request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0), request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0),
response_bodylen_(0), response_sent_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0),
request_content_length_(-1), response_content_length_(-1), response_content_length_(-1), upstream_(upstream), blocked_link_(nullptr),
upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0),
response_headers_sum_(0), request_datalen_(0), response_datalen_(0), response_headers_sum_(0), request_datalen_(0), response_datalen_(0),
num_retry_(0), stream_id_(stream_id), priority_(priority), num_retry_(0), stream_id_(stream_id), priority_(priority),
downstream_stream_id_(-1), downstream_stream_id_(-1),
response_rst_stream_error_code_(NGHTTP2_NO_ERROR), request_method_(-1), response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
request_state_(INITIAL), request_major_(1), request_minor_(1), request_state_(INITIAL), response_state_(INITIAL),
response_state_(INITIAL), response_http_status_(0), response_major_(1), response_http_status_(0), response_major_(1), response_minor_(1),
response_minor_(1), dispatch_state_(DISPATCH_NONE), dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false),
upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false), chunked_response_(false), response_connection_close_(false),
chunked_request_(false), request_connection_close_(false), response_header_key_prev_(false), response_trailer_key_prev_(false),
request_header_key_prev_(false), request_trailer_key_prev_(false), expect_final_response_(false), request_pending_(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) {
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
get_config()->stream_read_timeout); get_config()->stream_read_timeout);
@ -148,10 +143,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
downstream_rtimer_.data = this; downstream_rtimer_.data = this;
downstream_wtimer_.data = this; downstream_wtimer_.data = this;
http2::init_hdidx(request_hdidx_);
http2::init_hdidx(response_hdidx_); http2::init_hdidx(response_hdidx_);
request_headers_.reserve(16);
response_headers_.reserve(32); response_headers_.reserve(32);
} }
@ -252,16 +245,10 @@ const Headers::value_type *get_header_linear(const Headers &headers,
} }
} // namespace } // namespace
const Headers &Downstream::get_request_headers() const {
return request_headers_;
}
Headers &Downstream::get_request_headers() { return request_headers_; }
void Downstream::assemble_request_cookie() { void Downstream::assemble_request_cookie() {
std::string &cookie = assembled_request_cookie_; std::string &cookie = assembled_request_cookie_;
cookie = ""; cookie = "";
for (auto &kv : request_headers_) { for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' || if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) { !util::streq_l("cooki", kv.name.c_str(), 5)) {
continue; continue;
@ -282,7 +269,7 @@ void Downstream::assemble_request_cookie() {
size_t Downstream::count_crumble_request_cookie() { size_t Downstream::count_crumble_request_cookie() {
size_t n = 0; size_t n = 0;
for (auto &kv : request_headers_) { for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' || if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) { !util::streq_l("cooki", kv.name.c_str(), 5)) {
continue; continue;
@ -307,7 +294,7 @@ size_t Downstream::count_crumble_request_cookie() {
} }
void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) { void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
for (auto &kv : request_headers_) { for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' || if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) { !util::streq_l("cooki", kv.name.c_str(), 5)) {
continue; continue;
@ -424,122 +411,112 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
} }
} // namespace } // namespace
int Downstream::index_request_headers() { int FieldStore::index_headers() {
return index_headers(request_hdidx_, request_headers_, http2::init_hdidx(hdidx_);
request_content_length_); 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<const uint8_t *>(kv.name.c_str()), kv.name.size());
if (token < 0) {
continue;
} }
const Headers::value_type *Downstream::get_request_header(int16_t token) const { kv.token = token;
return http2::get_header(request_hdidx_, token, request_headers_); 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 * const Headers::value_type *FieldStore::header(int16_t token) const {
Downstream::get_request_header(const std::string &name) const { return http2::get_header(hdidx_, token, headers_);
return get_header_linear(request_headers_, name);
} }
void Downstream::add_request_header(std::string name, std::string value) { const Headers::value_type *FieldStore::header(const std::string &name) const {
add_header(request_header_key_prev_, request_headers_sum_, request_headers_, return get_header_linear(headers_, name);
std::move(name), std::move(value));
} }
void Downstream::set_last_request_header_value(const char *data, size_t len) { void FieldStore::add_header(std::string name, std::string value) {
set_last_header_value(request_header_key_prev_, request_headers_sum_, shrpx::add_header(header_key_prev_, buffer_size_, headers_, std::move(name),
request_headers_, data, len); std::move(value));
} }
void Downstream::add_request_header(std::string name, std::string value, 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 FieldStore::add_header(std::string name, std::string value,
int16_t token) { int16_t token) {
http2::index_header(request_hdidx_, token, request_headers_.size()); http2::index_header(hdidx_, token, headers_.size());
request_headers_sum_ += name.size() + value.size(); buffer_size_ += name.size() + value.size();
request_headers_.emplace_back(std::move(name), std::move(value), false, headers_.emplace_back(std::move(name), std::move(value), false, token);
token);
} }
void Downstream::add_request_header(const uint8_t *name, size_t namelen, void FieldStore::add_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) { bool no_index, int16_t token) {
http2::index_header(request_hdidx_, token, request_headers_.size()); http2::index_header(hdidx_, token, headers_.size());
add_header(request_headers_sum_, request_headers_, name, namelen, value, shrpx::add_header(buffer_size_, headers_, name, namelen, value, valuelen,
valuelen, no_index, token); no_index, token);
} }
bool Downstream::get_request_header_key_prev() const { void FieldStore::append_last_header_key(const char *data, size_t len) {
return request_header_key_prev_; 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) { void FieldStore::append_last_header_value(const char *data, size_t len) {
append_last_header_key(request_header_key_prev_, request_headers_sum_, shrpx::append_last_header_value(header_key_prev_, buffer_size_, headers_,
request_headers_, data, len); data, len);
} }
void Downstream::append_last_request_header_value(const char *data, void FieldStore::clear_headers() {
size_t len) { headers_.clear();
append_last_header_value(request_header_key_prev_, request_headers_sum_, http2::init_hdidx(hdidx_);
request_headers_, data, len);
} }
void Downstream::clear_request_headers() { void FieldStore::add_trailer(const uint8_t *name, size_t namelen,
Headers().swap(request_headers_);
http2::init_hdidx(request_hdidx_);
}
size_t Downstream::get_request_headers_sum() const {
return request_headers_sum_;
}
void Downstream::add_request_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) { bool no_index, int16_t token) {
// we never index trailer part. Header size limit should be applied // we never index trailer fields. Header size limit should be
// to all request header fields combined. // applied to all header and trailer fields combined.
add_header(request_headers_sum_, request_trailers_, name, namelen, value, shrpx::add_header(buffer_size_, trailers_, name, namelen, value, valuelen,
valuelen, no_index, -1); no_index, -1);
} }
const Headers &Downstream::get_request_trailers() const { void FieldStore::add_trailer(std::string name, std::string value) {
return request_trailers_; shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, std::move(name),
std::move(value));
} }
void Downstream::add_request_trailer(std::string name, std::string value) { void FieldStore::set_last_trailer_value(const char *data, size_t len) {
add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_, shrpx::set_last_header_value(trailer_key_prev_, buffer_size_, trailers_, data,
std::move(name), std::move(value)); len);
} }
void Downstream::set_last_request_trailer_value(const char *data, size_t len) { void FieldStore::append_last_trailer_key(const char *data, size_t len) {
set_last_header_value(request_trailer_key_prev_, request_headers_sum_, shrpx::append_last_header_key(trailer_key_prev_, buffer_size_, trailers_,
request_trailers_, data, len); data, len);
} }
bool Downstream::get_request_trailer_key_prev() const { void FieldStore::append_last_trailer_value(const char *data, size_t len) {
return request_trailer_key_prev_; shrpx::append_last_header_value(trailer_key_prev_, buffer_size_, trailers_,
} data, len);
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 Downstream::set_request_start_time( void Downstream::set_request_start_time(
@ -552,34 +529,6 @@ Downstream::get_request_start_time() const {
return request_start_time_; 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) { void Downstream::reset_upstream(Upstream *upstream) {
upstream_ = upstream; upstream_ = upstream;
if (dconn_) { 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; } 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() { bool Downstream::request_buf_full() {
if (dconn_) { if (dconn_) {
return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; return request_buf_.rleft() >= get_config()->downstream_request_buffer_size;
@ -696,14 +629,13 @@ void Downstream::rewrite_location_response_header(
return; return;
} }
std::string new_uri; std::string new_uri;
if (get_config()->no_host_rewrite || request_method_ == HTTP_CONNECT) { if (get_config()->no_host_rewrite || req_.method == HTTP_CONNECT) {
if (!request_http2_authority_.empty()) { if (!req_.authority.empty()) {
new_uri = http2::rewrite_location_uri( new_uri = http2::rewrite_location_uri((*hd).value, u, req_.authority,
(*hd).value, u, request_http2_authority_, request_http2_authority_, req_.authority, upstream_scheme);
upstream_scheme);
} }
if (new_uri.empty()) { if (new_uri.empty()) {
auto host = get_request_header(http2::HD_HOST); auto host = req_.fs.header(http2::HD_HOST);
if (host) { if (host) {
new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value, new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value,
(*host).value, upstream_scheme); (*host).value, upstream_scheme);
@ -718,12 +650,12 @@ void Downstream::rewrite_location_response_header(
if (request_downstream_host_.empty()) { if (request_downstream_host_.empty()) {
return; return;
} }
if (!request_http2_authority_.empty()) { if (!req_.authority.empty()) {
new_uri = http2::rewrite_location_uri( new_uri =
(*hd).value, u, request_downstream_host_, request_http2_authority_, http2::rewrite_location_uri((*hd).value, u, request_downstream_host_,
upstream_scheme); req_.authority, upstream_scheme);
} else { } else {
auto host = get_request_header(http2::HD_HOST); auto host = req_.fs.header(http2::HD_HOST);
if (host) { if (host) {
new_uri = http2::rewrite_location_uri((*hd).value, u, new_uri = http2::rewrite_location_uri((*hd).value, u,
request_downstream_host_, request_downstream_host_,
@ -912,23 +844,15 @@ void Downstream::set_response_content_length(int64_t len) {
response_content_length_ = 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 { bool Downstream::validate_request_bodylen() const {
if (request_content_length_ == -1) { if (req_.fs.content_length == -1) {
return true; return true;
} }
if (request_content_length_ != request_bodylen_) { if (req_.fs.content_length != request_bodylen_) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "request invalid bodylen: content-length=" DLOG(INFO, this) << "request invalid bodylen: content-length="
<< request_content_length_ << req_.fs.content_length
<< ", received=" << request_bodylen_; << ", received=" << request_bodylen_;
} }
return false; return false;
@ -959,7 +883,7 @@ void Downstream::set_priority(int32_t pri) { priority_ = pri; }
int32_t Downstream::get_priority() const { return priority_; } int32_t Downstream::get_priority() const { return priority_; }
void Downstream::check_upgrade_fulfilled() { void Downstream::check_upgrade_fulfilled() {
if (request_method_ == HTTP_CONNECT) { if (req_.method == HTTP_CONNECT) {
upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300;
return; return;
@ -967,40 +891,40 @@ void Downstream::check_upgrade_fulfilled() {
if (response_http_status_ == 101) { if (response_http_status_ == 101) {
// TODO Do more strict checking for upgrade headers // TODO Do more strict checking for upgrade headers
upgraded_ = upgrade_request_; upgraded_ = req_.upgrade_request;
return; return;
} }
} }
void Downstream::inspect_http2_request() { void Downstream::inspect_http2_request() {
if (request_method_ == HTTP_CONNECT) { if (req_.method == HTTP_CONNECT) {
upgrade_request_ = true; req_.upgrade_request = true;
} }
} }
void Downstream::inspect_http1_request() { void Downstream::inspect_http1_request() {
if (request_method_ == HTTP_CONNECT) { if (req_.method == HTTP_CONNECT) {
upgrade_request_ = true; req_.upgrade_request = true;
} }
if (!upgrade_request_) { if (!req_.upgrade_request) {
auto idx = request_hdidx_[http2::HD_UPGRADE]; auto upgrade = req_.fs.header(http2::HD_UPGRADE);
if (idx != -1) { if (upgrade) {
auto &val = request_headers_[idx].value; const auto &val = upgrade->value;
// TODO Perform more strict checking for upgrade headers // TODO Perform more strict checking for upgrade headers
if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(), if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(),
val.size())) { val.size())) {
http2_upgrade_seen_ = true; req_.http2_upgrade_seen = true;
} else { } else {
upgrade_request_ = true; req_.upgrade_request = true;
} }
} }
} }
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING]; auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING);
if (idx != -1) { if (transfer_encoding) {
request_content_length_ = -1; req_.fs.content_length = -1;
if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) { if (util::strifind(transfer_encoding->value.c_str(), "chunked")) {
chunked_request_ = true; chunked_request_ = true;
} }
} }
@ -1028,11 +952,8 @@ bool Downstream::get_non_final_response() const {
bool Downstream::get_upgraded() const { return upgraded_; } bool Downstream::get_upgraded() const { return upgraded_; }
bool Downstream::get_upgrade_request() const { return upgrade_request_; }
bool Downstream::get_http2_upgrade_request() const { bool Downstream::get_http2_upgrade_request() const {
return http2_upgrade_seen_ && return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) &&
request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1 &&
response_state_ == INITIAL; response_state_ == INITIAL;
} }
@ -1041,11 +962,11 @@ const std::string EMPTY;
} // namespace } // namespace
const std::string &Downstream::get_http2_settings() const { const std::string &Downstream::get_http2_settings() const {
auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS]; auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS);
if (idx == -1) { if (!http2_settings) {
return EMPTY; return EMPTY;
} }
return request_headers_[idx].value; return http2_settings->value;
} }
void Downstream::set_downstream_stream_id(int32_t stream_id) { 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; } void Downstream::reset_response_datalen() { response_datalen_ = 0; }
bool Downstream::expect_response_body() const { 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 { namespace {
@ -1106,13 +1027,6 @@ bool pseudo_header_allowed(const Headers &headers) {
} }
} // namespace } // 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 { bool Downstream::response_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(response_headers_)) { if (!pseudo_header_allowed(response_headers_)) {
return false; return false;
@ -1269,10 +1183,6 @@ BlockedLink *Downstream::detach_blocked_link() {
return link; return link;
} }
void Downstream::add_request_headers_sum(size_t amount) {
request_headers_sum_ += amount;
}
bool Downstream::can_detach_downstream_connection() const { bool Downstream::can_detach_downstream_connection() const {
return dconn_ && response_state_ == Downstream::MSG_COMPLETE && return dconn_ && response_state_ == Downstream::MSG_COMPLETE &&
request_state_ == Downstream::MSG_COMPLETE && !upgraded_ && request_state_ == Downstream::MSG_COMPLETE && !upgraded_ &&

View File

@ -49,6 +49,116 @@ class Upstream;
class DownstreamConnection; class DownstreamConnection;
struct BlockedLink; 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 { class Downstream {
public: public:
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id,
@ -79,9 +189,6 @@ public:
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded. // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
// This should not depend on inspect_http1_response(). // This should not depend on inspect_http1_response().
void check_upgrade_fulfilled(); 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 // Returns true if the upgrade is succeded as a result of the call
// check_upgrade_fulfilled(). HTTP/2 Upgrade is excluded. // check_upgrade_fulfilled(). HTTP/2 Upgrade is excluded.
bool get_upgraded() const; bool get_upgraded() const;
@ -94,9 +201,11 @@ public:
bool get_http2_upgrade_request() const; bool get_http2_upgrade_request() const;
// Returns the value of HTTP2-Settings request header field. // Returns the value of HTTP2-Settings request header field.
const std::string &get_http2_settings() const; const std::string &get_http2_settings() const;
// downstream request API // downstream request API
const Headers &get_request_headers() const; const Request &request() const { return req_; }
Headers &get_request_headers(); Request &request() { return req_; }
// Count number of crumbled cookies // Count number of crumbled cookies
size_t count_crumble_request_cookie(); size_t count_crumble_request_cookie();
// Crumbles (split cookie by ";") in request_headers_ and adds them // Crumbles (split cookie by ";") in request_headers_ and adds them
@ -104,78 +213,14 @@ public:
void crumble_request_cookie(std::vector<nghttp2_nv> &nva); void crumble_request_cookie(std::vector<nghttp2_nv> &nva);
void assemble_request_cookie(); void assemble_request_cookie();
const std::string &get_assembled_request_cookie() const; 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 void
set_request_start_time(std::chrono::high_resolution_clock::time_point time); set_request_start_time(std::chrono::high_resolution_clock::time_point time);
const std::chrono::high_resolution_clock::time_point & const std::chrono::high_resolution_clock::time_point &
get_request_start_time() const; 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(); int push_request_headers();
bool get_chunked_request() const; bool get_chunked_request() const;
void set_chunked_request(bool f); 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 push_upload_data_chunk(const uint8_t *data, size_t datalen);
int end_upload_data(); int end_upload_data();
size_t get_request_datalen() const; size_t get_request_datalen() const;
@ -184,9 +229,6 @@ public:
// Validates that received request body length and content-length // Validates that received request body length and content-length
// matches. // matches.
bool validate_request_bodylen() const; 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); void set_request_downstream_host(std::string host);
bool expect_response_body() const; bool expect_response_body() const;
enum { enum {
@ -362,19 +404,16 @@ public:
Downstream *dlnext, *dlprev; Downstream *dlnext, *dlprev;
private: private:
Headers request_headers_; Request req_;
Headers response_headers_; Headers response_headers_;
// trailer part. For HTTP/1.1, trailer part is only included with // trailer part. For HTTP/1.1, trailer part is only included with
// chunked encoding. For HTTP/2, there is no such limit. // chunked encoding. For HTTP/2, there is no such limit.
Headers request_trailers_;
Headers response_trailers_; Headers response_trailers_;
std::chrono::high_resolution_clock::time_point request_start_time_; 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 // host we requested to downstream. This is used to rewrite
// location header field to decide the location should be rewritten // location header field to decide the location should be rewritten
// or not. // or not.
@ -398,8 +437,6 @@ private:
// the length of response body sent to upstream client // the length of response body sent to upstream client
int64_t response_sent_bodylen_; 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. // content-length of response body, -1 if it is unknown.
int64_t response_content_length_; int64_t response_content_length_;
@ -409,7 +446,6 @@ private:
// only used by HTTP/2 or SPDY upstream // only used by HTTP/2 or SPDY upstream
BlockedLink *blocked_link_; BlockedLink *blocked_link_;
size_t request_headers_sum_;
size_t response_headers_sum_; size_t response_headers_sum_;
// The number of bytes not consumed by the application yet. // The number of bytes not consumed by the application yet.
@ -426,10 +462,7 @@ private:
// RST_STREAM error_code from downstream HTTP2 connection // RST_STREAM error_code from downstream HTTP2 connection
uint32_t response_rst_stream_error_code_; uint32_t response_rst_stream_error_code_;
int request_method_;
int request_state_; int request_state_;
int request_major_;
int request_minor_;
int response_state_; int response_state_;
unsigned int response_http_status_; unsigned int response_http_status_;
@ -439,22 +472,12 @@ private:
// only used by HTTP/2 or SPDY upstream // only used by HTTP/2 or SPDY upstream
int dispatch_state_; int dispatch_state_;
http2::HeaderIndex request_hdidx_;
http2::HeaderIndex response_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) // true if the connection is upgraded (HTTP Upgrade or CONNECT)
bool upgraded_; bool upgraded_;
bool http2_upgrade_seen_;
bool chunked_request_; 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 chunked_response_;
bool response_connection_close_; bool response_connection_close_;

View File

@ -79,7 +79,7 @@ DownstreamQueue::make_host_key(const std::string &host) const {
const std::string & const std::string &
DownstreamQueue::make_host_key(Downstream *downstream) const { 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) { void DownstreamQueue::mark_active(Downstream *downstream) {

View File

@ -33,16 +33,16 @@
namespace shrpx { namespace shrpx {
void test_downstream_index_request_headers(void) { void test_downstream_index_request_headers(void) {
Downstream d(nullptr, nullptr, 0, 0); Request req;
d.add_request_header("1", "0"); req.fs.add_header("1", "0");
d.add_request_header("2", "1"); req.fs.add_header("2", "1");
d.add_request_header("Charlie", "2"); req.fs.add_header("Charlie", "2");
d.add_request_header("Alpha", "3"); req.fs.add_header("Alpha", "3");
d.add_request_header("Delta", "4"); req.fs.add_header("Delta", "4");
d.add_request_header("BravO", "5"); req.fs.add_header("BravO", "5");
d.add_request_header(":method", "6"); req.fs.add_header(":method", "6");
d.add_request_header(":authority", "7"); req.fs.add_header(":authority", "7");
d.index_request_headers(); req.fs.index_headers();
auto ans = Headers{{"1", "0"}, auto ans = Headers{{"1", "0"},
{"2", "1"}, {"2", "1"},
@ -52,7 +52,7 @@ void test_downstream_index_request_headers(void) {
{"bravo", "5"}, {"bravo", "5"},
{":method", "6"}, {":method", "6"},
{":authority", "7"}}; {":authority", "7"}};
CU_ASSERT(ans == d.get_request_headers()); CU_ASSERT(ans == req.fs.headers());
} }
void test_downstream_index_response_headers(void) { 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) { void test_downstream_get_request_header(void) {
Downstream d(nullptr, nullptr, 0, 0); Request req;
d.add_request_header("alpha", "0"); req.fs.add_header("alpha", "0");
d.add_request_header(":authority", "1"); req.fs.add_header(":authority", "1");
d.add_request_header("content-length", "2"); req.fs.add_header("content-length", "2");
d.index_request_headers(); req.fs.index_headers();
// By token // By token
CU_ASSERT(Header(":authority", "1") == CU_ASSERT(Header(":authority", "1") == *req.fs.header(http2::HD__AUTHORITY));
*d.get_request_header(http2::HD__AUTHORITY)); CU_ASSERT(nullptr == req.fs.header(http2::HD__METHOD));
CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD));
// By name // By name
CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha")); CU_ASSERT(Header("alpha", "0") == *req.fs.header("alpha"));
CU_ASSERT(nullptr == d.get_request_header("bravo")); CU_ASSERT(nullptr == req.fs.header("bravo"));
} }
void test_downstream_get_response_header(void) { 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) { void test_downstream_crumble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0, 0); Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header(":method", "get"); auto &req = d.request();
d.add_request_header(":path", "/"); req.fs.add_header(":method", "get");
req.fs.add_header(":path", "/");
auto val = "alpha; bravo; ; ;; charlie;;"; auto val = "alpha; bravo; ; ;; charlie;;";
d.add_request_header( req.fs.add_header(
reinterpret_cast<const uint8_t *>("cookie"), sizeof("cookie") - 1, reinterpret_cast<const uint8_t *>("cookie"), sizeof("cookie") - 1,
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1); reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1);
d.add_request_header("cookie", ";delta"); req.fs.add_header("cookie", ";delta");
d.add_request_header("cookie", "echo"); req.fs.add_header("cookie", "echo");
std::vector<nghttp2_nv> nva; std::vector<nghttp2_nv> nva;
d.crumble_request_cookie(nva); d.crumble_request_cookie(nva);
@ -139,12 +139,13 @@ void test_downstream_crumble_request_cookie(void) {
void test_downstream_assemble_request_cookie(void) { void test_downstream_assemble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0, 0); Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header(":method", "get"); auto &req = d.request();
d.add_request_header(":path", "/"); req.fs.add_header(":method", "get");
d.add_request_header("cookie", "alpha"); req.fs.add_header(":path", "/");
d.add_request_header("cookie", "bravo;"); req.fs.add_header("cookie", "alpha");
d.add_request_header("cookie", "charlie; "); req.fs.add_header("cookie", "bravo;");
d.add_request_header("cookie", "delta;;"); req.fs.add_header("cookie", "charlie; ");
req.fs.add_header("cookie", "delta;;");
d.assemble_request_cookie(); d.assemble_request_cookie();
CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_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) { void test_downstream_rewrite_location_response_header(void) {
{ {
Downstream d(nullptr, nullptr, 0, 0); Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
d.set_request_downstream_host("localhost:3000"); 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.add_response_header("location", "http://localhost:3000/");
d.index_request_headers(); req.fs.index_headers();
d.index_response_headers(); d.index_response_headers();
d.rewrite_location_response_header("https"); d.rewrite_location_response_header("https");
auto location = d.get_response_header(http2::HD_LOCATION); 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); Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
d.set_request_downstream_host("localhost"); d.set_request_downstream_host("localhost");
d.set_request_http2_authority("localhost"); req.authority = "localhost";
d.add_response_header("location", "http://localhost:3000/"); d.add_response_header("location", "http://localhost:3000/");
d.index_response_headers(); d.index_response_headers();
d.rewrite_location_response_header("https"); d.rewrite_location_response_header("https");

View File

@ -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. // on the priority, DATA frame may come first.
return NGHTTP2_ERR_DEFERRED; return NGHTTP2_ERR_DEFERRED;
} }
const auto &req = downstream->request();
auto input = downstream->get_request_buf(); auto input = downstream->get_request_buf();
auto nread = input->remove(buf, length); auto nread = input->remove(buf, length);
auto input_empty = input->rleft() == 0; 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 // If connection is upgraded, don't set EOF flag, since HTTP/1
// will set MSG_COMPLETE to request state after upgrade response // will set MSG_COMPLETE to request state after upgrade response
// header is seen. // header is seen.
(!downstream->get_upgrade_request() || (!req.upgrade_request ||
(downstream->get_response_state() == Downstream::HEADER_COMPLETE && (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
!downstream->get_upgraded()))) { !downstream->get_upgraded()))) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *data_flags |= NGHTTP2_DATA_FLAG_EOF;
auto &trailers = downstream->get_request_trailers(); const auto &trailers = req.fs.trailers();
if (!trailers.empty()) { if (!trailers.empty()) {
std::vector<nghttp2_nv> nva; std::vector<nghttp2_nv> nva;
nva.reserve(trailers.size()); nva.reserve(trailers.size());
@ -248,10 +249,11 @@ int Http2DownstreamConnection::push_request_headers() {
downstream_->set_request_pending(false); downstream_->set_request_pending(false);
auto method = downstream_->get_request_method(); const auto &req = downstream_->request();
auto no_host_rewrite = get_config()->no_host_rewrite ||
get_config()->http2_proxy || auto no_host_rewrite =
get_config()->client_proxy || method == HTTP_CONNECT; 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 // http2session_ has already in CONNECTED state, so we can get
// addr_idx here. // 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 // For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless. // case, we use backend server's host nonetheless.
const char *authority = downstream_hostport; const char *authority = downstream_hostport;
auto &req_authority = downstream_->get_request_http2_authority(); if (no_host_rewrite && !req.authority.empty()) {
if (no_host_rewrite && !req_authority.empty()) { authority = req.authority.c_str();
authority = req_authority.c_str();
} }
downstream_->set_request_downstream_host(authority); downstream_->set_request_downstream_host(authority);
auto nheader = downstream_->get_request_headers().size();
size_t num_cookies = 0; size_t num_cookies = 0;
if (!get_config()->http2_no_cookie_crumbling) { if (!get_config()->http2_no_cookie_crumbling) {
num_cookies = downstream_->count_crumble_request_cookie(); num_cookies = downstream_->count_crumble_request_cookie();
@ -289,34 +288,30 @@ int Http2DownstreamConnection::push_request_headers() {
// 7. x-forwarded-proto (optional) // 7. x-forwarded-proto (optional)
// 8. te (optional) // 8. te (optional)
auto nva = std::vector<nghttp2_nv>(); auto nva = std::vector<nghttp2_nv>();
nva.reserve(nheader + 8 + num_cookies + nva.reserve(req.fs.headers().size() + 8 + num_cookies +
get_config()->add_request_headers.size()); get_config()->add_request_headers.size());
nva.push_back( nva.push_back(
http2::make_nv_lc_nocopy(":method", http2::to_method_string(method))); http2::make_nv_lc_nocopy(":method", http2::to_method_string(req.method)));
auto &scheme = downstream_->get_request_http2_scheme();
nva.push_back(http2::make_nv_lc_nocopy(":authority", authority)); nva.push_back(http2::make_nv_lc_nocopy(":authority", authority));
if (method != HTTP_CONNECT) { if (req.method != HTTP_CONNECT) {
assert(!scheme.empty()); 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 (req.method == HTTP_OPTIONS && req.path.empty()) {
if (method == HTTP_OPTIONS && path.empty()) {
nva.push_back(http2::make_nv_ll(":path", "*")); nva.push_back(http2::make_nv_ll(":path", "*"));
} else { } 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; bool chunked_encoding = false;
auto transfer_encoding = auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING);
downstream_->get_request_header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding && if (transfer_encoding &&
util::strieq_l("chunked", (*transfer_encoding).value)) { util::strieq_l("chunked", (*transfer_encoding).value)) {
chunked_encoding = true; chunked_encoding = true;
@ -327,7 +322,7 @@ int Http2DownstreamConnection::push_request_headers() {
} }
std::string xff_value; 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 (get_config()->add_x_forwarded_for) {
if (xff && !get_config()->strip_incoming_x_forwarded_for) { if (xff && !get_config()->strip_incoming_x_forwarded_for) {
xff_value = (*xff).value; xff_value = (*xff).value;
@ -341,13 +336,13 @@ int Http2DownstreamConnection::push_request_headers() {
} }
if (!get_config()->http2_proxy && !get_config()->client_proxy && 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 // 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; 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 (get_config()->no_via) {
if (via) { if (via) {
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value)); 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;
via_value += ", "; via_value += ", ";
} }
via_value += http::create_via_header_value( via_value += http::create_via_header_value(req.http_major, req.http_minor);
downstream_->get_request_major(), downstream_->get_request_minor());
nva.push_back(http2::make_nv_ls("via", via_value)); 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 // HTTP/1 upstream request can contain keyword other than
// "trailers". We just forward "trailers". // "trailers". We just forward "trailers".
// TODO more strict handling required here. // TODO more strict handling required here.
@ -382,12 +376,11 @@ int Http2DownstreamConnection::push_request_headers() {
DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
} }
auto content_length = auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH);
downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
// TODO check content-length: 0 case // TODO check content-length: 0 case
if (downstream_->get_request_method() == HTTP_CONNECT || chunked_encoding || if (req.method == HTTP_CONNECT || chunked_encoding || content_length ||
content_length || downstream_->get_request_http2_expect_body()) { req.http2_expect_body) {
// Request-body is expected. // Request-body is expected.
nghttp2_data_provider data_prd; nghttp2_data_provider data_prd;
data_prd.source.ptr = this; data_prd.source.ptr = this;

View File

@ -783,10 +783,11 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
assert(promised_downstream); assert(promised_downstream);
auto &promised_req = promised_downstream->request();
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
promised_downstream->add_request_header(name, namelen, value, valuelen, promised_req.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
token);
return 0; return 0;
} }
} }
@ -855,6 +856,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
int rv; int rv;
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
auto &nva = downstream->get_response_headers(); auto &nva = downstream->get_response_headers();
@ -927,9 +929,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
downstream->expect_response_body()) { downstream->expect_response_body()) {
// Here we have response body but Content-Length is not known in // Here we have response body but Content-Length is not known in
// advance. // advance.
if (downstream->get_request_major() <= 0 || if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
(downstream->get_request_major() == 1 &&
downstream->get_request_minor() == 0)) {
// We simply close connection for pre-HTTP/1.1 in this case. // We simply close connection for pre-HTTP/1.1 in this case.
downstream->set_response_connection_close(true); downstream->set_response_connection_close(true);
} else { } else {
@ -1892,14 +1892,15 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
int Http2Session::handle_downstream_push_promise_complete( int Http2Session::handle_downstream_push_promise_complete(
Downstream *downstream, Downstream *promised_downstream) { Downstream *downstream, Downstream *promised_downstream) {
auto authority = auto &promised_req = promised_downstream->request();
promised_downstream->get_request_header(http2::HD__AUTHORITY);
auto path = promised_downstream->get_request_header(http2::HD__PATH); auto authority = promised_req.fs.header(http2::HD__AUTHORITY);
auto method = promised_downstream->get_request_header(http2::HD__METHOD); auto path = promised_req.fs.header(http2::HD__PATH);
auto scheme = promised_downstream->get_request_header(http2::HD__SCHEME); auto method = promised_req.fs.header(http2::HD__METHOD);
auto scheme = promised_req.fs.header(http2::HD__SCHEME);
if (!authority) { 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); 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 // TODO Rewrite authority if we enabled rewrite host. But we
// really don't know how to rewrite host. Should we use the same // really don't know how to rewrite host. Should we use the same
// host in associated stream? // host in associated stream?
promised_downstream->set_request_http2_authority( promised_req.authority = http2::value_to_str(authority);
http2::value_to_str(authority)); promised_req.method = method_token;
promised_downstream->set_request_method(method_token);
// libnghttp2 ensures that we don't have CONNECT method in // libnghttp2 ensures that we don't have CONNECT method in
// PUSH_PROMISE, and guarantees that :scheme exists. // 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. // For server-wide OPTIONS request, path is empty.
if (method_token != HTTP_OPTIONS || path->value != "*") { if (method_token != HTTP_OPTIONS || path->value != "*") {
promised_downstream->set_request_path(http2::rewrite_clean_path( promised_req.path = http2::rewrite_clean_path(std::begin(path->value),
std::begin(path->value), std::end(path->value))); std::end(path->value));
} }
promised_downstream->inspect_http2_request(); promised_downstream->inspect_http2_request();

View File

@ -111,7 +111,7 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
rv = nghttp2_session_upgrade2( rv = nghttp2_session_upgrade2(
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()), session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
settings_payload.size(), settings_payload.size(),
http->get_downstream()->get_request_method() == HTTP_HEAD, nullptr); http->get_downstream()->request().method == HTTP_HEAD, nullptr);
if (rv != 0) { if (rv != 0) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " 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; 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 || get_config()->header_field_buffer ||
downstream->get_request_headers().size() + req.fs.num_fields() >= get_config()->max_header_fields) {
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0; return 0;
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large or many header field size=" ULOG(INFO, upstream) << "Too large or many header field size="
<< downstream->get_request_headers_sum() + namelen + << req.fs.buffer_size() + namelen + valuelen
valuelen << ", num=" << ", num=" << req.fs.num_fields() + 1;
<< downstream->get_request_headers().size() + 1;
} }
// just ignore header fields if this is trailer part. // just ignore header fields if this is trailer part.
@ -197,14 +196,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
// just store header fields for trailer part // just store header fields for trailer part
downstream->add_request_trailer(name, namelen, value, valuelen, req.fs.add_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
return 0; return 0;
} }
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
downstream->add_request_header(name, namelen, value, valuelen, req.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token); flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0; return 0;
} }
@ -233,10 +232,12 @@ int on_begin_headers_callback(nghttp2_session *session,
downstream->reset_upstream_rtimer(); downstream->reset_upstream_rtimer();
auto &req = downstream->request();
// Although, we deprecated minor version from HTTP/2, we supply // Although, we deprecated minor version from HTTP/2, we supply
// minor version 0 to use via header field in a conventional way. // minor version 0 to use via header field in a conventional way.
downstream->set_request_major(2); req.http_major = 2;
downstream->set_request_minor(0); req.http_minor = 0;
upstream->add_pending_downstream(std::move(downstream)); upstream->add_pending_downstream(std::move(downstream));
@ -250,7 +251,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
return 0; return 0;
} }
auto &nva = downstream->get_request_headers(); auto &req = downstream->request();
auto &nva = req.fs.headers();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
std::stringstream ss; 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); http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
} }
auto content_length = auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH);
downstream->get_request_header(http2::HD_CONTENT_LENGTH);
if (content_length) { if (content_length) {
// libnghttp2 guarantees this can be parsed // libnghttp2 guarantees this can be parsed
auto len = util::parse_uint(content_length->value); req.fs.content_length = util::parse_uint(content_length->value);
downstream->set_request_content_length(len);
} }
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. // 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); auto method_token = http2::lookup_method_token(method->value);
if (method_token == -1) { if (method_token == -1) {
@ -294,28 +293,29 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
return 0; return 0;
} }
downstream->set_request_method(method_token); req.method = method_token;
downstream->set_request_http2_scheme(http2::value_to_str(scheme)); req.scheme = http2::value_to_str(scheme);
// nghttp2 library guarantees either :authority or host exist // nghttp2 library guarantees either :authority or host exist
if (!authority) { 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 (path) {
if (method_token == HTTP_OPTIONS && path->value == "*") { if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty. // Server-wide OPTIONS request. Path is empty.
} else if (get_config()->http2_proxy || get_config()->client_proxy) { } 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 { } else {
auto &value = path->value; const auto &value = path->value;
downstream->set_request_path( req.path = http2::rewrite_clean_path(std::begin(value), std::end(value));
http2::rewrite_clean_path(std::begin(value), std::end(value)));
} }
} }
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
downstream->set_request_http2_expect_body(true); req.http2_expect_body = true;
} }
downstream->inspect_http2_request(); downstream->inspect_http2_request();
@ -352,8 +352,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
} }
void Http2Upstream::start_downstream(Downstream *downstream) { void Http2Upstream::start_downstream(Downstream *downstream) {
if (downstream_queue_.can_activate( if (downstream_queue_.can_activate(downstream->request().authority)) {
downstream->get_request_http2_authority())) {
initiate_downstream(downstream); initiate_downstream(downstream);
return; return;
} }
@ -553,6 +552,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
auto downstream = make_unique<Downstream>(upstream, handler->get_mcpool(), auto downstream = make_unique<Downstream>(upstream, handler->get_mcpool(),
promised_stream_id, 0); promised_stream_id, 0);
auto &req = downstream->request();
// As long as we use nghttp2_session_mem_send(), setting stream // As long as we use nghttp2_session_mem_send(), setting stream
// user data here should not fail. This is because this callback // 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->disable_upstream_rtimer();
downstream->set_request_major(2); req.http_major = 2;
downstream->set_request_minor(0); req.http_minor = 0;
for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { for (size_t i = 0; i < frame->push_promise.nvlen; ++i) {
auto &nv = frame->push_promise.nva[i]; auto &nv = frame->push_promise.nva[i];
auto token = http2::lookup_token(nv.name, nv.namelen); auto token = http2::lookup_token(nv.name, nv.namelen);
switch (token) { switch (token) {
case http2::HD__METHOD: case http2::HD__METHOD:
downstream->set_request_method( req.method = http2::lookup_method_token(nv.value, nv.valuelen);
http2::lookup_method_token(nv.value, nv.valuelen));
break; break;
case http2::HD__SCHEME: case http2::HD__SCHEME:
downstream->set_request_http2_scheme( req.scheme.assign(nv.value, nv.value + nv.valuelen);
{nv.value, nv.value + nv.valuelen});
break; break;
case http2::HD__AUTHORITY: case http2::HD__AUTHORITY:
downstream->set_request_http2_authority( req.authority.assign(nv.value, nv.value + nv.valuelen);
{nv.value, nv.value + nv.valuelen});
break; break;
case http2::HD__PATH: case http2::HD__PATH:
downstream->set_request_path( req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen);
http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen));
break; break;
} }
downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, req.fs.add_header(nv.name, nv.namelen, nv.value, nv.valuelen,
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
token);
} }
downstream->inspect_http2_request(); downstream->inspect_http2_request();
@ -1433,6 +1428,8 @@ void Http2Upstream::remove_downstream(Downstream *downstream) {
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
int rv; int rv;
const auto &req = downstream->request();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
if (downstream->get_non_final_response()) { if (downstream->get_non_final_response()) {
DLOG(INFO, downstream) << "HTTP non-final response header"; 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 && if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!get_config()->no_location_rewrite) { !get_config()->no_location_rewrite) {
downstream->rewrite_location_response_header( downstream->rewrite_location_response_header(req.scheme);
downstream->get_request_http2_scheme());
} }
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
@ -1582,8 +1578,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
(downstream->get_stream_id() % 2) && (downstream->get_stream_id() % 2) &&
downstream->get_response_header(http2::HD_LINK) && downstream->get_response_header(http2::HD_LINK) &&
downstream->get_response_http_status() == 200 && downstream->get_response_http_status() == 200 &&
(downstream->get_request_method() == HTTP_GET || (req.method == HTTP_GET || req.method == HTTP_POST)) {
downstream->get_request_method() == HTTP_POST)) {
if (prepare_push_promise(downstream) != 0) { if (prepare_push_promise(downstream) != 0) {
// Continue to send response even if push was failed. // Continue to send response even if push was failed.
@ -1766,8 +1761,9 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
const char *base; const char *base;
size_t baselen; size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen, const auto &req = downstream->request();
downstream->get_request_path());
rv = http2::get_pure_path_component(&base, &baselen, req.path);
if (rv != 0) { if (rv != 0) {
return 0; return 0;
} }
@ -1782,6 +1778,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
auto uri = link.uri.first; auto uri = link.uri.first;
auto len = link.uri.second - link.uri.first; auto len = link.uri.second - link.uri.first;
const std::string *scheme_ptr, *authority_ptr;
std::string scheme, authority, path; std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, rv = http2::construct_push_component(scheme, authority, path, base,
@ -1791,14 +1788,18 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
} }
if (scheme.empty()) { if (scheme.empty()) {
scheme = downstream->get_request_http2_scheme(); scheme_ptr = &req.scheme;
} else {
scheme_ptr = &scheme;
} }
if (authority.empty()) { 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) { if (rv != 0) {
return -1; return -1;
} }
@ -1811,9 +1812,11 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
const std::string &authority, const std::string &authority,
const std::string &path, const std::string &path,
Downstream *downstream) { Downstream *downstream) {
const auto &req = downstream->request();
std::vector<nghttp2_nv> nva; std::vector<nghttp2_nv> nva;
// 4 for :method, :scheme, :path and :authority // 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 // juse use "GET" for now
nva.push_back(http2::make_nv_ll(":method", "GET")); 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(":path", path));
nva.push_back(http2::make_nv_ls(":authority", authority)); 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) { switch (kv.token) {
// TODO generate referer // TODO generate referer
case http2::HD__AUTHORITY: case http2::HD__AUTHORITY:
@ -1884,12 +1887,14 @@ int Http2Upstream::initiate_push(Downstream *downstream, const char *uri,
const char *base; const char *base;
size_t baselen; size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen, const auto &req = downstream->request();
downstream->get_request_path());
rv = http2::get_pure_path_component(&base, &baselen, req.path);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
} }
const std::string *scheme_ptr, *authority_ptr;
std::string scheme, authority, path; std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, baselen, 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()) { if (scheme.empty()) {
scheme = downstream->get_request_http2_scheme(); scheme_ptr = &req.scheme;
} else {
scheme_ptr = &scheme;
} }
if (authority.empty()) { 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) { if (rv != 0) {
return -1; return -1;
@ -1939,12 +1948,14 @@ Http2Upstream::on_downstream_push_promise(Downstream *downstream,
// frontend. // frontend.
auto promised_downstream = auto promised_downstream =
make_unique<Downstream>(this, handler_->get_mcpool(), 0, 0); make_unique<Downstream>(this, handler_->get_mcpool(), 0, 0);
auto &promised_req = promised_downstream->request();
promised_downstream->set_downstream_stream_id(promised_stream_id); promised_downstream->set_downstream_stream_id(promised_stream_id);
promised_downstream->disable_upstream_rtimer(); promised_downstream->disable_upstream_rtimer();
promised_downstream->set_request_major(2); promised_req.http_major = 2;
promised_downstream->set_request_minor(0); promised_req.http_minor = 0;
auto ptr = promised_downstream.get(); auto ptr = promised_downstream.get();
add_pending_downstream(std::move(promised_downstream)); add_pending_downstream(std::move(promised_downstream));
@ -1957,7 +1968,8 @@ int Http2Upstream::on_downstream_push_promise_complete(
Downstream *downstream, Downstream *promised_downstream) { Downstream *downstream, Downstream *promised_downstream) {
std::vector<nghttp2_nv> nva; std::vector<nghttp2_nv> 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()); nva.reserve(headers.size());

View File

@ -214,19 +214,19 @@ int HttpDownstreamConnection::push_request_headers() {
->downstream_addr_groups[group_] ->downstream_addr_groups[group_]
.addrs[addr_idx_] .addrs[addr_idx_]
.hostport.get(); .hostport.get();
auto method = downstream_->get_request_method(); const auto &req = downstream_->request();
auto connect_method = method == HTTP_CONNECT;
auto connect_method = req.method == HTTP_CONNECT;
// For HTTP/1.0 request, there is no authority in request. In that // For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless. // case, we use backend server's host nonetheless.
const char *authority = downstream_hostport; const char *authority = downstream_hostport;
auto &req_authority = downstream_->get_request_http2_authority();
auto no_host_rewrite = get_config()->no_host_rewrite || auto no_host_rewrite = get_config()->no_host_rewrite ||
get_config()->http2_proxy || get_config()->http2_proxy ||
get_config()->client_proxy || connect_method; get_config()->client_proxy || connect_method;
if (no_host_rewrite && !req_authority.empty()) { if (no_host_rewrite && !req.authority.empty()) {
authority = req_authority.c_str(); authority = req.authority.c_str();
} }
auto authoritylen = strlen(authority); auto authoritylen = strlen(authority);
@ -237,35 +237,31 @@ int HttpDownstreamConnection::push_request_headers() {
auto buf = downstream_->get_request_buf(); auto buf = downstream_->get_request_buf();
// Assume that method and request path do not contain \r\n. // 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(meth, strlen(meth));
buf->append(" "); buf->append(" ");
auto &scheme = downstream_->get_request_http2_scheme();
auto &path = downstream_->get_request_path();
if (connect_method) { if (connect_method) {
buf->append(authority, authoritylen); buf->append(authority, authoritylen);
} else if (get_config()->http2_proxy || get_config()->client_proxy) { } else if (get_config()->http2_proxy || get_config()->client_proxy) {
// Construct absolute-form request target because we are going to // Construct absolute-form request target because we are going to
// send a request to a HTTP/1 proxy. // send a request to a HTTP/1 proxy.
assert(!scheme.empty()); assert(!req.scheme.empty());
buf->append(scheme); buf->append(req.scheme);
buf->append("://"); buf->append("://");
buf->append(authority, authoritylen); buf->append(authority, authoritylen);
buf->append(path); buf->append(req.path);
} else if (method == HTTP_OPTIONS && path.empty()) { } else if (req.method == HTTP_OPTIONS && req.path.empty()) {
// Server-wide OPTIONS // Server-wide OPTIONS
buf->append("*"); buf->append("*");
} else { } else {
buf->append(path); buf->append(req.path);
} }
buf->append(" HTTP/1.1\r\nHost: "); buf->append(" HTTP/1.1\r\nHost: ");
buf->append(authority, authoritylen); buf->append(authority, authoritylen);
buf->append("\r\n"); buf->append("\r\n");
http2::build_http1_headers_from_headers(buf, http2::build_http1_headers_from_headers(buf, req.fs.headers());
downstream_->get_request_headers());
if (!downstream_->get_assembled_request_cookie().empty()) { if (!downstream_->get_assembled_request_cookie().empty()) {
buf->append("Cookie: "); buf->append("Cookie: ");
@ -273,26 +269,26 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append("\r\n"); buf->append("\r\n");
} }
if (!connect_method && downstream_->get_request_http2_expect_body() && if (!connect_method && req.http2_expect_body &&
!downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) { !req.fs.header(http2::HD_CONTENT_LENGTH)) {
downstream_->set_chunked_request(true); downstream_->set_chunked_request(true);
buf->append("Transfer-Encoding: chunked\r\n"); buf->append("Transfer-Encoding: chunked\r\n");
} }
if (downstream_->get_request_connection_close()) { if (req.connection_close) {
buf->append("Connection: close\r\n"); buf->append("Connection: close\r\n");
} }
if (!connect_method && downstream_->get_upgrade_request()) { if (!connect_method && req.upgrade_request) {
auto connection = downstream_->get_request_header(http2::HD_CONNECTION); auto connection = req.fs.header(http2::HD_CONNECTION);
if (connection) { if (connection) {
buf->append("Connection: "); buf->append("Connection: ");
buf->append((*connection).value); buf->append((*connection).value);
buf->append("\r\n"); buf->append("\r\n");
} }
auto upgrade = downstream_->get_request_header(http2::HD_UPGRADE); auto upgrade = req.fs.header(http2::HD_UPGRADE);
if (upgrade) { if (upgrade) {
buf->append("Upgrade: "); buf->append("Upgrade: ");
buf->append((*upgrade).value); 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) { if (get_config()->add_x_forwarded_for) {
buf->append("X-Forwarded-For: "); buf->append("X-Forwarded-For: ");
if (xff && !get_config()->strip_incoming_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 && if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!connect_method) { !connect_method) {
buf->append("X-Forwarded-Proto: "); buf->append("X-Forwarded-Proto: ");
assert(!scheme.empty()); assert(!req.scheme.empty());
buf->append(scheme); buf->append(req.scheme);
buf->append("\r\n"); 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 (get_config()->no_via) {
if (via) { if (via) {
buf->append("Via: "); buf->append("Via: ");
@ -334,8 +330,7 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append((*via).value); buf->append((*via).value);
buf->append(", "); buf->append(", ");
} }
buf->append(http::create_via_header_value( buf->append(http::create_via_header_value(req.http_major, req.http_minor));
downstream_->get_request_major(), downstream_->get_request_minor()));
buf->append("\r\n"); buf->append("\r\n");
} }
@ -392,8 +387,10 @@ int HttpDownstreamConnection::end_upload_data() {
return 0; return 0;
} }
const auto &req = downstream_->request();
auto output = downstream_->get_request_buf(); auto output = downstream_->get_request_buf();
auto &trailers = downstream_->get_request_trailers(); const auto &trailers = req.fs.trailers();
if (trailers.empty()) { if (trailers.empty()) {
output->append("0\r\n\r\n"); output->append("0\r\n\r\n");
} else { } else {
@ -484,6 +481,7 @@ namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
int rv; int rv;
downstream->set_response_http_status(htp->status_code); 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 // TODO It seems that the cases other than HEAD are handled by
// http-parser. Need test. // http-parser. Need test.
return downstream->get_request_method() == HTTP_HEAD || return req.method == HTTP_HEAD || (100 <= status && status <= 199) ||
(100 <= status && status <= 199) || status == 204 || status == 204 || status == 304
status == 304
? 1 ? 1
: 0; : 0;
} }

View File

@ -85,25 +85,27 @@ namespace {
int htp_uricb(http_parser *htp, const char *data, size_t len) { int htp_uricb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request();
// We happen to have the same value for method token. // 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 > if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large URI size=" 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); assert(downstream->get_request_state() == Downstream::INITIAL);
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1; return -1;
} }
downstream->add_request_headers_sum(len);
if (downstream->get_request_method() == HTTP_CONNECT) { req.fs.add_extra_buffer_size(len);
downstream->append_request_http2_authority(data, len);
if (req.method == HTTP_CONNECT) {
req.authority.append(data, len);
} else { } else {
downstream->append_request_path(data, len); req.path.append(data, len);
} }
return 0; return 0;
@ -114,11 +116,12 @@ namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len > auto &req = downstream->request();
get_config()->header_field_buffer) {
if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size=" 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) { if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); 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; return -1;
} }
if (downstream->get_request_state() == Downstream::INITIAL) { if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) { if (req.fs.header_key_prev()) {
downstream->append_last_request_header_key(data, len); req.fs.append_last_header_key(data, len);
} else { } else {
if (downstream->get_request_headers().size() >= if (req.fs.num_fields() >= get_config()->max_header_fields) {
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num=" ULOG(INFO, upstream)
<< downstream->get_request_headers().size() + 1; << "Too many header field num=" << req.fs.num_fields() + 1;
} }
downstream->set_request_state( downstream->set_request_state(
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1; return -1;
} }
downstream->add_request_header(std::string(data, len), ""); req.fs.add_header(std::string(data, len), "");
} }
} else { } else {
// trailer part // trailer part
if (downstream->get_request_trailer_key_prev()) { if (req.fs.trailer_key_prev()) {
downstream->append_last_request_trailer_key(data, len); req.fs.append_last_trailer_key(data, len);
} else { } else {
if (downstream->get_request_headers().size() + if (req.fs.num_fields() >= get_config()->max_header_fields) {
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num=" ULOG(INFO, upstream)
<< downstream->get_request_headers().size() + 1; << "Too many header field num=" << req.fs.num_fields() + 1;
} }
return -1; return -1;
} }
downstream->add_request_trailer(std::string(data, len), ""); req.fs.add_trailer(std::string(data, len), "");
} }
} }
return 0; return 0;
@ -166,11 +166,12 @@ namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len > auto &req = downstream->request();
get_config()->header_field_buffer) {
if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size=" 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) { if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); 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; return -1;
} }
if (downstream->get_request_state() == Downstream::INITIAL) { if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) { if (req.fs.header_key_prev()) {
downstream->set_last_request_header_value(data, len); req.fs.set_last_header_value(data, len);
} else { } else {
downstream->append_last_request_header_value(data, len); req.fs.append_last_header_value(data, len);
} }
} else { } else {
if (downstream->get_request_trailer_key_prev()) { if (req.fs.trailer_key_prev()) {
downstream->set_last_request_trailer_value(data, len); req.fs.set_last_trailer_value(data, len);
} else { } else {
downstream->append_last_request_trailer_value(data, len); req.fs.append_last_trailer_value(data, len);
} }
} }
return 0; return 0;
@ -195,13 +196,14 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
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) { http_parser_url &u) {
assert(u.field_set & (1 << UF_HOST)); 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 // As per https://tools.ietf.org/html/rfc7230#section-5.4, we
// rewrite host header field with authority component. // rewrite host header field with authority component.
std::string authority;
http2::copy_url_component(authority, &u, UF_HOST, uri); http2::copy_url_component(authority, &u, UF_HOST, uri);
// TODO properly check IPv6 numeric address // TODO properly check IPv6 numeric address
if (authority.find(':') != std::string::npos) { if (authority.find(':') != std::string::npos) {
@ -212,23 +214,20 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
authority += ':'; authority += ':';
authority += util::utos(u.port); authority += util::utos(u.port);
} }
downstream->set_request_http2_authority(authority);
std::string scheme; http2::copy_url_component(req.scheme, &u, UF_SCHEMA, uri);
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
downstream->set_request_http2_scheme(std::move(scheme));
std::string path; std::string path;
if (u.field_set & (1 << UF_PATH)) { if (u.field_set & (1 << UF_PATH)) {
http2::copy_url_component(path, &u, UF_PATH, uri); 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: // Server-wide OPTIONS takes following form in proxy request:
// //
// OPTIONS http://example.org HTTP/1.1 // OPTIONS http://example.org HTTP/1.1
// //
// Notice that no slash after authority. See // Notice that no slash after authority. See
// http://tools.ietf.org/html/rfc7230#section-5.3.4 // http://tools.ietf.org/html/rfc7230#section-5.3.4
downstream->set_request_path(""); req.path = "";
// we ignore query component here // we ignore query component here
return; return;
} else { } else {
@ -240,10 +239,9 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
path.append(uri + fdata.off, fdata.len); path.append(uri + fdata.off, fdata.len);
} }
if (get_config()->http2_proxy || get_config()->client_proxy) { if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(std::move(path)); req.path = std::move(path);
} else { } else {
downstream->set_request_path( req.path = http2::rewrite_clean_path(std::begin(path), std::end(path));
http2::rewrite_clean_path(std::begin(path), std::end(path)));
} }
} }
} // namespace } // namespace
@ -256,36 +254,34 @@ int htp_hdrs_completecb(http_parser *htp) {
ULOG(INFO, upstream) << "HTTP request headers completed"; ULOG(INFO, upstream) << "HTTP request headers completed";
} }
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request();
downstream->set_request_major(htp->http_major); req.http_major = htp->http_major;
downstream->set_request_minor(htp->http_minor); 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)) { if (LOG_ENABLED(INFO)) {
std::stringstream ss; std::stringstream ss;
ss << http2::to_method_string(method) << " " ss << http2::to_method_string(method) << " "
<< (method == HTTP_CONNECT ? downstream->get_request_http2_authority() << (method == HTTP_CONNECT ? req.authority : req.path) << " "
: downstream->get_request_path()) << " " << "HTTP/" << req.http_major << "." << req.http_minor << "\n";
<< "HTTP/" << downstream->get_request_major() << "."
<< downstream->get_request_minor() << "\n"; for (const auto &kv : req.fs.headers()) {
const auto &headers = downstream->get_request_headers(); ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
for (size_t i = 0; i < headers.size(); ++i) {
ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": "
<< headers[i].value << "\n";
} }
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
} }
if (downstream->index_request_headers() != 0) { if (req.fs.index_headers() != 0) {
return -1; return -1;
} }
if (downstream->get_request_major() == 1 && if (req.http_major == 1 && req.http_minor == 1 &&
downstream->get_request_minor() == 1 && !req.fs.header(http2::HD_HOST)) {
!downstream->get_request_header(http2::HD_HOST)) {
return -1; return -1;
} }
@ -295,7 +291,7 @@ int htp_hdrs_completecb(http_parser *htp) {
http_parser_url u{}; http_parser_url u{};
// make a copy of request path, since we may set request path // make a copy of request path, since we may set request path
// while we are refering to original 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); rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u);
if (rv != 0) { if (rv != 0) {
// Expect to respond with 400 bad request // Expect to respond with 400 bad request
@ -309,24 +305,23 @@ int htp_hdrs_completecb(http_parser *htp) {
} }
if (method == HTTP_OPTIONS && path == "*") { if (method == HTTP_OPTIONS && path == "*") {
downstream->set_request_path(""); req.path = "";
} else { } else {
downstream->set_request_path( req.path = http2::rewrite_clean_path(std::begin(path), std::end(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) { if (host) {
downstream->set_request_http2_authority(host->value); req.authority = host->value;
} }
if (upstream->get_client_handler()->get_ssl()) { if (upstream->get_client_handler()->get_ssl()) {
downstream->set_request_http2_scheme("https"); req.scheme = "https";
} else { } else {
downstream->set_request_http2_scheme("http"); req.scheme = "http";
} }
} else { } 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, int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen) { size_t bodylen) {
auto major = downstream->get_request_major(); const auto &req = downstream->request();
auto minor = downstream->get_request_minor();
auto connection_close = false; 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; connection_close = true;
} else { } else {
auto c = downstream->get_response_header(http2::HD_CONNECTION); 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 #ifdef HAVE_MRUBY
if (!downstream->get_non_final_response()) { if (!downstream->get_non_final_response()) {
auto worker = handler_->get_worker(); auto worker = handler_->get_worker();
@ -912,14 +908,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
} }
#endif // HAVE_MRUBY #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(); auto buf = downstream->get_response_buf();
buf->append("HTTP/"); buf->append("HTTP/");
buf->append(util::utos(downstream->get_request_major())); buf->append(util::utos(req.http_major));
buf->append("."); buf->append(".");
buf->append(util::utos(downstream->get_request_minor())); buf->append(util::utos(req.http_minor));
buf->append(" "); buf->append(" ");
buf->append(http2::get_status_string(downstream->get_response_http_status())); buf->append(http2::get_status_string(downstream->get_response_http_status()));
buf->append("\r\n"); 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 // We check downstream->get_response_connection_close() in case when
// the Content-Length is not available. // the Content-Length is not available.
if (!downstream->get_request_connection_close() && if (!req.connection_close && !downstream->get_response_connection_close()) {
!downstream->get_response_connection_close()) { if (req.http_major <= 0 || req.http_minor <= 0) {
if (downstream->get_request_major() <= 0 ||
downstream->get_request_minor() <= 0) {
// We add this header for HTTP/1.0 or HTTP/0.9 clients // We add this header for HTTP/1.0 or HTTP/0.9 clients
buf->append("Connection: Keep-Alive\r\n"); 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) { int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
const auto &req = downstream->request();
if (downstream->get_chunked_response()) { if (downstream->get_chunked_response()) {
auto output = downstream->get_response_buf(); auto output = downstream->get_response_buf();
auto &trailers = downstream->get_response_trailers(); auto &trailers = downstream->get_response_trailers();
@ -1087,8 +1083,7 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
downstream->set_response_connection_close(true); downstream->set_response_connection_close(true);
} }
if (downstream->get_request_connection_close() || if (req.connection_close || downstream->get_response_connection_close()) {
downstream->get_response_connection_close()) {
auto handler = get_client_handler(); auto handler = get_client_handler();
handler->set_should_close_after_write(true); handler->set_should_close_after_write(true);
} }

View File

@ -220,6 +220,11 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
auto downstream = lgsp.downstream; auto downstream = lgsp.downstream;
const Request *req = nullptr;
if (downstream) {
req = &downstream->request();
}
auto p = buf; auto p = buf;
auto avail = sizeof(buf) - 2; auto avail = sizeof(buf) - 2;
@ -259,8 +264,8 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
std::tie(p, avail) = copy(util::utos(lgsp.body_bytes_sent), avail, p); std::tie(p, avail) = copy(util::utos(lgsp.body_bytes_sent), avail, p);
break; break;
case SHRPX_LOGF_HTTP: case SHRPX_LOGF_HTTP:
if (downstream) { if (req) {
auto hd = downstream->get_request_header(lf.value.get()); auto hd = req->fs.header(lf.value.get());
if (hd) { if (hd) {
std::tie(p, avail) = copy((*hd).value, avail, p); std::tie(p, avail) = copy((*hd).value, avail, p);
break; break;
@ -271,10 +276,9 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
break; break;
case SHRPX_LOGF_AUTHORITY: case SHRPX_LOGF_AUTHORITY:
if (downstream) { if (req) {
auto &authority = downstream->get_request_http2_authority(); if (!req->authority.empty()) {
if (!authority.empty()) { std::tie(p, avail) = copy(req->authority, avail, p);
std::tie(p, avail) = copy(authority, avail, p);
break; break;
} }
} }

View File

@ -96,7 +96,7 @@ int MRubyContext::run_app(Downstream *downstream, int phase) {
mrb_->ud = nullptr; mrb_->ud = nullptr;
if (data.request_headers_dirty) { if (data.request_headers_dirty) {
downstream->index_request_headers(); downstream->request().fs.index_headers();
} }
if (data.response_headers_dirty) { if (data.response_headers_dirty) {

View File

@ -49,7 +49,8 @@ namespace {
mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) { mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -57,7 +58,8 @@ namespace {
mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) { mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -65,7 +67,8 @@ namespace {
mrb_value request_get_method(mrb_state *mrb, mrb_value self) { mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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); return mrb_str_new_cstr(mrb, method);
} }
@ -75,6 +78,7 @@ namespace {
mrb_value request_set_method(mrb_state *mrb, mrb_value self) { mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_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"); mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported");
} }
downstream->set_request_method(token); req.method = token;
return self; return self;
} }
@ -100,9 +104,9 @@ namespace {
mrb_value request_get_authority(mrb_state *mrb, mrb_value self) { mrb_value request_get_authority(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -110,6 +114,7 @@ namespace {
mrb_value request_set_authority(mrb_state *mrb, mrb_value self) { mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_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"); 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; return self;
} }
@ -130,9 +135,9 @@ namespace {
mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) { mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -140,6 +145,7 @@ namespace {
mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) { mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_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"); 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; return self;
} }
@ -160,9 +166,9 @@ namespace {
mrb_value request_get_path(mrb_state *mrb, mrb_value self) { mrb_value request_get_path(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -170,6 +176,7 @@ namespace {
mrb_value request_set_path(mrb_state *mrb, mrb_value self) { mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_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_int pathlen;
mrb_get_args(mrb, "s", &path, &pathlen); mrb_get_args(mrb, "s", &path, &pathlen);
downstream->set_request_path(std::string(path, pathlen)); req.path.assign(path, pathlen);
return self; return self;
} }
@ -187,7 +194,8 @@ namespace {
mrb_value request_get_headers(mrb_state *mrb, mrb_value self) { mrb_value request_get_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; 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 } // namespace
@ -195,6 +203,7 @@ namespace {
mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) { mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_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) { if (repl) {
size_t p = 0; size_t p = 0;
auto &headers = downstream->get_request_headers(); auto &headers = req.fs.headers();
for (size_t i = 0; i < headers.size(); ++i) { for (size_t i = 0; i < headers.size(); ++i) {
auto &hd = headers[i]; auto &hd = headers[i];
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key), if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key),
@ -227,13 +236,11 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto n = mrb_ary_len(mrb, values); auto n = mrb_ary_len(mrb, values);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
auto value = mrb_ary_entry(values, i); auto value = mrb_ary_entry(values, i);
downstream->add_request_header( req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value))); std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
} }
} else if (!mrb_nil_p(values)) { } else if (!mrb_nil_p(values)) {
downstream->add_request_header( req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(values), RSTRING_LEN(values))); std::string(RSTRING_PTR(values), RSTRING_LEN(values)));
} }
@ -259,10 +266,11 @@ namespace {
mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) { mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud); auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST); check_phase(mrb, data->phase, PHASE_REQUEST);
downstream->clear_request_headers(); req.fs.clear_headers();
return mrb_nil_value(); return mrb_nil_value();
} }

View File

@ -147,6 +147,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
auto downstream = upstream->add_pending_downstream( auto downstream = upstream->add_pending_downstream(
frame->syn_stream.stream_id, frame->syn_stream.pri); frame->syn_stream.stream_id, frame->syn_stream.pri);
auto &req = downstream->request();
downstream->reset_upstream_rtimer(); downstream->reset_upstream_rtimer();
auto nv = frame->syn_stream.nv; 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) { 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) { if (upstream->error_reply(downstream, 400) != 0) {
ULOG(FATAL, upstream) << "error_reply failed"; ULOG(FATAL, upstream) << "error_reply failed";
} }
return; return;
} }
auto path = downstream->get_request_header(http2::HD__PATH); auto path = req.fs.header(http2::HD__PATH);
auto scheme = downstream->get_request_header(http2::HD__SCHEME); auto scheme = req.fs.header(http2::HD__SCHEME);
auto host = downstream->get_request_header(http2::HD__HOST); auto host = req.fs.header(http2::HD__HOST);
auto method = downstream->get_request_header(http2::HD__METHOD); auto method = req.fs.header(http2::HD__METHOD);
if (!method) { if (!method) {
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
@ -222,24 +224,24 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
return; return;
} }
downstream->set_request_method(method_token); req.method = method_token;
if (is_connect) { if (is_connect) {
downstream->set_request_http2_authority(path->value); req.authority = path->value;
} else { } else {
downstream->set_request_http2_scheme(scheme->value); req.scheme = scheme->value;
downstream->set_request_http2_authority(host->value); req.authority = host->value;
if (get_config()->http2_proxy || get_config()->client_proxy) { 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 == "*") { } else if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty. // Server-wide OPTIONS request. Path is empty.
} else { } else {
downstream->set_request_path(http2::rewrite_clean_path( req.path = http2::rewrite_clean_path(std::begin(path->value),
std::begin(path->value), std::end(path->value))); std::end(path->value));
} }
} }
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { 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(); downstream->inspect_http2_request();
@ -285,8 +287,7 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
} // namespace } // namespace
void SpdyUpstream::start_downstream(Downstream *downstream) { void SpdyUpstream::start_downstream(Downstream *downstream) {
if (downstream_queue_.can_activate( if (downstream_queue_.can_activate(downstream->request().authority)) {
downstream->get_request_http2_authority())) {
initiate_downstream(downstream); initiate_downstream(downstream);
return; return;
} }
@ -946,6 +947,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
return 0; return 0;
} }
const auto &req = downstream->request();
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
auto worker = handler_->get_worker(); auto worker = handler_->get_worker();
auto mruby_ctx = worker->get_mruby_context(); 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 && if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!get_config()->no_location_rewrite) { !get_config()->no_location_rewrite) {
downstream->rewrite_location_response_header( downstream->rewrite_location_response_header(req.scheme);
downstream->get_request_http2_scheme());
} }
size_t nheader = downstream->get_response_headers().size(); size_t nheader = downstream->get_response_headers().size();
// 8 means server, :status, :version and possible via header field. // 8 means server, :status, :version and possible via header field.