Merge branch 'nghttpx-refactor-downstream'

This commit is contained in:
Tatsuhiro Tsujikawa 2016-01-14 01:06:45 +09:00
commit 543f2d58fc
17 changed files with 788 additions and 1135 deletions

View File

@ -101,14 +101,10 @@ int main(int argc, char *argv[]) {
shrpx::test_http2_get_pure_path_component) ||
!CU_add_test(pSuite, "http2_construct_push_component",
shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "downstream_index_request_headers",
shrpx::test_downstream_index_request_headers) ||
!CU_add_test(pSuite, "downstream_index_response_headers",
shrpx::test_downstream_index_response_headers) ||
!CU_add_test(pSuite, "downstream_get_request_header",
shrpx::test_downstream_get_request_header) ||
!CU_add_test(pSuite, "downstream_get_response_header",
shrpx::test_downstream_get_response_header) ||
!CU_add_test(pSuite, "downstream_field_store_index_headers",
shrpx::test_downstream_field_store_index_headers) ||
!CU_add_test(pSuite, "downstream_field_store_header",
shrpx::test_downstream_field_store_header) ||
!CU_add_test(pSuite, "downstream_crumble_request_cookie",
shrpx::test_downstream_crumble_request_cookie) ||
!CU_add_test(pSuite, "downstream_assemble_request_cookie",

View File

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

View File

@ -116,22 +116,14 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
: dlnext(nullptr), dlprev(nullptr),
request_start_time_(std::chrono::high_resolution_clock::now()),
request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0),
response_bodylen_(0), response_sent_bodylen_(0),
request_content_length_(-1), response_content_length_(-1),
upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0),
response_headers_sum_(0), request_datalen_(0), response_datalen_(0),
response_bodylen_(0), response_sent_bodylen_(0), upstream_(upstream),
blocked_link_(nullptr), request_datalen_(0), response_datalen_(0),
num_retry_(0), stream_id_(stream_id), priority_(priority),
downstream_stream_id_(-1),
response_rst_stream_error_code_(NGHTTP2_NO_ERROR), request_method_(-1),
request_state_(INITIAL), request_major_(1), request_minor_(1),
response_state_(INITIAL), response_http_status_(0), response_major_(1),
response_minor_(1), dispatch_state_(DISPATCH_NONE),
upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false),
chunked_request_(false), request_connection_close_(false),
request_header_key_prev_(false), request_trailer_key_prev_(false),
request_http2_expect_body_(false), chunked_response_(false),
response_connection_close_(false), response_header_key_prev_(false),
response_trailer_key_prev_(false), expect_final_response_(false),
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
request_state_(INITIAL), response_state_(INITIAL),
dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false),
chunked_response_(false), expect_final_response_(false),
request_pending_(false) {
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
@ -147,12 +139,6 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
upstream_wtimer_.data = this;
downstream_rtimer_.data = this;
downstream_wtimer_.data = this;
http2::init_hdidx(request_hdidx_);
http2::init_hdidx(response_hdidx_);
request_headers_.reserve(16);
response_headers_.reserve(32);
}
Downstream::~Downstream() {
@ -252,16 +238,10 @@ const Headers::value_type *get_header_linear(const Headers &headers,
}
} // namespace
const Headers &Downstream::get_request_headers() const {
return request_headers_;
}
Headers &Downstream::get_request_headers() { return request_headers_; }
void Downstream::assemble_request_cookie() {
std::string &cookie = assembled_request_cookie_;
std::string Downstream::assemble_request_cookie() const {
std::string cookie;
cookie = "";
for (auto &kv : request_headers_) {
for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) {
continue;
@ -278,11 +258,13 @@ void Downstream::assemble_request_cookie() {
if (cookie.size() >= 2) {
cookie.erase(cookie.size() - 2);
}
return cookie;
}
size_t Downstream::count_crumble_request_cookie() {
size_t n = 0;
for (auto &kv : request_headers_) {
for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) {
continue;
@ -307,7 +289,7 @@ size_t Downstream::count_crumble_request_cookie() {
}
void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
for (auto &kv : request_headers_) {
for (auto &kv : req_.fs.headers()) {
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
!util::streq_l("cooki", kv.name.c_str(), 5)) {
continue;
@ -335,10 +317,6 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
}
}
const std::string &Downstream::get_assembled_request_cookie() const {
return assembled_request_cookie_;
}
namespace {
void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name,
std::string value) {
@ -361,7 +339,7 @@ void add_header(size_t &sum, Headers &headers, const uint8_t *name,
} // namespace
namespace {
void append_last_header_key(bool key_prev, size_t &sum, Headers &headers,
void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers,
const char *data, size_t len) {
assert(key_prev);
sum += len;
@ -371,33 +349,21 @@ void append_last_header_key(bool key_prev, size_t &sum, Headers &headers,
} // namespace
namespace {
void append_last_header_value(bool key_prev, size_t &sum, Headers &headers,
void append_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
const char *data, size_t len) {
assert(!key_prev);
key_prev = false;
sum += len;
auto &item = headers.back();
item.value.append(data, len);
}
} // namespace
namespace {
void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
const char *data, size_t len) {
key_prev = false;
sum += len;
auto &item = headers.back();
item.value.assign(data, len);
}
} // namespace
namespace {
int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
int64_t &content_length) {
http2::init_hdidx(hdidx);
int FieldStore::index_headers() {
http2::init_hdidx(hdidx_);
content_length = -1;
for (size_t i = 0; i < headers.size(); ++i) {
auto &kv = headers[i];
for (size_t i = 0; i < headers_.size(); ++i) {
auto &kv = headers_[i];
util::inp_strlower(kv.name);
auto token = http2::lookup_token(
@ -407,7 +373,7 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
}
kv.token = token;
http2::index_header(hdidx, token, i);
http2::index_header(hdidx_, token, i);
if (token == http2::HD_CONTENT_LENGTH) {
auto len = util::parse_uint(kv.value);
@ -422,124 +388,76 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
}
return 0;
}
} // namespace
int Downstream::index_request_headers() {
return index_headers(request_hdidx_, request_headers_,
request_content_length_);
const Headers::value_type *FieldStore::header(int16_t token) const {
return http2::get_header(hdidx_, token, headers_);
}
const Headers::value_type *Downstream::get_request_header(int16_t token) const {
return http2::get_header(request_hdidx_, token, request_headers_);
Headers::value_type *FieldStore::header(int16_t token) {
return http2::get_header(hdidx_, token, headers_);
}
const Headers::value_type *
Downstream::get_request_header(const std::string &name) const {
return get_header_linear(request_headers_, name);
const Headers::value_type *FieldStore::header(const std::string &name) const {
return get_header_linear(headers_, name);
}
void Downstream::add_request_header(std::string name, std::string value) {
add_header(request_header_key_prev_, request_headers_sum_, request_headers_,
std::move(name), std::move(value));
void FieldStore::add_header(std::string name, std::string value) {
shrpx::add_header(header_key_prev_, buffer_size_, headers_, std::move(name),
std::move(value));
}
void Downstream::set_last_request_header_value(const char *data, size_t len) {
set_last_header_value(request_header_key_prev_, request_headers_sum_,
request_headers_, data, len);
void FieldStore::add_header(std::string name, std::string value,
int16_t token) {
http2::index_header(hdidx_, token, headers_.size());
buffer_size_ += name.size() + value.size();
headers_.emplace_back(std::move(name), std::move(value), false, token);
}
void Downstream::add_request_header(std::string name, std::string value,
int16_t token) {
http2::index_header(request_hdidx_, token, request_headers_.size());
request_headers_sum_ += name.size() + value.size();
request_headers_.emplace_back(std::move(name), std::move(value), false,
token);
void FieldStore::add_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
http2::index_header(hdidx_, token, headers_.size());
shrpx::add_header(buffer_size_, headers_, name, namelen, value, valuelen,
no_index, token);
}
void Downstream::add_request_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
http2::index_header(request_hdidx_, token, request_headers_.size());
add_header(request_headers_sum_, request_headers_, name, namelen, value,
valuelen, no_index, token);
void FieldStore::append_last_header_key(const char *data, size_t len) {
shrpx::append_last_header_key(header_key_prev_, buffer_size_, headers_, data,
len);
}
bool Downstream::get_request_header_key_prev() const {
return request_header_key_prev_;
void FieldStore::append_last_header_value(const char *data, size_t len) {
shrpx::append_last_header_value(header_key_prev_, buffer_size_, headers_,
data, len);
}
void Downstream::append_last_request_header_key(const char *data, size_t len) {
append_last_header_key(request_header_key_prev_, request_headers_sum_,
request_headers_, data, len);
void FieldStore::clear_headers() {
headers_.clear();
http2::init_hdidx(hdidx_);
}
void Downstream::append_last_request_header_value(const char *data,
size_t len) {
append_last_header_value(request_header_key_prev_, request_headers_sum_,
request_headers_, data, len);
void FieldStore::add_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
// we never index trailer fields. Header size limit should be
// applied to all header and trailer fields combined.
shrpx::add_header(buffer_size_, trailers_, name, namelen, value, valuelen,
no_index, -1);
}
void Downstream::clear_request_headers() {
Headers().swap(request_headers_);
http2::init_hdidx(request_hdidx_);
void FieldStore::add_trailer(std::string name, std::string value) {
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, std::move(name),
std::move(value));
}
size_t Downstream::get_request_headers_sum() const {
return request_headers_sum_;
void FieldStore::append_last_trailer_key(const char *data, size_t len) {
shrpx::append_last_header_key(trailer_key_prev_, buffer_size_, trailers_,
data, len);
}
void Downstream::add_request_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
// we never index trailer part. Header size limit should be applied
// to all request header fields combined.
add_header(request_headers_sum_, request_trailers_, name, namelen, value,
valuelen, no_index, -1);
}
const Headers &Downstream::get_request_trailers() const {
return request_trailers_;
}
void Downstream::add_request_trailer(std::string name, std::string value) {
add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_,
std::move(name), std::move(value));
}
void Downstream::set_last_request_trailer_value(const char *data, size_t len) {
set_last_header_value(request_trailer_key_prev_, request_headers_sum_,
request_trailers_, data, len);
}
bool Downstream::get_request_trailer_key_prev() const {
return request_trailer_key_prev_;
}
void Downstream::append_last_request_trailer_key(const char *data, size_t len) {
append_last_header_key(request_trailer_key_prev_, request_headers_sum_,
request_trailers_, data, len);
}
void Downstream::append_last_request_trailer_value(const char *data,
size_t len) {
append_last_header_value(request_trailer_key_prev_, request_headers_sum_,
request_trailers_, data, len);
}
void Downstream::set_request_method(int method) { request_method_ = method; }
int Downstream::get_request_method() const { return request_method_; }
void Downstream::set_request_path(std::string path) {
request_path_ = std::move(path);
}
void Downstream::append_request_path(const char *data, size_t len) {
request_path_.append(data, len);
}
const std::string &Downstream::get_request_path() const {
return request_path_;
void FieldStore::append_last_trailer_value(const char *data, size_t len) {
shrpx::append_last_header_value(trailer_key_prev_, buffer_size_, trailers_,
data, len);
}
void Downstream::set_request_start_time(
@ -552,34 +470,6 @@ Downstream::get_request_start_time() const {
return request_start_time_;
}
const std::string &Downstream::get_request_http2_scheme() const {
return request_http2_scheme_;
}
void Downstream::set_request_http2_scheme(std::string scheme) {
request_http2_scheme_ = std::move(scheme);
}
const std::string &Downstream::get_request_http2_authority() const {
return request_http2_authority_;
}
void Downstream::set_request_http2_authority(std::string authority) {
request_http2_authority_ = std::move(authority);
}
void Downstream::append_request_http2_authority(const char *data, size_t len) {
request_http2_authority_.append(data, len);
}
void Downstream::set_request_major(int major) { request_major_ = major; }
void Downstream::set_request_minor(int minor) { request_minor_ = minor; }
int Downstream::get_request_major() const { return request_major_; }
int Downstream::get_request_minor() const { return request_minor_; }
void Downstream::reset_upstream(Upstream *upstream) {
upstream_ = upstream;
if (dconn_) {
@ -601,22 +491,6 @@ bool Downstream::get_chunked_request() const { return chunked_request_; }
void Downstream::set_chunked_request(bool f) { chunked_request_ = f; }
bool Downstream::get_request_connection_close() const {
return request_connection_close_;
}
void Downstream::set_request_connection_close(bool f) {
request_connection_close_ = f;
}
bool Downstream::get_request_http2_expect_body() const {
return request_http2_expect_body_;
}
void Downstream::set_request_http2_expect_body(bool f) {
request_http2_expect_body_ = f;
}
bool Downstream::request_buf_full() {
if (dconn_) {
return request_buf_.rleft() >= get_config()->downstream_request_buffer_size;
@ -662,30 +536,9 @@ int Downstream::end_upload_data() {
return dconn_->end_upload_data();
}
const Headers &Downstream::get_response_headers() const {
return response_headers_;
}
Headers &Downstream::get_response_headers() { return response_headers_; }
int Downstream::index_response_headers() {
return index_headers(response_hdidx_, response_headers_,
response_content_length_);
}
const Headers::value_type *
Downstream::get_response_header(int16_t token) const {
return http2::get_header(response_hdidx_, token, response_headers_);
}
Headers::value_type *Downstream::get_response_header(int16_t token) {
return http2::get_header(response_hdidx_, token, response_headers_);
}
void Downstream::rewrite_location_response_header(
const std::string &upstream_scheme) {
auto hd =
http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_);
auto hd = resp_.fs.header(http2::HD_LOCATION);
if (!hd) {
return;
}
@ -696,14 +549,13 @@ void Downstream::rewrite_location_response_header(
return;
}
std::string new_uri;
if (get_config()->no_host_rewrite || request_method_ == HTTP_CONNECT) {
if (!request_http2_authority_.empty()) {
new_uri = http2::rewrite_location_uri(
(*hd).value, u, request_http2_authority_, request_http2_authority_,
upstream_scheme);
if (get_config()->no_host_rewrite || req_.method == HTTP_CONNECT) {
if (!req_.authority.empty()) {
new_uri = http2::rewrite_location_uri((*hd).value, u, req_.authority,
req_.authority, upstream_scheme);
}
if (new_uri.empty()) {
auto host = get_request_header(http2::HD_HOST);
auto host = req_.fs.header(http2::HD_HOST);
if (host) {
new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value,
(*host).value, upstream_scheme);
@ -718,12 +570,12 @@ void Downstream::rewrite_location_response_header(
if (request_downstream_host_.empty()) {
return;
}
if (!request_http2_authority_.empty()) {
new_uri = http2::rewrite_location_uri(
(*hd).value, u, request_downstream_host_, request_http2_authority_,
upstream_scheme);
if (!req_.authority.empty()) {
new_uri =
http2::rewrite_location_uri((*hd).value, u, request_downstream_host_,
req_.authority, upstream_scheme);
} else {
auto host = get_request_header(http2::HD_HOST);
auto host = req_.fs.header(http2::HD_HOST);
if (host) {
new_uri = http2::rewrite_location_uri((*hd).value, u,
request_downstream_host_,
@ -734,131 +586,18 @@ void Downstream::rewrite_location_response_header(
}
}
}
if (!new_uri.empty()) {
auto idx = response_hdidx_[http2::HD_LOCATION];
response_headers_[idx].value = std::move(new_uri);
if (new_uri.empty()) {
return;
}
}
void Downstream::add_response_header(std::string name, std::string value) {
add_header(response_header_key_prev_, response_headers_sum_,
response_headers_, std::move(name), std::move(value));
}
void Downstream::set_last_response_header_value(const char *data, size_t len) {
set_last_header_value(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::add_response_header(std::string name, std::string value,
int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
response_headers_sum_ += name.size() + value.size();
response_headers_.emplace_back(std::move(name), std::move(value), false,
token);
}
void Downstream::add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
add_header(response_headers_sum_, response_headers_, name, namelen, value,
valuelen, no_index, token);
}
bool Downstream::get_response_header_key_prev() const {
return response_header_key_prev_;
}
void Downstream::append_last_response_header_key(const char *data, size_t len) {
append_last_header_key(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::append_last_response_header_value(const char *data,
size_t len) {
append_last_header_value(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::clear_response_headers() {
Headers().swap(response_headers_);
http2::init_hdidx(response_hdidx_);
}
size_t Downstream::get_response_headers_sum() const {
return response_headers_sum_;
}
const Headers &Downstream::get_response_trailers() const {
return response_trailers_;
}
void Downstream::add_response_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
add_header(response_headers_sum_, response_trailers_, name, namelen, value,
valuelen, no_index, -1);
}
unsigned int Downstream::get_response_http_status() const {
return response_http_status_;
}
void Downstream::add_response_trailer(std::string name, std::string value) {
add_header(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, std::move(name), std::move(value));
}
void Downstream::set_last_response_trailer_value(const char *data, size_t len) {
set_last_header_value(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
bool Downstream::get_response_trailer_key_prev() const {
return response_trailer_key_prev_;
}
void Downstream::append_last_response_trailer_key(const char *data,
size_t len) {
append_last_header_key(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
void Downstream::append_last_response_trailer_value(const char *data,
size_t len) {
append_last_header_value(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
void Downstream::set_response_http_status(unsigned int status) {
response_http_status_ = status;
}
void Downstream::set_response_major(int major) { response_major_ = major; }
void Downstream::set_response_minor(int minor) { response_minor_ = minor; }
int Downstream::get_response_major() const { return response_major_; }
int Downstream::get_response_minor() const { return response_minor_; }
int Downstream::get_response_version() const {
return response_major_ * 100 + response_minor_;
(*hd).value = std::move(new_uri);
}
bool Downstream::get_chunked_response() const { return chunked_response_; }
void Downstream::set_chunked_response(bool f) { chunked_response_ = f; }
bool Downstream::get_response_connection_close() const {
return response_connection_close_;
}
void Downstream::set_response_connection_close(bool f) {
response_connection_close_ = f;
}
int Downstream::on_read() {
if (!dconn_) {
DLOG(INFO, this) << "dconn_ is NULL";
@ -904,31 +643,15 @@ int64_t Downstream::get_response_sent_bodylen() const {
return response_sent_bodylen_;
}
int64_t Downstream::get_response_content_length() const {
return response_content_length_;
}
void Downstream::set_response_content_length(int64_t len) {
response_content_length_ = len;
}
int64_t Downstream::get_request_content_length() const {
return request_content_length_;
}
void Downstream::set_request_content_length(int64_t len) {
request_content_length_ = len;
}
bool Downstream::validate_request_bodylen() const {
if (request_content_length_ == -1) {
if (req_.fs.content_length == -1) {
return true;
}
if (request_content_length_ != request_bodylen_) {
if (req_.fs.content_length != request_bodylen_) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "request invalid bodylen: content-length="
<< request_content_length_
<< req_.fs.content_length
<< ", received=" << request_bodylen_;
}
return false;
@ -938,14 +661,14 @@ bool Downstream::validate_request_bodylen() const {
}
bool Downstream::validate_response_bodylen() const {
if (!expect_response_body() || response_content_length_ == -1) {
if (!expect_response_body() || resp_.fs.content_length == -1) {
return true;
}
if (response_content_length_ != response_bodylen_) {
if (resp_.fs.content_length != response_bodylen_) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "response invalid bodylen: content-length="
<< response_content_length_
<< resp_.fs.content_length
<< ", received=" << response_bodylen_;
}
return false;
@ -959,80 +682,77 @@ void Downstream::set_priority(int32_t pri) { priority_ = pri; }
int32_t Downstream::get_priority() const { return priority_; }
void Downstream::check_upgrade_fulfilled() {
if (request_method_ == HTTP_CONNECT) {
upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300;
if (req_.method == HTTP_CONNECT) {
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
return;
}
if (response_http_status_ == 101) {
if (resp_.http_status == 101) {
// TODO Do more strict checking for upgrade headers
upgraded_ = upgrade_request_;
upgraded_ = req_.upgrade_request;
return;
}
}
void Downstream::inspect_http2_request() {
if (request_method_ == HTTP_CONNECT) {
upgrade_request_ = true;
if (req_.method == HTTP_CONNECT) {
req_.upgrade_request = true;
}
}
void Downstream::inspect_http1_request() {
if (request_method_ == HTTP_CONNECT) {
upgrade_request_ = true;
if (req_.method == HTTP_CONNECT) {
req_.upgrade_request = true;
}
if (!upgrade_request_) {
auto idx = request_hdidx_[http2::HD_UPGRADE];
if (idx != -1) {
auto &val = request_headers_[idx].value;
if (!req_.upgrade_request) {
auto upgrade = req_.fs.header(http2::HD_UPGRADE);
if (upgrade) {
const auto &val = upgrade->value;
// TODO Perform more strict checking for upgrade headers
if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(),
val.size())) {
http2_upgrade_seen_ = true;
req_.http2_upgrade_seen = true;
} else {
upgrade_request_ = true;
req_.upgrade_request = true;
}
}
}
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING];
if (idx != -1) {
request_content_length_ = -1;
if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
auto transfer_encoding = req_.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding) {
req_.fs.content_length = -1;
if (util::strifind(transfer_encoding->value.c_str(), "chunked")) {
chunked_request_ = true;
}
}
}
void Downstream::inspect_http1_response() {
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];
if (idx != -1) {
response_content_length_ = -1;
if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) {
auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding) {
resp_.fs.content_length = -1;
if (util::strifind(transfer_encoding->value.c_str(), "chunked")) {
chunked_response_ = true;
}
}
}
void Downstream::reset_response() {
response_http_status_ = 0;
response_major_ = 1;
response_minor_ = 1;
resp_.http_status = 0;
resp_.http_major = 1;
resp_.http_minor = 1;
}
bool Downstream::get_non_final_response() const {
return !upgraded_ && response_http_status_ / 100 == 1;
return !upgraded_ && resp_.http_status / 100 == 1;
}
bool Downstream::get_upgraded() const { return upgraded_; }
bool Downstream::get_upgrade_request() const { return upgrade_request_; }
bool Downstream::get_http2_upgrade_request() const {
return http2_upgrade_seen_ &&
request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1 &&
return req_.http2_upgrade_seen && req_.fs.header(http2::HD_HTTP2_SETTINGS) &&
response_state_ == INITIAL;
}
@ -1041,11 +761,11 @@ const std::string EMPTY;
} // namespace
const std::string &Downstream::get_http2_settings() const {
auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS];
if (idx == -1) {
auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS);
if (!http2_settings) {
return EMPTY;
}
return request_headers_[idx].value;
return http2_settings->value;
}
void Downstream::set_downstream_stream_id(int32_t stream_id) {
@ -1093,31 +813,7 @@ size_t Downstream::get_response_datalen() const { return response_datalen_; }
void Downstream::reset_response_datalen() { response_datalen_ = 0; }
bool Downstream::expect_response_body() const {
return http2::expect_response_body(request_method_, response_http_status_);
}
namespace {
bool pseudo_header_allowed(const Headers &headers) {
if (headers.empty()) {
return true;
}
return headers.back().name.c_str()[0] == ':';
}
} // namespace
bool Downstream::request_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(request_headers_)) {
return false;
}
return http2::check_http2_request_pseudo_header(request_hdidx_, token);
}
bool Downstream::response_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(response_headers_)) {
return false;
}
return http2::check_http2_response_pseudo_header(response_hdidx_, token);
return http2::expect_response_body(req_.method, resp_.http_status);
}
namespace {
@ -1232,7 +928,7 @@ void Downstream::disable_downstream_wtimer() {
disable_timer(loop, &downstream_wtimer_);
}
bool Downstream::accesslog_ready() const { return response_http_status_ > 0; }
bool Downstream::accesslog_ready() const { return resp_.http_status > 0; }
void Downstream::add_retry() { ++num_retry_; }
@ -1269,14 +965,10 @@ BlockedLink *Downstream::detach_blocked_link() {
return link;
}
void Downstream::add_request_headers_sum(size_t amount) {
request_headers_sum_ += amount;
}
bool Downstream::can_detach_downstream_connection() const {
return dconn_ && response_state_ == Downstream::MSG_COMPLETE &&
request_state_ == Downstream::MSG_COMPLETE && !upgraded_ &&
!response_connection_close_;
!resp_.connection_close;
}
DefaultMemchunks Downstream::pop_response_buf() {

View File

@ -49,6 +49,126 @@ class Upstream;
class DownstreamConnection;
struct BlockedLink;
class FieldStore {
public:
FieldStore(size_t headers_initial_capacity)
: content_length(-1), buffer_size_(0), header_key_prev_(false),
trailer_key_prev_(false) {
http2::init_hdidx(hdidx_);
headers_.reserve(headers_initial_capacity);
}
const Headers &headers() const { return headers_; }
const Headers &trailers() const { return trailers_; }
Headers &headers() { return headers_; }
const void add_extra_buffer_size(size_t n) { buffer_size_ += n; }
size_t buffer_size() const { return buffer_size_; }
size_t num_fields() const { return headers_.size() + trailers_.size(); }
// Returns pointer to the header field with the name |name|. If
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.
// This function must be called after headers are indexed
const Headers::value_type *header(int16_t token) const;
Headers::value_type *header(int16_t token);
// Returns pointer to the header field with the name |name|. If no
// such header is found, returns nullptr.
const Headers::value_type *header(const std::string &name) const;
void add_header(std::string name, std::string value);
void add_header(std::string name, std::string value, int16_t token);
void add_header(const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen, bool no_index, int16_t token);
void append_last_header_key(const char *data, size_t len);
void append_last_header_value(const char *data, size_t len);
bool header_key_prev() const { return header_key_prev_; }
// Lower the header field names and indexes header fields. If there
// is any invalid headers (e.g., multiple Content-Length having
// different values), returns -1.
int index_headers();
// Empties headers.
void clear_headers();
void add_trailer(const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen, bool no_index, int16_t token);
void add_trailer(std::string name, std::string value);
void append_last_trailer_key(const char *data, size_t len);
void append_last_trailer_value(const char *data, size_t len);
bool trailer_key_prev() const { return trailer_key_prev_; }
// content-length, -1 if it is unknown.
int64_t content_length;
private:
Headers headers_;
// trailer fields. For HTTP/1.1, trailer fields are only included
// with chunked encoding. For HTTP/2, there is no such limit.
Headers trailers_;
http2::HeaderIndex hdidx_;
// Sum of the length of name and value in headers_ and trailers_.
// This could also be increased by add_extra_buffer_size() to take
// into account for request URI in case of HTTP/1.x request.
size_t buffer_size_;
bool header_key_prev_;
bool trailer_key_prev_;
};
struct Request {
Request()
: fs(16), method(-1), http_major(1), http_minor(1),
upgrade_request(false), http2_upgrade_seen(false),
connection_close(false), http2_expect_body(false) {}
FieldStore fs;
// Request scheme. For HTTP/2, this is :scheme header field value.
// For HTTP/1.1, this is deduced from URI or connection.
std::string scheme;
// Request authority. This is HTTP/2 :authority header field value
// or host header field value. We may deduce it from absolute-form
// HTTP/1 request. We also store authority-form HTTP/1 request.
// This could be empty if request comes from HTTP/1.0 without Host
// header field and origin-form.
std::string authority;
// Request path, including query component. For HTTP/1.1, this is
// request-target. For HTTP/2, this is :path header field value.
// For CONNECT request, this is empty.
std::string path;
int method;
// HTTP major and minor version
int http_major, http_minor;
// Returns true if the request is HTTP upgrade (HTTP Upgrade or
// CONNECT method). Upgrade to HTTP/2 is excluded. For HTTP/2
// Upgrade, check get_http2_upgrade_request().
bool upgrade_request;
// true if h2c is seen in Upgrade header field.
bool http2_upgrade_seen;
bool connection_close;
// true if this is HTTP/2, and request body is expected. Note that
// we don't take into account HTTP method here.
bool http2_expect_body;
};
struct Response {
Response()
: fs(32), http_status(0), http_major(1), http_minor(1),
connection_close(false) {}
FieldStore fs;
// HTTP status code
unsigned int http_status;
int http_major, http_minor;
bool connection_close;
};
class Downstream {
public:
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id,
@ -79,9 +199,6 @@ public:
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
// This should not depend on inspect_http1_response().
void check_upgrade_fulfilled();
// Returns true if the request is upgrade. Upgrade to HTTP/2 is
// excluded. For HTTP/2 Upgrade, check get_http2_upgrade_request().
bool get_upgrade_request() const;
// Returns true if the upgrade is succeded as a result of the call
// check_upgrade_fulfilled(). HTTP/2 Upgrade is excluded.
bool get_upgraded() const;
@ -94,88 +211,27 @@ public:
bool get_http2_upgrade_request() const;
// Returns the value of HTTP2-Settings request header field.
const std::string &get_http2_settings() const;
// downstream request API
const Headers &get_request_headers() const;
Headers &get_request_headers();
const Request &request() const { return req_; }
Request &request() { return req_; }
// Count number of crumbled cookies
size_t count_crumble_request_cookie();
// Crumbles (split cookie by ";") in request_headers_ and adds them
// in |nva|. Headers::no_index is inherited.
void crumble_request_cookie(std::vector<nghttp2_nv> &nva);
void assemble_request_cookie();
const std::string &get_assembled_request_cookie() const;
// Lower the request header field names and indexes request headers.
// If there is any invalid headers (e.g., multiple Content-Length
// having different values), returns -1.
int index_request_headers();
// Returns pointer to the request header with the name |name|. If
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.
// This function must be called after headers are indexed
const Headers::value_type *get_request_header(int16_t token) const;
// Returns pointer to the request header with the name |name|. If
// no such header is found, returns nullptr.
const Headers::value_type *get_request_header(const std::string &name) const;
void add_request_header(std::string name, std::string value);
void set_last_request_header_value(const char *data, size_t len);
// Assembles request cookies. The opposite operation against
// crumble_request_cookie().
std::string assemble_request_cookie() const;
void add_request_header(std::string name, std::string value, int16_t token);
void add_request_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token);
bool get_request_header_key_prev() const;
void append_last_request_header_key(const char *data, size_t len);
void append_last_request_header_value(const char *data, size_t len);
// Empties request headers.
void clear_request_headers();
size_t get_request_headers_sum() const;
const Headers &get_request_trailers() const;
void add_request_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token);
void add_request_trailer(std::string name, std::string value);
void set_last_request_trailer_value(const char *data, size_t len);
bool get_request_trailer_key_prev() const;
void append_last_request_trailer_key(const char *data, size_t len);
void append_last_request_trailer_value(const char *data, size_t len);
void set_request_method(int method);
int get_request_method() const;
void set_request_path(std::string path);
void add_request_headers_sum(size_t amount);
void
set_request_start_time(std::chrono::high_resolution_clock::time_point time);
const std::chrono::high_resolution_clock::time_point &
get_request_start_time() const;
void append_request_path(const char *data, size_t len);
// Returns request path. For HTTP/1.1, this is request-target. For
// HTTP/2, this is :path header field value. For CONNECT request,
// this is empty.
const std::string &get_request_path() const;
// Returns HTTP/2 :scheme header field value.
const std::string &get_request_http2_scheme() const;
void set_request_http2_scheme(std::string scheme);
// Returns :authority or host header field value. We may deduce it
// from absolute-form HTTP/1 request. We also store authority-form
// HTTP/1 request. This could be empty if request comes from
// HTTP/1.0 without Host header field and origin-form.
const std::string &get_request_http2_authority() const;
void set_request_http2_authority(std::string authority);
void append_request_http2_authority(const char *data, size_t len);
void set_request_major(int major);
void set_request_minor(int minor);
int get_request_major() const;
int get_request_minor() const;
int push_request_headers();
bool get_chunked_request() const;
void set_chunked_request(bool f);
bool get_request_connection_close() const;
void set_request_connection_close(bool f);
bool get_request_http2_expect_body() const;
void set_request_http2_expect_body(bool f);
int push_upload_data_chunk(const uint8_t *data, size_t datalen);
int end_upload_data();
size_t get_request_datalen() const;
@ -184,9 +240,6 @@ public:
// Validates that received request body length and content-length
// matches.
bool validate_request_bodylen() const;
int64_t get_request_content_length() const;
void set_request_content_length(int64_t len);
bool request_pseudo_header_allowed(int16_t token) const;
void set_request_downstream_host(std::string host);
bool expect_response_body() const;
enum {
@ -212,58 +265,17 @@ public:
bool get_request_pending() const;
// Returns true if request is ready to be submitted to downstream.
bool request_submission_ready() const;
// downstream response API
const Headers &get_response_headers() const;
Headers &get_response_headers();
// Lower the response header field names and indexes response
// headers. If there are invalid headers (e.g., multiple
// Content-Length with different values), returns -1.
int index_response_headers();
// Returns pointer to the response header with the name |name|. If
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.
// This function must be called after response headers are indexed.
const Headers::value_type *get_response_header(int16_t token) const;
Headers::value_type *get_response_header(int16_t token);
const Response &response() const { return resp_; }
Response &response() { return resp_; }
// Rewrites the location response header field.
void rewrite_location_response_header(const std::string &upstream_scheme);
void add_response_header(std::string name, std::string value);
void set_last_response_header_value(const char *data, size_t len);
void add_response_header(std::string name, std::string value, int16_t token);
void add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token);
bool get_response_header_key_prev() const;
void append_last_response_header_key(const char *data, size_t len);
void append_last_response_header_value(const char *data, size_t len);
// Empties response headers.
void clear_response_headers();
size_t get_response_headers_sum() const;
const Headers &get_response_trailers() const;
void add_response_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token);
void add_response_trailer(std::string name, std::string value);
void set_last_response_trailer_value(const char *data, size_t len);
bool get_response_trailer_key_prev() const;
void append_last_response_trailer_key(const char *data, size_t len);
void append_last_response_trailer_value(const char *data, size_t len);
unsigned int get_response_http_status() const;
void set_response_http_status(unsigned int status);
void set_response_major(int major);
void set_response_minor(int minor);
int get_response_major() const;
int get_response_minor() const;
int get_response_version() const;
bool get_chunked_response() const;
void set_chunked_response(bool f);
bool get_response_connection_close() const;
void set_response_connection_close(bool f);
void set_response_state(int state);
int get_response_state() const;
DefaultMemchunks *get_response_buf();
@ -272,8 +284,6 @@ public:
int64_t get_response_bodylen() const;
void add_response_sent_bodylen(size_t amount);
int64_t get_response_sent_bodylen() const;
int64_t get_response_content_length() const;
void set_response_content_length(int64_t len);
// Validates that received response body length and content-length
// matches.
bool validate_response_bodylen() const;
@ -292,7 +302,6 @@ public:
void dec_response_datalen(size_t len);
size_t get_response_datalen() const;
void reset_response_datalen();
bool response_pseudo_header_allowed(int16_t token) const;
// Call this method when there is incoming data in downstream
// connection.
@ -362,24 +371,15 @@ public:
Downstream *dlnext, *dlprev;
private:
Headers request_headers_;
Headers response_headers_;
// trailer part. For HTTP/1.1, trailer part is only included with
// chunked encoding. For HTTP/2, there is no such limit.
Headers request_trailers_;
Headers response_trailers_;
Request req_;
Response resp_;
std::chrono::high_resolution_clock::time_point request_start_time_;
std::string request_path_;
std::string request_http2_scheme_;
std::string request_http2_authority_;
// host we requested to downstream. This is used to rewrite
// location header field to decide the location should be rewritten
// or not.
std::string request_downstream_host_;
std::string assembled_request_cookie_;
DefaultMemchunks request_buf_;
DefaultMemchunks response_buf_;
@ -398,20 +398,12 @@ private:
// the length of response body sent to upstream client
int64_t response_sent_bodylen_;
// content-length of request body, -1 if it is unknown.
int64_t request_content_length_;
// content-length of response body, -1 if it is unknown.
int64_t response_content_length_;
Upstream *upstream_;
std::unique_ptr<DownstreamConnection> dconn_;
// only used by HTTP/2 or SPDY upstream
BlockedLink *blocked_link_;
size_t request_headers_sum_;
size_t response_headers_sum_;
// The number of bytes not consumed by the application yet.
size_t request_datalen_;
size_t response_datalen_;
@ -426,40 +418,18 @@ private:
// RST_STREAM error_code from downstream HTTP2 connection
uint32_t response_rst_stream_error_code_;
int request_method_;
int request_state_;
int request_major_;
int request_minor_;
int response_state_;
unsigned int response_http_status_;
int response_major_;
int response_minor_;
// only used by HTTP/2 or SPDY upstream
int dispatch_state_;
http2::HeaderIndex request_hdidx_;
http2::HeaderIndex response_hdidx_;
// true if the request contains upgrade token (HTTP Upgrade or
// CONNECT)
bool upgrade_request_;
// true if the connection is upgraded (HTTP Upgrade or CONNECT)
bool upgraded_;
bool http2_upgrade_seen_;
bool chunked_request_;
bool request_connection_close_;
bool request_header_key_prev_;
bool request_trailer_key_prev_;
bool request_http2_expect_body_;
bool chunked_response_;
bool response_connection_close_;
bool response_header_key_prev_;
bool response_trailer_key_prev_;
bool expect_final_response_;
// true if downstream request is pending because backend connection
// has not been established or should be checked before use;

View File

@ -79,7 +79,7 @@ DownstreamQueue::make_host_key(const std::string &host) const {
const std::string &
DownstreamQueue::make_host_key(Downstream *downstream) const {
return make_host_key(downstream->get_request_http2_authority());
return make_host_key(downstream->request().authority);
}
void DownstreamQueue::mark_active(Downstream *downstream) {

View File

@ -32,17 +32,17 @@
namespace shrpx {
void test_downstream_index_request_headers(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header("1", "0");
d.add_request_header("2", "1");
d.add_request_header("Charlie", "2");
d.add_request_header("Alpha", "3");
d.add_request_header("Delta", "4");
d.add_request_header("BravO", "5");
d.add_request_header(":method", "6");
d.add_request_header(":authority", "7");
d.index_request_headers();
void test_downstream_field_store_index_headers(void) {
FieldStore fs(0);
fs.add_header("1", "0");
fs.add_header("2", "1");
fs.add_header("Charlie", "2");
fs.add_header("Alpha", "3");
fs.add_header("Delta", "4");
fs.add_header("BravO", "5");
fs.add_header(":method", "6");
fs.add_header(":authority", "7");
fs.index_headers();
auto ans = Headers{{"1", "0"},
{"2", "1"},
@ -52,62 +52,36 @@ void test_downstream_index_request_headers(void) {
{"bravo", "5"},
{":method", "6"},
{":authority", "7"}};
CU_ASSERT(ans == d.get_request_headers());
CU_ASSERT(ans == fs.headers());
}
void test_downstream_index_response_headers(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_response_header("Charlie", "0");
d.add_response_header("Alpha", "1");
d.add_response_header("Delta", "2");
d.add_response_header("BravO", "3");
d.index_response_headers();
auto ans =
Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}};
CU_ASSERT(ans == d.get_response_headers());
}
void test_downstream_get_request_header(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header("alpha", "0");
d.add_request_header(":authority", "1");
d.add_request_header("content-length", "2");
d.index_request_headers();
void test_downstream_field_store_header(void) {
FieldStore fs(0);
fs.add_header("alpha", "0");
fs.add_header(":authority", "1");
fs.add_header("content-length", "2");
fs.index_headers();
// By token
CU_ASSERT(Header(":authority", "1") ==
*d.get_request_header(http2::HD__AUTHORITY));
CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD));
CU_ASSERT(Header(":authority", "1") == *fs.header(http2::HD__AUTHORITY));
CU_ASSERT(nullptr == fs.header(http2::HD__METHOD));
// By name
CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha"));
CU_ASSERT(nullptr == d.get_request_header("bravo"));
}
void test_downstream_get_response_header(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_response_header("alpha", "0");
d.add_response_header(":status", "1");
d.add_response_header("content-length", "2");
d.index_response_headers();
// By token
CU_ASSERT(Header(":status", "1") ==
*d.get_response_header(http2::HD__STATUS));
CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD));
CU_ASSERT(Header("alpha", "0") == *fs.header("alpha"));
CU_ASSERT(nullptr == fs.header("bravo"));
}
void test_downstream_crumble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header(":method", "get");
d.add_request_header(":path", "/");
auto &req = d.request();
req.fs.add_header(":method", "get");
req.fs.add_header(":path", "/");
auto val = "alpha; bravo; ; ;; charlie;;";
d.add_request_header(
req.fs.add_header(
reinterpret_cast<const uint8_t *>("cookie"), sizeof("cookie") - 1,
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1);
d.add_request_header("cookie", ";delta");
d.add_request_header("cookie", "echo");
req.fs.add_header("cookie", ";delta");
req.fs.add_header("cookie", "echo");
std::vector<nghttp2_nv> nva;
d.crumble_request_cookie(nva);
@ -139,36 +113,40 @@ void test_downstream_crumble_request_cookie(void) {
void test_downstream_assemble_request_cookie(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_request_header(":method", "get");
d.add_request_header(":path", "/");
d.add_request_header("cookie", "alpha");
d.add_request_header("cookie", "bravo;");
d.add_request_header("cookie", "charlie; ");
d.add_request_header("cookie", "delta;;");
d.assemble_request_cookie();
CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie());
auto &req = d.request();
req.fs.add_header(":method", "get");
req.fs.add_header(":path", "/");
req.fs.add_header("cookie", "alpha");
req.fs.add_header("cookie", "bravo;");
req.fs.add_header("cookie", "charlie; ");
req.fs.add_header("cookie", "delta;;");
CU_ASSERT("alpha; bravo; charlie; delta" == d.assemble_request_cookie());
}
void test_downstream_rewrite_location_response_header(void) {
{
Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
auto &resp = d.response();
d.set_request_downstream_host("localhost:3000");
d.add_request_header("host", "localhost");
d.add_response_header("location", "http://localhost:3000/");
d.index_request_headers();
d.index_response_headers();
req.fs.add_header("host", "localhost");
resp.fs.add_header("location", "http://localhost:3000/");
req.fs.index_headers();
resp.fs.index_headers();
d.rewrite_location_response_header("https");
auto location = d.get_response_header(http2::HD_LOCATION);
auto location = resp.fs.header(http2::HD_LOCATION);
CU_ASSERT("https://localhost/" == (*location).value);
}
{
Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
auto &resp = d.response();
d.set_request_downstream_host("localhost");
d.set_request_http2_authority("localhost");
d.add_response_header("location", "http://localhost:3000/");
d.index_response_headers();
req.authority = "localhost";
resp.fs.add_header("location", "http://localhost:3000/");
resp.fs.index_headers();
d.rewrite_location_response_header("https");
auto location = d.get_response_header(http2::HD_LOCATION);
auto location = resp.fs.header(http2::HD_LOCATION);
CU_ASSERT("https://localhost/" == (*location).value);
}
}

View File

@ -31,10 +31,8 @@
namespace shrpx {
void test_downstream_index_request_headers(void);
void test_downstream_index_response_headers(void);
void test_downstream_get_request_header(void);
void test_downstream_get_response_header(void);
void test_downstream_field_store_index_headers(void);
void test_downstream_field_store_header(void);
void test_downstream_crumble_request_cookie(void);
void test_downstream_assemble_request_cookie(void);
void test_downstream_rewrite_location_response_header(void);

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

View File

@ -752,6 +752,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0;
}
auto &resp = downstream->response();
switch (frame->hd.type) {
case NGHTTP2_HEADERS: {
auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
@ -759,15 +761,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (trailer) {
// just store header fields for trailer part
downstream->add_response_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
resp.fs.add_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
return 0;
}
auto token = http2::lookup_token(name, namelen);
downstream->add_response_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
resp.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
case NGHTTP2_PUSH_PROMISE: {
@ -783,10 +785,11 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
assert(promised_downstream);
auto &promised_req = promised_downstream->request();
auto token = http2::lookup_token(name, namelen);
promised_downstream->add_request_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX,
token);
promised_req.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
}
@ -855,18 +858,20 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
int rv;
auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
auto &resp = downstream->response();
auto &nva = downstream->get_response_headers();
auto &nva = resp.fs.headers();
downstream->set_expect_final_response(false);
auto status = downstream->get_response_header(http2::HD__STATUS);
auto status = resp.fs.header(http2::HD__STATUS);
// libnghttp2 guarantees this exists and can be parsed
auto status_code = http2::parse_http_status_code(status->value);
downstream->set_response_http_status(status_code);
downstream->set_response_major(2);
downstream->set_response_minor(0);
resp.http_status = status_code;
resp.http_major = 2;
resp.http_minor = 0;
if (LOG_ENABLED(INFO)) {
std::stringstream ss;
@ -902,7 +907,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
downstream->check_upgrade_fulfilled();
if (downstream->get_upgraded()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
// On upgrade sucess, both ends can send data
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
// If resume_read fails, just drop connection. Not ideal.
@ -915,29 +920,24 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
<< "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
}
} else {
auto content_length =
downstream->get_response_header(http2::HD_CONTENT_LENGTH);
auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH);
if (content_length) {
// libnghttp2 guarantees this can be parsed
auto len = util::parse_uint(content_length->value);
downstream->set_response_content_length(len);
resp.fs.content_length = util::parse_uint(content_length->value);
}
if (downstream->get_response_content_length() == -1 &&
downstream->expect_response_body()) {
if (resp.fs.content_length == -1 && downstream->expect_response_body()) {
// Here we have response body but Content-Length is not known in
// advance.
if (downstream->get_request_major() <= 0 ||
(downstream->get_request_major() == 1 &&
downstream->get_request_minor() == 0)) {
if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
// We simply close connection for pre-HTTP/1.1 in this case.
downstream->set_response_connection_close(true);
resp.connection_close = true;
} else {
// Otherwise, use chunked encoding to keep upstream connection
// open. In HTTP2, we are supporsed not to receive
// transfer-encoding.
downstream->add_response_header("transfer-encoding", "chunked",
http2::HD_TRANSFER_ENCODING);
resp.fs.add_header("transfer-encoding", "chunked",
http2::HD_TRANSFER_ENCODING);
downstream->set_chunked_response(true);
}
}
@ -1892,14 +1892,15 @@ int Http2Session::handle_downstream_push_promise(Downstream *downstream,
int Http2Session::handle_downstream_push_promise_complete(
Downstream *downstream, Downstream *promised_downstream) {
auto authority =
promised_downstream->get_request_header(http2::HD__AUTHORITY);
auto path = promised_downstream->get_request_header(http2::HD__PATH);
auto method = promised_downstream->get_request_header(http2::HD__METHOD);
auto scheme = promised_downstream->get_request_header(http2::HD__SCHEME);
auto &promised_req = promised_downstream->request();
auto authority = promised_req.fs.header(http2::HD__AUTHORITY);
auto path = promised_req.fs.header(http2::HD__PATH);
auto method = promised_req.fs.header(http2::HD__METHOD);
auto scheme = promised_req.fs.header(http2::HD__SCHEME);
if (!authority) {
authority = promised_downstream->get_request_header(http2::HD_HOST);
authority = promised_req.fs.header(http2::HD_HOST);
}
auto method_token = http2::lookup_method_token(method->value);
@ -1914,17 +1915,16 @@ int Http2Session::handle_downstream_push_promise_complete(
// TODO Rewrite authority if we enabled rewrite host. But we
// really don't know how to rewrite host. Should we use the same
// host in associated stream?
promised_downstream->set_request_http2_authority(
http2::value_to_str(authority));
promised_downstream->set_request_method(method_token);
promised_req.authority = http2::value_to_str(authority);
promised_req.method = method_token;
// libnghttp2 ensures that we don't have CONNECT method in
// PUSH_PROMISE, and guarantees that :scheme exists.
promised_downstream->set_request_http2_scheme(http2::value_to_str(scheme));
promised_req.scheme = http2::value_to_str(scheme);
// For server-wide OPTIONS request, path is empty.
if (method_token != HTTP_OPTIONS || path->value != "*") {
promised_downstream->set_request_path(http2::rewrite_clean_path(
std::begin(path->value), std::end(path->value)));
promised_req.path = http2::rewrite_clean_path(std::begin(path->value),
std::end(path->value));
}
promised_downstream->inspect_http2_request();

View File

@ -111,7 +111,7 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
rv = nghttp2_session_upgrade2(
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
settings_payload.size(),
http->get_downstream()->get_request_method() == HTTP_HEAD, nullptr);
http->get_downstream()->request().method == HTTP_HEAD, nullptr);
if (rv != 0) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: "
@ -167,20 +167,19 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0;
}
if (downstream->get_request_headers_sum() + namelen + valuelen >
auto &req = downstream->request();
if (req.fs.buffer_size() + namelen + valuelen >
get_config()->header_field_buffer ||
downstream->get_request_headers().size() +
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) {
req.fs.num_fields() >= get_config()->max_header_fields) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0;
}
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large or many header field size="
<< downstream->get_request_headers_sum() + namelen +
valuelen << ", num="
<< downstream->get_request_headers().size() + 1;
<< req.fs.buffer_size() + namelen + valuelen
<< ", num=" << req.fs.num_fields() + 1;
}
// just ignore header fields if this is trailer part.
@ -197,15 +196,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
// just store header fields for trailer part
downstream->add_request_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
req.fs.add_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
return 0;
}
auto token = http2::lookup_token(name, namelen);
downstream->add_request_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
req.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
} // namespace
@ -233,10 +232,12 @@ int on_begin_headers_callback(nghttp2_session *session,
downstream->reset_upstream_rtimer();
auto &req = downstream->request();
// Although, we deprecated minor version from HTTP/2, we supply
// minor version 0 to use via header field in a conventional way.
downstream->set_request_major(2);
downstream->set_request_minor(0);
req.http_major = 2;
req.http_minor = 0;
upstream->add_pending_downstream(std::move(downstream));
@ -250,7 +251,8 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
return 0;
}
auto &nva = downstream->get_request_headers();
auto &req = downstream->request();
auto &nva = req.fs.headers();
if (LOG_ENABLED(INFO)) {
std::stringstream ss;
@ -265,20 +267,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
}
auto content_length =
downstream->get_request_header(http2::HD_CONTENT_LENGTH);
auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH);
if (content_length) {
// libnghttp2 guarantees this can be parsed
auto len = util::parse_uint(content_length->value);
downstream->set_request_content_length(len);
req.fs.content_length = util::parse_uint(content_length->value);
}
auto authority = downstream->get_request_header(http2::HD__AUTHORITY);
auto path = downstream->get_request_header(http2::HD__PATH);
auto method = downstream->get_request_header(http2::HD__METHOD);
auto scheme = downstream->get_request_header(http2::HD__SCHEME);
// presence of mandatory header fields are guaranteed by libnghttp2.
auto authority = req.fs.header(http2::HD__AUTHORITY);
auto path = req.fs.header(http2::HD__PATH);
auto method = req.fs.header(http2::HD__METHOD);
auto scheme = req.fs.header(http2::HD__SCHEME);
auto method_token = http2::lookup_method_token(method->value);
if (method_token == -1) {
@ -294,28 +293,29 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
return 0;
}
downstream->set_request_method(method_token);
downstream->set_request_http2_scheme(http2::value_to_str(scheme));
req.method = method_token;
req.scheme = http2::value_to_str(scheme);
// nghttp2 library guarantees either :authority or host exist
if (!authority) {
authority = downstream->get_request_header(http2::HD_HOST);
authority = req.fs.header(http2::HD_HOST);
}
downstream->set_request_http2_authority(http2::value_to_str(authority));
req.authority = http2::value_to_str(authority);
if (path) {
if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty.
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(http2::value_to_str(path));
req.path = http2::value_to_str(path);
} else {
auto &value = path->value;
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(value), std::end(value)));
const auto &value = path->value;
req.path = http2::rewrite_clean_path(std::begin(value), std::end(value));
}
}
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
downstream->set_request_http2_expect_body(true);
req.http2_expect_body = true;
}
downstream->inspect_http2_request();
@ -352,8 +352,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
}
void Http2Upstream::start_downstream(Downstream *downstream) {
if (downstream_queue_.can_activate(
downstream->get_request_http2_authority())) {
if (downstream_queue_.can_activate(downstream->request().authority)) {
initiate_downstream(downstream);
return;
}
@ -553,6 +552,7 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
auto downstream = make_unique<Downstream>(upstream, handler->get_mcpool(),
promised_stream_id, 0);
auto &req = downstream->request();
// As long as we use nghttp2_session_mem_send(), setting stream
// user data here should not fail. This is because this callback
@ -563,33 +563,28 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
downstream->disable_upstream_rtimer();
downstream->set_request_major(2);
downstream->set_request_minor(0);
req.http_major = 2;
req.http_minor = 0;
for (size_t i = 0; i < frame->push_promise.nvlen; ++i) {
auto &nv = frame->push_promise.nva[i];
auto token = http2::lookup_token(nv.name, nv.namelen);
switch (token) {
case http2::HD__METHOD:
downstream->set_request_method(
http2::lookup_method_token(nv.value, nv.valuelen));
req.method = http2::lookup_method_token(nv.value, nv.valuelen);
break;
case http2::HD__SCHEME:
downstream->set_request_http2_scheme(
{nv.value, nv.value + nv.valuelen});
req.scheme.assign(nv.value, nv.value + nv.valuelen);
break;
case http2::HD__AUTHORITY:
downstream->set_request_http2_authority(
{nv.value, nv.value + nv.valuelen});
req.authority.assign(nv.value, nv.value + nv.valuelen);
break;
case http2::HD__PATH:
downstream->set_request_path(
http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen));
req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen);
break;
}
downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen,
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX,
token);
req.fs.add_header(nv.name, nv.namelen, nv.value, nv.valuelen,
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
}
downstream->inspect_http2_request();
@ -1247,6 +1242,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
assert(body);
auto dconn = downstream->get_downstream_connection();
const auto &resp = downstream->response();
if (body->rleft() == 0 && dconn &&
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
@ -1268,7 +1264,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
if (!downstream->get_upgraded()) {
auto &trailers = downstream->get_response_trailers();
const auto &trailers = resp.fs.trailers();
if (!trailers.empty()) {
std::vector<nghttp2_nv> nva;
nva.reserve(trailers.size());
@ -1308,18 +1304,19 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
data_prd_ptr = &data_prd;
}
auto &headers = downstream->get_response_headers();
const auto &resp = downstream->response();
const auto &headers = resp.fs.headers();
auto nva = std::vector<nghttp2_nv>();
// 2 for :status and server
nva.reserve(2 + headers.size());
std::string status_code_str;
auto response_status_const =
http2::stringify_status(downstream->get_response_http_status());
auto response_status_const = http2::stringify_status(resp.http_status);
if (response_status_const) {
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
} else {
status_code_str = util::utos(downstream->get_response_http_status());
status_code_str = util::utos(resp.http_status);
nva.push_back(http2::make_nv_ls(":status", status_code_str));
}
@ -1339,7 +1336,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
nva.push_back(
http2::make_nv_lc_nocopy("server", get_config()->server_name));
}
@ -1364,8 +1361,10 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
int Http2Upstream::error_reply(Downstream *downstream,
unsigned int status_code) {
int rv;
auto &resp = downstream->response();
auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
resp.http_status = status_code;
auto body = downstream->get_response_buf();
body->append(html.c_str(), html.size());
downstream->set_response_state(Downstream::MSG_COMPLETE);
@ -1433,6 +1432,9 @@ void Http2Upstream::remove_downstream(Downstream *downstream) {
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
int rv;
const auto &req = downstream->request();
auto &resp = downstream->response();
if (LOG_ENABLED(INFO)) {
if (downstream->get_non_final_response()) {
DLOG(INFO, downstream) << "HTTP non-final response header";
@ -1443,8 +1445,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!get_config()->no_location_rewrite) {
downstream->rewrite_location_response_header(
downstream->get_request_http2_scheme());
downstream->rewrite_location_response_header(req.scheme);
}
#ifdef HAVE_MRUBY
@ -1466,25 +1467,24 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
}
#endif // HAVE_MRUBY
size_t nheader = downstream->get_response_headers().size();
auto nva = std::vector<nghttp2_nv>();
// 4 means :status and possible server, via and x-http2-push header
// field.
nva.reserve(nheader + 4 + get_config()->add_response_headers.size());
nva.reserve(resp.fs.headers().size() + 4 +
get_config()->add_response_headers.size());
std::string via_value;
std::string response_status;
auto response_status_const =
http2::stringify_status(downstream->get_response_http_status());
auto response_status_const = http2::stringify_status(resp.http_status);
if (response_status_const) {
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
} else {
response_status = util::utos(downstream->get_response_http_status());
response_status = util::utos(resp.http_status);
nva.push_back(http2::make_nv_ls(":status", response_status));
}
if (downstream->get_non_final_response()) {
http2::copy_headers_to_nva(nva, downstream->get_response_headers());
http2::copy_headers_to_nva(nva, resp.fs.headers());
if (LOG_ENABLED(INFO)) {
log_response_headers(downstream, nva);
@ -1494,7 +1494,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
downstream->get_stream_id(), nullptr,
nva.data(), nva.size(), nullptr);
downstream->clear_response_headers();
resp.fs.clear_headers();
if (rv != 0) {
ULOG(FATAL, this) << "nghttp2_submit_headers() failed";
@ -1504,19 +1504,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return 0;
}
http2::copy_headers_to_nva_nocopy(nva, downstream->get_response_headers());
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
nva.push_back(
http2::make_nv_lc_nocopy("server", get_config()->server_name));
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
nva.push_back(http2::make_nv_ls_nocopy("server", (*server).value));
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
@ -1526,8 +1526,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
via_value = (*via).value;
via_value += ", ";
}
via_value += http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor());
via_value +=
http::create_via_header_value(resp.http_major, resp.http_minor);
nva.push_back(http2::make_nv_ls("via", via_value));
}
@ -1579,11 +1579,9 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
nghttp2_session_get_remote_settings(session_,
NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 &&
!get_config()->http2_proxy && !get_config()->client_proxy &&
(downstream->get_stream_id() % 2) &&
downstream->get_response_header(http2::HD_LINK) &&
downstream->get_response_http_status() == 200 &&
(downstream->get_request_method() == HTTP_GET ||
downstream->get_request_method() == HTTP_POST)) {
(downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) &&
resp.http_status == 200 &&
(req.method == HTTP_GET || req.method == HTTP_POST)) {
if (prepare_push_promise(downstream) != 0) {
// Continue to send response even if push was failed.
@ -1624,9 +1622,11 @@ int Http2Upstream::on_downstream_body_complete(Downstream *downstream) {
DLOG(INFO, downstream) << "HTTP response completed";
}
auto &resp = downstream->response();
if (!downstream->validate_response_bodylen()) {
rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_connection_close(true);
resp.connection_close = true;
return 0;
}
@ -1766,13 +1766,15 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
const char *base;
size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen,
downstream->get_request_path());
const auto &req = downstream->request();
const auto &resp = downstream->response();
rv = http2::get_pure_path_component(&base, &baselen, req.path);
if (rv != 0) {
return 0;
}
for (auto &kv : downstream->get_response_headers()) {
for (auto &kv : resp.fs.headers()) {
if (kv.token != http2::HD_LINK) {
continue;
}
@ -1782,6 +1784,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
auto uri = link.uri.first;
auto len = link.uri.second - link.uri.first;
const std::string *scheme_ptr, *authority_ptr;
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base,
@ -1791,14 +1794,18 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
}
if (scheme.empty()) {
scheme = downstream->get_request_http2_scheme();
scheme_ptr = &req.scheme;
} else {
scheme_ptr = &scheme;
}
if (authority.empty()) {
authority = downstream->get_request_http2_authority();
authority_ptr = &req.authority;
} else {
authority_ptr = &authority;
}
rv = submit_push_promise(scheme, authority, path, downstream);
rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream);
if (rv != 0) {
return -1;
}
@ -1811,9 +1818,11 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
const std::string &authority,
const std::string &path,
Downstream *downstream) {
const auto &req = downstream->request();
std::vector<nghttp2_nv> nva;
// 4 for :method, :scheme, :path and :authority
nva.reserve(4 + downstream->get_request_headers().size());
nva.reserve(4 + req.fs.headers().size());
// juse use "GET" for now
nva.push_back(http2::make_nv_ll(":method", "GET"));
@ -1821,7 +1830,7 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
nva.push_back(http2::make_nv_ls(":path", path));
nva.push_back(http2::make_nv_ls(":authority", authority));
for (auto &kv : downstream->get_request_headers()) {
for (auto &kv : req.fs.headers()) {
switch (kv.token) {
// TODO generate referer
case http2::HD__AUTHORITY:
@ -1884,12 +1893,14 @@ int Http2Upstream::initiate_push(Downstream *downstream, const char *uri,
const char *base;
size_t baselen;
rv = http2::get_pure_path_component(&base, &baselen,
downstream->get_request_path());
const auto &req = downstream->request();
rv = http2::get_pure_path_component(&base, &baselen, req.path);
if (rv != 0) {
return -1;
}
const std::string *scheme_ptr, *authority_ptr;
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, baselen,
@ -1899,14 +1910,18 @@ int Http2Upstream::initiate_push(Downstream *downstream, const char *uri,
}
if (scheme.empty()) {
scheme = downstream->get_request_http2_scheme();
scheme_ptr = &req.scheme;
} else {
scheme_ptr = &scheme;
}
if (authority.empty()) {
authority = downstream->get_request_http2_authority();
authority_ptr = &req.authority;
} else {
authority_ptr = &authority;
}
rv = submit_push_promise(scheme, authority, path, downstream);
rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream);
if (rv != 0) {
return -1;
@ -1939,12 +1954,14 @@ Http2Upstream::on_downstream_push_promise(Downstream *downstream,
// frontend.
auto promised_downstream =
make_unique<Downstream>(this, handler_->get_mcpool(), 0, 0);
auto &promised_req = promised_downstream->request();
promised_downstream->set_downstream_stream_id(promised_stream_id);
promised_downstream->disable_upstream_rtimer();
promised_downstream->set_request_major(2);
promised_downstream->set_request_minor(0);
promised_req.http_major = 2;
promised_req.http_minor = 0;
auto ptr = promised_downstream.get();
add_pending_downstream(std::move(promised_downstream));
@ -1957,7 +1974,8 @@ int Http2Upstream::on_downstream_push_promise_complete(
Downstream *downstream, Downstream *promised_downstream) {
std::vector<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());

View File

@ -54,9 +54,10 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
auto downstream = dconn->get_downstream();
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
auto &resp = downstream->response();
// Do this so that dconn is not pooled
downstream->set_response_connection_close(true);
resp.connection_close = true;
if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) {
delete handler;
@ -214,85 +215,80 @@ int HttpDownstreamConnection::push_request_headers() {
->downstream_addr_groups[group_]
.addrs[addr_idx_]
.hostport.get();
auto method = downstream_->get_request_method();
auto connect_method = method == HTTP_CONNECT;
const auto &req = downstream_->request();
auto connect_method = req.method == HTTP_CONNECT;
// For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless.
const char *authority = downstream_hostport;
auto &req_authority = downstream_->get_request_http2_authority();
auto no_host_rewrite = get_config()->no_host_rewrite ||
get_config()->http2_proxy ||
get_config()->client_proxy || connect_method;
if (no_host_rewrite && !req_authority.empty()) {
authority = req_authority.c_str();
if (no_host_rewrite && !req.authority.empty()) {
authority = req.authority.c_str();
}
auto authoritylen = strlen(authority);
downstream_->set_request_downstream_host(authority);
downstream_->assemble_request_cookie();
auto buf = downstream_->get_request_buf();
// Assume that method and request path do not contain \r\n.
auto meth = http2::to_method_string(method);
auto meth = http2::to_method_string(req.method);
buf->append(meth, strlen(meth));
buf->append(" ");
auto &scheme = downstream_->get_request_http2_scheme();
auto &path = downstream_->get_request_path();
if (connect_method) {
buf->append(authority, authoritylen);
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
// Construct absolute-form request target because we are going to
// send a request to a HTTP/1 proxy.
assert(!scheme.empty());
buf->append(scheme);
assert(!req.scheme.empty());
buf->append(req.scheme);
buf->append("://");
buf->append(authority, authoritylen);
buf->append(path);
} else if (method == HTTP_OPTIONS && path.empty()) {
buf->append(req.path);
} else if (req.method == HTTP_OPTIONS && req.path.empty()) {
// Server-wide OPTIONS
buf->append("*");
} else {
buf->append(path);
buf->append(req.path);
}
buf->append(" HTTP/1.1\r\nHost: ");
buf->append(authority, authoritylen);
buf->append("\r\n");
http2::build_http1_headers_from_headers(buf,
downstream_->get_request_headers());
http2::build_http1_headers_from_headers(buf, req.fs.headers());
if (!downstream_->get_assembled_request_cookie().empty()) {
auto cookie = downstream_->assemble_request_cookie();
if (!cookie.empty()) {
buf->append("Cookie: ");
buf->append(downstream_->get_assembled_request_cookie());
buf->append(cookie);
buf->append("\r\n");
}
if (!connect_method && downstream_->get_request_http2_expect_body() &&
!downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) {
if (!connect_method && req.http2_expect_body &&
!req.fs.header(http2::HD_CONTENT_LENGTH)) {
downstream_->set_chunked_request(true);
buf->append("Transfer-Encoding: chunked\r\n");
}
if (downstream_->get_request_connection_close()) {
if (req.connection_close) {
buf->append("Connection: close\r\n");
}
if (!connect_method && downstream_->get_upgrade_request()) {
auto connection = downstream_->get_request_header(http2::HD_CONNECTION);
if (!connect_method && req.upgrade_request) {
auto connection = req.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
buf->append((*connection).value);
buf->append("\r\n");
}
auto upgrade = downstream_->get_request_header(http2::HD_UPGRADE);
auto upgrade = req.fs.header(http2::HD_UPGRADE);
if (upgrade) {
buf->append("Upgrade: ");
buf->append((*upgrade).value);
@ -300,7 +296,7 @@ int HttpDownstreamConnection::push_request_headers() {
}
}
auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
auto xff = req.fs.header(http2::HD_X_FORWARDED_FOR);
if (get_config()->add_x_forwarded_for) {
buf->append("X-Forwarded-For: ");
if (xff && !get_config()->strip_incoming_x_forwarded_for) {
@ -317,11 +313,11 @@ int HttpDownstreamConnection::push_request_headers() {
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!connect_method) {
buf->append("X-Forwarded-Proto: ");
assert(!scheme.empty());
buf->append(scheme);
assert(!req.scheme.empty());
buf->append(req.scheme);
buf->append("\r\n");
}
auto via = downstream_->get_request_header(http2::HD_VIA);
auto via = req.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
buf->append("Via: ");
@ -334,8 +330,7 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append((*via).value);
buf->append(", ");
}
buf->append(http::create_via_header_value(
downstream_->get_request_major(), downstream_->get_request_minor()));
buf->append(http::create_via_header_value(req.http_major, req.http_minor));
buf->append("\r\n");
}
@ -392,8 +387,10 @@ int HttpDownstreamConnection::end_upload_data() {
return 0;
}
const auto &req = downstream_->request();
auto output = downstream_->get_request_buf();
auto &trailers = downstream_->get_request_trailers();
const auto &trailers = req.fs.trailers();
if (trailers.empty()) {
output->append("0\r\n\r\n");
} else {
@ -484,13 +481,15 @@ namespace {
int htp_hdrs_completecb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
auto &resp = downstream->response();
int rv;
downstream->set_response_http_status(htp->status_code);
downstream->set_response_major(htp->http_major);
downstream->set_response_minor(htp->http_minor);
resp.http_status = htp->status_code;
resp.http_major = htp->http_major;
resp.http_minor = htp->http_minor;
if (downstream->index_response_headers() != 0) {
if (resp.fs.index_headers() != 0) {
downstream->set_response_state(Downstream::MSG_BAD_HEADER);
return -1;
}
@ -502,7 +501,7 @@ int htp_hdrs_completecb(http_parser *htp) {
if (downstream->get_non_final_response()) {
// Reset content-length because we reuse same Downstream for the
// next response.
downstream->set_response_content_length(-1);
resp.fs.content_length = -1;
// For non-final response code, we just call
// on_downstream_header_complete() without changing response
// state.
@ -516,13 +515,13 @@ int htp_hdrs_completecb(http_parser *htp) {
return 1;
}
downstream->set_response_connection_close(!http_should_keep_alive(htp));
resp.connection_close = !http_should_keep_alive(htp);
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->inspect_http1_response();
if (downstream->get_upgraded()) {
// content-length must be ignored for upgraded connection.
downstream->set_response_content_length(-1);
downstream->set_response_connection_close(true);
resp.fs.content_length = -1;
resp.connection_close = true;
// transfer-encoding not applied to upgraded connection
downstream->set_chunked_response(false);
}
@ -542,7 +541,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
}
unsigned int status = downstream->get_response_http_status();
auto status = resp.http_status;
// Ignore the response body. HEAD response may contain
// Content-Length or Transfer-Encoding: chunked. Some server send
// 304 status code with nonzero Content-Length, but without response
@ -551,9 +550,8 @@ int htp_hdrs_completecb(http_parser *htp) {
// TODO It seems that the cases other than HEAD are handled by
// http-parser. Need test.
return downstream->get_request_method() == HTTP_HEAD ||
(100 <= status && status <= 199) || status == 204 ||
status == 304
return req.method == HTTP_HEAD || (100 <= status && status <= 199) ||
status == 204 || status == 304
? 1
: 0;
}
@ -562,18 +560,20 @@ int htp_hdrs_completecb(http_parser *htp) {
namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response();
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) {
downstream->append_last_response_header_key(data, len);
if (resp.fs.header_key_prev()) {
resp.fs.append_last_header_key(data, len);
} else {
downstream->add_response_header(std::string(data, len), "");
resp.fs.add_header(std::string(data, len), "");
}
} else {
// trailer part
if (downstream->get_response_trailer_key_prev()) {
downstream->append_last_response_trailer_key(data, len);
if (resp.fs.trailer_key_prev()) {
resp.fs.append_last_trailer_key(data, len);
} else {
downstream->add_response_trailer(std::string(data, len), "");
resp.fs.add_trailer(std::string(data, len), "");
}
}
return 0;
@ -583,18 +583,12 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response();
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) {
downstream->set_last_response_header_value(data, len);
} else {
downstream->append_last_response_header_value(data, len);
}
resp.fs.append_last_header_value(data, len);
} else {
if (downstream->get_response_trailer_key_prev()) {
downstream->set_last_response_trailer_value(data, len);
} else {
downstream->append_last_response_trailer_value(data, len);
}
resp.fs.append_last_trailer_value(data, len);
}
return 0;
}

View File

@ -85,25 +85,27 @@ namespace {
int htp_uricb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream();
auto &req = downstream->request();
// We happen to have the same value for method token.
downstream->set_request_method(htp->method);
req.method = htp->method;
if (downstream->get_request_headers_sum() + len >
get_config()->header_field_buffer) {
if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large URI size="
<< downstream->get_request_headers_sum() + len;
<< req.fs.buffer_size() + len;
}
assert(downstream->get_request_state() == Downstream::INITIAL);
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1;
}
downstream->add_request_headers_sum(len);
if (downstream->get_request_method() == HTTP_CONNECT) {
downstream->append_request_http2_authority(data, len);
req.fs.add_extra_buffer_size(len);
if (req.method == HTTP_CONNECT) {
req.authority.append(data, len);
} else {
downstream->append_request_path(data, len);
req.path.append(data, len);
}
return 0;
@ -114,11 +116,12 @@ namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len >
get_config()->header_field_buffer) {
auto &req = downstream->request();
if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size="
<< downstream->get_request_headers_sum() + len;
<< req.fs.buffer_size() + len;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
@ -126,36 +129,33 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) {
downstream->append_last_request_header_key(data, len);
if (req.fs.header_key_prev()) {
req.fs.append_last_header_key(data, len);
} else {
if (downstream->get_request_headers().size() >=
get_config()->max_header_fields) {
if (req.fs.num_fields() >= get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num="
<< downstream->get_request_headers().size() + 1;
ULOG(INFO, upstream)
<< "Too many header field num=" << req.fs.num_fields() + 1;
}
downstream->set_request_state(
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
return -1;
}
downstream->add_request_header(std::string(data, len), "");
req.fs.add_header(std::string(data, len), "");
}
} else {
// trailer part
if (downstream->get_request_trailer_key_prev()) {
downstream->append_last_request_trailer_key(data, len);
if (req.fs.trailer_key_prev()) {
req.fs.append_last_trailer_key(data, len);
} else {
if (downstream->get_request_headers().size() +
downstream->get_request_trailers().size() >=
get_config()->max_header_fields) {
if (req.fs.num_fields() >= get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num="
<< downstream->get_request_headers().size() + 1;
ULOG(INFO, upstream)
<< "Too many header field num=" << req.fs.num_fields() + 1;
}
return -1;
}
downstream->add_request_trailer(std::string(data, len), "");
req.fs.add_trailer(std::string(data, len), "");
}
}
return 0;
@ -166,11 +166,12 @@ namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len >
get_config()->header_field_buffer) {
auto &req = downstream->request();
if (req.fs.buffer_size() + len > get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size="
<< downstream->get_request_headers_sum() + len;
<< req.fs.buffer_size() + len;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
@ -178,30 +179,23 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) {
downstream->set_last_request_header_value(data, len);
} else {
downstream->append_last_request_header_value(data, len);
}
req.fs.append_last_header_value(data, len);
} else {
if (downstream->get_request_trailer_key_prev()) {
downstream->set_last_request_trailer_value(data, len);
} else {
downstream->append_last_request_trailer_value(data, len);
}
req.fs.append_last_trailer_value(data, len);
}
return 0;
}
} // namespace
namespace {
void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
void rewrite_request_host_path_from_uri(Request &req, const char *uri,
http_parser_url &u) {
assert(u.field_set & (1 << UF_HOST));
auto &authority = req.authority;
authority.clear();
// As per https://tools.ietf.org/html/rfc7230#section-5.4, we
// rewrite host header field with authority component.
std::string authority;
http2::copy_url_component(authority, &u, UF_HOST, uri);
// TODO properly check IPv6 numeric address
if (authority.find(':') != std::string::npos) {
@ -212,23 +206,20 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
authority += ':';
authority += util::utos(u.port);
}
downstream->set_request_http2_authority(authority);
std::string scheme;
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
downstream->set_request_http2_scheme(std::move(scheme));
http2::copy_url_component(req.scheme, &u, UF_SCHEMA, uri);
std::string path;
if (u.field_set & (1 << UF_PATH)) {
http2::copy_url_component(path, &u, UF_PATH, uri);
} else if (downstream->get_request_method() == HTTP_OPTIONS) {
} else if (req.method == HTTP_OPTIONS) {
// Server-wide OPTIONS takes following form in proxy request:
//
// OPTIONS http://example.org HTTP/1.1
//
// Notice that no slash after authority. See
// http://tools.ietf.org/html/rfc7230#section-5.3.4
downstream->set_request_path("");
req.path = "";
// we ignore query component here
return;
} else {
@ -240,10 +231,9 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
path.append(uri + fdata.off, fdata.len);
}
if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(std::move(path));
req.path = std::move(path);
} else {
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(path), std::end(path)));
req.path = http2::rewrite_clean_path(std::begin(path), std::end(path));
}
}
} // namespace
@ -256,36 +246,35 @@ int htp_hdrs_completecb(http_parser *htp) {
ULOG(INFO, upstream) << "HTTP request headers completed";
}
auto downstream = upstream->get_downstream();
auto &req = downstream->request();
auto &resp = downstream->response();
downstream->set_request_major(htp->http_major);
downstream->set_request_minor(htp->http_minor);
req.http_major = htp->http_major;
req.http_minor = htp->http_minor;
downstream->set_request_connection_close(!http_should_keep_alive(htp));
req.connection_close = !http_should_keep_alive(htp);
auto method = downstream->get_request_method();
auto method = req.method;
if (LOG_ENABLED(INFO)) {
std::stringstream ss;
ss << http2::to_method_string(method) << " "
<< (method == HTTP_CONNECT ? downstream->get_request_http2_authority()
: downstream->get_request_path()) << " "
<< "HTTP/" << downstream->get_request_major() << "."
<< downstream->get_request_minor() << "\n";
const auto &headers = downstream->get_request_headers();
for (size_t i = 0; i < headers.size(); ++i) {
ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": "
<< headers[i].value << "\n";
<< (method == HTTP_CONNECT ? req.authority : req.path) << " "
<< "HTTP/" << req.http_major << "." << req.http_minor << "\n";
for (const auto &kv : req.fs.headers()) {
ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
}
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
}
if (downstream->index_request_headers() != 0) {
if (req.fs.index_headers() != 0) {
return -1;
}
if (downstream->get_request_major() == 1 &&
downstream->get_request_minor() == 1 &&
!downstream->get_request_header(http2::HD_HOST)) {
if (req.http_major == 1 && req.http_minor == 1 &&
!req.fs.header(http2::HD_HOST)) {
return -1;
}
@ -295,7 +284,7 @@ int htp_hdrs_completecb(http_parser *htp) {
http_parser_url u{};
// make a copy of request path, since we may set request path
// while we are refering to original request path.
auto path = downstream->get_request_path();
auto path = req.path;
rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u);
if (rv != 0) {
// Expect to respond with 400 bad request
@ -309,24 +298,23 @@ int htp_hdrs_completecb(http_parser *htp) {
}
if (method == HTTP_OPTIONS && path == "*") {
downstream->set_request_path("");
req.path = "";
} else {
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(path), std::end(path)));
req.path = http2::rewrite_clean_path(std::begin(path), std::end(path));
}
auto host = downstream->get_request_header(http2::HD_HOST);
auto host = req.fs.header(http2::HD_HOST);
if (host) {
downstream->set_request_http2_authority(host->value);
req.authority = host->value;
}
if (upstream->get_client_handler()->get_ssl()) {
downstream->set_request_http2_scheme("https");
req.scheme = "https";
} else {
downstream->set_request_http2_scheme("http");
req.scheme = "http";
}
} else {
rewrite_request_host_path_from_uri(downstream, path.c_str(), u);
rewrite_request_host_path_from_uri(req, path.c_str(), u);
}
}
@ -338,7 +326,7 @@ int htp_hdrs_completecb(http_parser *htp) {
auto mruby_ctx = worker->get_mruby_context();
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
downstream->set_response_http_status(500);
resp.http_status = 500;
return -1;
}
#endif // HAVE_MRUBY
@ -527,7 +515,7 @@ int HttpsUpstream::on_read() {
if (htperr == HPE_INVALID_METHOD) {
status_code = 501;
} else if (downstream) {
status_code = downstream->get_response_http_status();
status_code = downstream->response().http_status;
if (status_code == 0) {
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
status_code = 503;
@ -571,6 +559,7 @@ int HttpsUpstream::on_write() {
auto dconn = downstream->get_downstream_connection();
auto output = downstream->get_response_buf();
const auto &resp = downstream->response();
if (output->rleft() == 0 && dconn &&
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
@ -591,7 +580,7 @@ int HttpsUpstream::on_write() {
// We need to postpone detachment until all data are sent so that
// we can notify nghttp2 library all data consumed.
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_connection_close() ||
if (resp.connection_close ||
downstream->get_request_state() != Downstream::MSG_COMPLETE) {
// Connection close
downstream->pop_downstream_connection();
@ -766,32 +755,31 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen) {
auto major = downstream->get_request_major();
auto minor = downstream->get_request_minor();
const auto &req = downstream->request();
auto &resp = downstream->response();
auto connection_close = false;
if (major <= 0 || (major == 1 && minor == 0)) {
if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
connection_close = true;
} else {
auto c = downstream->get_response_header(http2::HD_CONNECTION);
auto c = resp.fs.header(http2::HD_CONNECTION);
if (c && util::strieq_l("close", c->value)) {
connection_close = true;
}
}
if (connection_close) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
handler_->set_should_close_after_write(true);
}
auto output = downstream->get_response_buf();
output->append("HTTP/1.1 ");
output->append(
http2::get_status_string(downstream->get_response_http_status()));
output->append(http2::get_status_string(resp.http_status));
output->append("\r\n");
for (auto &kv : downstream->get_response_headers()) {
for (auto &kv : resp.fs.headers()) {
if (kv.name.empty() || kv.name[0] == ':') {
continue;
}
@ -801,7 +789,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
output->append("\r\n");
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
output->append("Server: ");
output->append(get_config()->server_name,
strlen(get_config()->server_name));
@ -828,10 +816,12 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
downstream = get_downstream();
}
downstream->set_response_http_status(status_code);
auto &resp = downstream->response();
resp.http_status = status_code;
// we are going to close connection for both frontend and backend in
// error condition. This is safest option.
downstream->set_response_connection_close(true);
resp.connection_close = true;
handler_->set_should_close_after_write(true);
auto output = downstream->get_response_buf();
@ -896,6 +886,9 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
const auto &req = downstream->request();
auto &resp = downstream->response();
#ifdef HAVE_MRUBY
if (!downstream->get_non_final_response()) {
auto worker = handler_->get_worker();
@ -912,16 +905,16 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
#endif // HAVE_MRUBY
auto connect_method = downstream->get_request_method() == HTTP_CONNECT;
auto connect_method = req.method == HTTP_CONNECT;
auto buf = downstream->get_response_buf();
buf->append("HTTP/");
buf->append(util::utos(downstream->get_request_major()));
buf->append(util::utos(req.http_major));
buf->append(".");
buf->append(util::utos(downstream->get_request_minor()));
buf->append(util::utos(req.http_minor));
buf->append(" ");
buf->append(http2::get_status_string(downstream->get_response_http_status()));
buf->append(http2::get_status_string(resp.http_status));
buf->append("\r\n");
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
@ -930,8 +923,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
get_client_handler()->get_upstream_scheme());
}
http2::build_http1_headers_from_headers(buf,
downstream->get_response_headers());
http2::build_http1_headers_from_headers(buf, resp.fs.headers());
if (downstream->get_non_final_response()) {
buf->append("\r\n");
@ -940,7 +932,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
log_response_headers(buf);
}
downstream->clear_response_headers();
resp.fs.clear_headers();
return 0;
}
@ -950,15 +942,13 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
// after graceful shutdown commenced, add connection: close header
// field.
if (worker->get_graceful_shutdown()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
}
// We check downstream->get_response_connection_close() in case when
// the Content-Length is not available.
if (!downstream->get_request_connection_close() &&
!downstream->get_response_connection_close()) {
if (downstream->get_request_major() <= 0 ||
downstream->get_request_minor() <= 0) {
if (!req.connection_close && !resp.connection_close) {
if (req.http_major <= 0 || req.http_minor <= 0) {
// We add this header for HTTP/1.0 or HTTP/0.9 clients
buf->append("Connection: Keep-Alive\r\n");
}
@ -967,14 +957,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
if (!connect_method && downstream->get_upgraded()) {
auto connection = downstream->get_response_header(http2::HD_CONNECTION);
auto connection = resp.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
buf->append((*connection).value);
buf->append("\r\n");
}
auto upgrade = downstream->get_response_header(http2::HD_UPGRADE);
auto upgrade = resp.fs.header(http2::HD_UPGRADE);
if (upgrade) {
buf->append("Upgrade: ");
buf->append((*upgrade).value);
@ -982,7 +972,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
if (!downstream->get_response_header(http2::HD_ALT_SVC)) {
if (!resp.fs.header(http2::HD_ALT_SVC)) {
// We won't change or alter alt-svc from backend for now
if (!get_config()->altsvcs.empty()) {
buf->append("Alt-Svc: ");
@ -1002,7 +992,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append(get_config()->server_name, strlen(get_config()->server_name));
buf->append("\r\n");
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
buf->append("Server: ");
buf->append((*server).value);
@ -1010,7 +1000,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
buf->append("Via: ");
@ -1023,8 +1013,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append((*via).value);
buf->append(", ");
}
buf->append(http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor()));
buf->append(
http::create_via_header_value(resp.http_major, resp.http_minor));
buf->append("\r\n");
}
@ -1068,9 +1058,12 @@ int HttpsUpstream::on_downstream_body(Downstream *downstream,
}
int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
const auto &req = downstream->request();
auto &resp = downstream->response();
if (downstream->get_chunked_response()) {
auto output = downstream->get_response_buf();
auto &trailers = downstream->get_response_trailers();
const auto &trailers = resp.fs.trailers();
if (trailers.empty()) {
output->append("0\r\n\r\n");
} else {
@ -1084,11 +1077,10 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
}
if (!downstream->validate_response_bodylen()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
}
if (downstream->get_request_connection_close() ||
downstream->get_response_connection_close()) {
if (req.connection_close || resp.connection_close) {
auto handler = get_client_handler();
handler->set_should_close_after_write(true);
}

View File

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

View File

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

View File

@ -49,7 +49,8 @@ namespace {
mrb_value request_get_http_version_major(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return mrb_fixnum_value(downstream->get_request_major());
const auto &req = downstream->request();
return mrb_fixnum_value(req.http_major);
}
} // namespace
@ -57,7 +58,8 @@ namespace {
mrb_value request_get_http_version_minor(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return mrb_fixnum_value(downstream->get_request_minor());
const auto &req = downstream->request();
return mrb_fixnum_value(req.http_minor);
}
} // namespace
@ -65,7 +67,8 @@ namespace {
mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto method = http2::to_method_string(downstream->get_request_method());
const auto &req = downstream->request();
auto method = http2::to_method_string(req.method);
return mrb_str_new_cstr(mrb, method);
}
@ -75,6 +78,7 @@ namespace {
mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
@ -90,7 +94,7 @@ mrb_value request_set_method(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "method not supported");
}
downstream->set_request_method(token);
req.method = token;
return self;
}
@ -100,9 +104,9 @@ namespace {
mrb_value request_get_authority(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &authority = downstream->get_request_http2_authority();
const auto &req = downstream->request();
return mrb_str_new(mrb, authority.c_str(), authority.size());
return mrb_str_new(mrb, req.authority.c_str(), req.authority.size());
}
} // namespace
@ -110,6 +114,7 @@ namespace {
mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
@ -120,7 +125,7 @@ mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string");
}
downstream->set_request_http2_authority(std::string(authority, n));
req.authority.assign(authority, n);
return self;
}
@ -130,9 +135,9 @@ namespace {
mrb_value request_get_scheme(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &scheme = downstream->get_request_http2_scheme();
const auto &req = downstream->request();
return mrb_str_new(mrb, scheme.c_str(), scheme.size());
return mrb_str_new(mrb, req.scheme.c_str(), req.scheme.size());
}
} // namespace
@ -140,6 +145,7 @@ namespace {
mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
@ -150,7 +156,7 @@ mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string");
}
downstream->set_request_http2_scheme(std::string(scheme, n));
req.scheme.assign(scheme, n);
return self;
}
@ -160,9 +166,9 @@ namespace {
mrb_value request_get_path(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &path = downstream->get_request_path();
const auto &req = downstream->request();
return mrb_str_new(mrb, path.c_str(), path.size());
return mrb_str_new(mrb, req.path.c_str(), req.path.size());
}
} // namespace
@ -170,6 +176,7 @@ namespace {
mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
@ -177,7 +184,7 @@ mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
mrb_int pathlen;
mrb_get_args(mrb, "s", &path, &pathlen);
downstream->set_request_path(std::string(path, pathlen));
req.path.assign(path, pathlen);
return self;
}
@ -187,7 +194,8 @@ namespace {
mrb_value request_get_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return create_headers_hash(mrb, downstream->get_request_headers());
const auto &req = downstream->request();
return create_headers_hash(mrb, req.fs.headers());
}
} // namespace
@ -195,6 +203,7 @@ namespace {
mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
@ -209,7 +218,7 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
if (repl) {
size_t p = 0;
auto &headers = downstream->get_request_headers();
auto &headers = req.fs.headers();
for (size_t i = 0; i < headers.size(); ++i) {
auto &hd = headers[i];
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key),
@ -227,14 +236,12 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto n = mrb_ary_len(mrb, values);
for (int i = 0; i < n; ++i) {
auto value = mrb_ary_entry(values, i);
downstream->add_request_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
}
} else if (!mrb_nil_p(values)) {
downstream->add_request_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(values), RSTRING_LEN(values)));
req.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(values), RSTRING_LEN(values)));
}
data->request_headers_dirty = true;
@ -259,10 +266,11 @@ namespace {
mrb_value request_clear_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &req = downstream->request();
check_phase(mrb, data->phase, PHASE_REQUEST);
downstream->clear_request_headers();
req.fs.clear_headers();
return mrb_nil_value();
}

View File

@ -49,7 +49,8 @@ namespace {
mrb_value response_get_http_version_major(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return mrb_fixnum_value(downstream->get_response_major());
const auto &resp = downstream->response();
return mrb_fixnum_value(resp.http_major);
}
} // namespace
@ -57,7 +58,8 @@ namespace {
mrb_value response_get_http_version_minor(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return mrb_fixnum_value(downstream->get_response_minor());
const auto &resp = downstream->response();
return mrb_fixnum_value(resp.http_minor);
}
} // namespace
@ -65,8 +67,8 @@ namespace {
mrb_value response_get_status(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return mrb_fixnum_value(downstream->get_response_http_status());
const auto &resp = downstream->response();
return mrb_fixnum_value(resp.http_status);
}
} // namespace
@ -74,6 +76,7 @@ namespace {
mrb_value response_set_status(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &resp = downstream->response();
mrb_int status;
mrb_get_args(mrb, "i", &status);
@ -83,7 +86,7 @@ mrb_value response_set_status(mrb_state *mrb, mrb_value self) {
"invalid status; it should be [200, 999], inclusive");
}
downstream->set_response_http_status(status);
resp.http_status = status;
return self;
}
@ -93,7 +96,9 @@ namespace {
mrb_value response_get_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
return create_headers_hash(mrb, downstream->get_response_headers());
const auto &resp = downstream->response();
return create_headers_hash(mrb, resp.fs.headers());
}
} // namespace
@ -101,6 +106,7 @@ namespace {
mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &resp = downstream->response();
mrb_value key, values;
mrb_get_args(mrb, "oo", &key, &values);
@ -113,7 +119,7 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
if (repl) {
size_t p = 0;
auto &headers = downstream->get_response_headers();
auto &headers = resp.fs.headers();
for (size_t i = 0; i < headers.size(); ++i) {
auto &hd = headers[i];
if (util::streq(std::begin(hd.name), hd.name.size(), RSTRING_PTR(key),
@ -131,14 +137,12 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
auto n = mrb_ary_len(mrb, values);
for (int i = 0; i < n; ++i) {
auto value = mrb_ary_entry(values, i);
downstream->add_response_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(value), RSTRING_LEN(value)));
}
} else if (!mrb_nil_p(values)) {
downstream->add_response_header(
std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(values), RSTRING_LEN(values)));
resp.fs.add_header(std::string(RSTRING_PTR(key), RSTRING_LEN(key)),
std::string(RSTRING_PTR(values), RSTRING_LEN(values)));
}
data->response_headers_dirty = true;
@ -163,8 +167,9 @@ namespace {
mrb_value response_clear_headers(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &resp = downstream->response();
downstream->clear_response_headers();
resp.fs.clear_headers();
return mrb_nil_value();
}
@ -174,6 +179,7 @@ namespace {
mrb_value response_return(mrb_state *mrb, mrb_value self) {
auto data = static_cast<MRubyAssocData *>(mrb->ud);
auto downstream = data->downstream;
auto &resp = downstream->response();
int rv;
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
@ -187,12 +193,12 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
const uint8_t *body = nullptr;
size_t bodylen = 0;
if (downstream->get_response_http_status() == 0) {
downstream->set_response_http_status(200);
if (resp.http_status == 0) {
resp.http_status = 200;
}
if (data->response_headers_dirty) {
downstream->index_response_headers();
resp.fs.index_headers();
data->response_headers_dirty = false;
}
@ -201,21 +207,20 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
bodylen = vallen;
}
auto cl = downstream->get_response_header(http2::HD_CONTENT_LENGTH);
auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
if (cl) {
cl->value = util::utos(bodylen);
} else {
downstream->add_response_header("content-length", util::utos(bodylen),
http2::HD_CONTENT_LENGTH);
resp.fs.add_header("content-length", util::utos(bodylen),
http2::HD_CONTENT_LENGTH);
}
downstream->set_response_content_length(bodylen);
resp.fs.content_length = bodylen;
auto date = downstream->get_response_header(http2::HD_DATE);
auto date = resp.fs.header(http2::HD_DATE);
if (!date) {
auto lgconf = log_config();
lgconf->update_tstamp(std::chrono::system_clock::now());
downstream->add_response_header("date", lgconf->time_http_str,
http2::HD_DATE);
resp.fs.add_header("date", lgconf->time_http_str, http2::HD_DATE);
}
auto upstream = downstream->get_upstream();

View File

@ -147,6 +147,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
auto downstream = upstream->add_pending_downstream(
frame->syn_stream.stream_id, frame->syn_stream.pri);
auto &req = downstream->request();
downstream->reset_upstream_rtimer();
auto nv = frame->syn_stream.nv;
@ -178,20 +180,20 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
}
for (size_t i = 0; nv[i]; i += 2) {
downstream->add_request_header(nv[i], nv[i + 1]);
req.fs.add_header(nv[i], nv[i + 1]);
}
if (downstream->index_request_headers() != 0) {
if (req.fs.index_headers() != 0) {
if (upstream->error_reply(downstream, 400) != 0) {
ULOG(FATAL, upstream) << "error_reply failed";
}
return;
}
auto path = downstream->get_request_header(http2::HD__PATH);
auto scheme = downstream->get_request_header(http2::HD__SCHEME);
auto host = downstream->get_request_header(http2::HD__HOST);
auto method = downstream->get_request_header(http2::HD__METHOD);
auto path = req.fs.header(http2::HD__PATH);
auto scheme = req.fs.header(http2::HD__SCHEME);
auto host = req.fs.header(http2::HD__HOST);
auto method = req.fs.header(http2::HD__METHOD);
if (!method) {
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
@ -222,24 +224,24 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
return;
}
downstream->set_request_method(method_token);
req.method = method_token;
if (is_connect) {
downstream->set_request_http2_authority(path->value);
req.authority = path->value;
} else {
downstream->set_request_http2_scheme(scheme->value);
downstream->set_request_http2_authority(host->value);
req.scheme = scheme->value;
req.authority = host->value;
if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(path->value);
req.path = path->value;
} else if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty.
} else {
downstream->set_request_path(http2::rewrite_clean_path(
std::begin(path->value), std::end(path->value)));
req.path = http2::rewrite_clean_path(std::begin(path->value),
std::end(path->value));
}
}
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
downstream->set_request_http2_expect_body(true);
req.http2_expect_body = true;
}
downstream->inspect_http2_request();
@ -285,8 +287,7 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
} // namespace
void SpdyUpstream::start_downstream(Downstream *downstream) {
if (downstream_queue_.can_activate(
downstream->get_request_http2_authority())) {
if (downstream_queue_.can_activate(downstream->request().authority)) {
initiate_downstream(downstream);
return;
}
@ -819,10 +820,11 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
data_prd_ptr = &data_prd;
}
auto status_string =
http2::get_status_string(downstream->get_response_http_status());
const auto &resp = downstream->response();
auto &headers = downstream->get_response_headers();
auto status_string = http2::get_status_string(resp.http_status);
const auto &headers = resp.fs.headers();
auto nva = std::vector<const char *>();
// 3 for :status, :version and server
@ -848,7 +850,7 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
nva.push_back(kv.value.c_str());
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
nva.push_back("server");
nva.push_back(get_config()->server_name);
}
@ -875,8 +877,10 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
int SpdyUpstream::error_reply(Downstream *downstream,
unsigned int status_code) {
int rv;
auto &resp = downstream->response();
auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
resp.http_status = status_code;
auto body = downstream->get_response_buf();
body->append(html.c_str(), html.size());
downstream->set_response_state(Downstream::MSG_COMPLETE);
@ -937,15 +941,19 @@ void SpdyUpstream::remove_downstream(Downstream *downstream) {
// WARNING: Never call directly or indirectly spdylay_session_send or
// spdylay_session_recv. These calls may delete downstream.
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
auto &resp = downstream->response();
if (downstream->get_non_final_response()) {
// SPDY does not support non-final response. We could send it
// with HEADERS and final response in SYN_REPLY, but it is not
// official way.
downstream->clear_response_headers();
resp.fs.clear_headers();
return 0;
}
const auto &req = downstream->request();
#ifdef HAVE_MRUBY
auto worker = handler_->get_worker();
auto mruby_ctx = worker->get_mruby_context();
@ -969,23 +977,22 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!get_config()->no_location_rewrite) {
downstream->rewrite_location_response_header(
downstream->get_request_http2_scheme());
downstream->rewrite_location_response_header(req.scheme);
}
size_t nheader = downstream->get_response_headers().size();
// 8 means server, :status, :version and possible via header field.
auto nv = make_unique<const char *[]>(
nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1);
resp.fs.headers().size() * 2 + 8 +
get_config()->add_response_headers.size() * 2 + 1);
size_t hdidx = 0;
std::string via_value;
std::string status_string =
http2::get_status_string(downstream->get_response_http_status());
auto status_string = http2::get_status_string(resp.http_status);
nv[hdidx++] = ":status";
nv[hdidx++] = status_string.c_str();
nv[hdidx++] = ":version";
nv[hdidx++] = "HTTP/1.1";
for (auto &hd : downstream->get_response_headers()) {
for (auto &hd : resp.fs.headers()) {
if (hd.name.empty() || hd.name.c_str()[0] == ':') {
continue;
}
@ -1007,14 +1014,14 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
nv[hdidx++] = "server";
nv[hdidx++] = get_config()->server_name;
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
nv[hdidx++] = "server";
nv[hdidx++] = server->value.c_str();
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
nv[hdidx++] = "via";
@ -1025,8 +1032,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
via_value = via->value;
via_value += ", ";
}
via_value += http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor());
via_value +=
http::create_via_header_value(resp.http_major, resp.http_minor);
nv[hdidx++] = "via";
nv[hdidx++] = via_value.c_str();
}
@ -1084,9 +1091,11 @@ int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) {
DLOG(INFO, downstream) << "HTTP response completed";
}
auto &resp = downstream->response();
if (!downstream->validate_response_bodylen()) {
rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
downstream->set_response_connection_close(true);
resp.connection_close = true;
return 0;
}