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