nghttpx: Extract response related fields to Response struct

This commit is contained in:
Tatsuhiro Tsujikawa 2016-01-14 00:37:45 +09:00
parent a7fd37ffdf
commit 3b8889a2a1
13 changed files with 245 additions and 510 deletions

View File

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

View File

@ -812,6 +812,7 @@ std::string construct_absolute_request_uri(const Request &req) {
void ClientHandler::write_accesslog(Downstream *downstream) {
nghttp2::ssl::TLSSessionInfo tls_info;
const auto &req = downstream->request();
const auto &resp = downstream->response();
upstream_accesslog(
get_config()->accesslog_format,
@ -832,8 +833,7 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
downstream->get_request_start_time(), // request_start_time
std::chrono::high_resolution_clock::now(), // request_end_time
req.http_major, req.http_minor,
downstream->get_response_http_status(),
req.http_major, req.http_minor, resp.http_status,
downstream->get_response_sent_bodylen(), port_.c_str(),
get_config()->port, get_config()->pid,
});

View File

@ -116,18 +116,15 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
: dlnext(nullptr), dlprev(nullptr),
request_start_time_(std::chrono::high_resolution_clock::now()),
request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0),
response_bodylen_(0), response_sent_bodylen_(0),
response_content_length_(-1), upstream_(upstream), blocked_link_(nullptr),
response_headers_sum_(0), request_datalen_(0), response_datalen_(0),
response_bodylen_(0), response_sent_bodylen_(0), upstream_(upstream),
blocked_link_(nullptr), request_datalen_(0), response_datalen_(0),
num_retry_(0), stream_id_(stream_id), priority_(priority),
downstream_stream_id_(-1),
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
request_state_(INITIAL), response_state_(INITIAL),
response_http_status_(0), response_major_(1), response_minor_(1),
dispatch_state_(DISPATCH_NONE), upgraded_(false), chunked_request_(false),
chunked_response_(false), response_connection_close_(false),
response_header_key_prev_(false), response_trailer_key_prev_(false),
expect_final_response_(false), request_pending_(false) {
chunked_response_(false), expect_final_response_(false),
request_pending_(false) {
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
get_config()->stream_read_timeout);
@ -142,10 +139,6 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
upstream_wtimer_.data = this;
downstream_rtimer_.data = this;
downstream_wtimer_.data = this;
http2::init_hdidx(response_hdidx_);
response_headers_.reserve(32);
}
Downstream::~Downstream() {
@ -367,50 +360,6 @@ void append_last_header_value(bool key_prev, size_t &sum, Headers &headers,
}
} // namespace
namespace {
void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
const char *data, size_t len) {
key_prev = false;
sum += len;
auto &item = headers.back();
item.value.assign(data, len);
}
} // namespace
namespace {
int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
int64_t &content_length) {
http2::init_hdidx(hdidx);
content_length = -1;
for (size_t i = 0; i < headers.size(); ++i) {
auto &kv = headers[i];
util::inp_strlower(kv.name);
auto token = http2::lookup_token(
reinterpret_cast<const uint8_t *>(kv.name.c_str()), kv.name.size());
if (token < 0) {
continue;
}
kv.token = token;
http2::index_header(hdidx, token, i);
if (token == http2::HD_CONTENT_LENGTH) {
auto len = util::parse_uint(kv.value);
if (len == -1) {
return -1;
}
if (content_length != -1) {
return -1;
}
content_length = len;
}
}
return 0;
}
} // namespace
int FieldStore::index_headers() {
http2::init_hdidx(hdidx_);
content_length = -1;
@ -446,6 +395,10 @@ const Headers::value_type *FieldStore::header(int16_t token) const {
return http2::get_header(hdidx_, token, headers_);
}
Headers::value_type *FieldStore::header(int16_t token) {
return http2::get_header(hdidx_, token, headers_);
}
const Headers::value_type *FieldStore::header(const std::string &name) const {
return get_header_linear(headers_, name);
}
@ -585,30 +538,9 @@ int Downstream::end_upload_data() {
return dconn_->end_upload_data();
}
const Headers &Downstream::get_response_headers() const {
return response_headers_;
}
Headers &Downstream::get_response_headers() { return response_headers_; }
int Downstream::index_response_headers() {
return index_headers(response_hdidx_, response_headers_,
response_content_length_);
}
const Headers::value_type *
Downstream::get_response_header(int16_t token) const {
return http2::get_header(response_hdidx_, token, response_headers_);
}
Headers::value_type *Downstream::get_response_header(int16_t token) {
return http2::get_header(response_hdidx_, token, response_headers_);
}
void Downstream::rewrite_location_response_header(
const std::string &upstream_scheme) {
auto hd =
http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_);
auto hd = resp_.fs.header(http2::HD_LOCATION);
if (!hd) {
return;
}
@ -656,131 +588,18 @@ void Downstream::rewrite_location_response_header(
}
}
}
if (!new_uri.empty()) {
auto idx = response_hdidx_[http2::HD_LOCATION];
response_headers_[idx].value = std::move(new_uri);
if (new_uri.empty()) {
return;
}
}
void Downstream::add_response_header(std::string name, std::string value) {
add_header(response_header_key_prev_, response_headers_sum_,
response_headers_, std::move(name), std::move(value));
}
void Downstream::set_last_response_header_value(const char *data, size_t len) {
set_last_header_value(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::add_response_header(std::string name, std::string value,
int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
response_headers_sum_ += name.size() + value.size();
response_headers_.emplace_back(std::move(name), std::move(value), false,
token);
}
void Downstream::add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
add_header(response_headers_sum_, response_headers_, name, namelen, value,
valuelen, no_index, token);
}
bool Downstream::get_response_header_key_prev() const {
return response_header_key_prev_;
}
void Downstream::append_last_response_header_key(const char *data, size_t len) {
append_last_header_key(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::append_last_response_header_value(const char *data,
size_t len) {
append_last_header_value(response_header_key_prev_, response_headers_sum_,
response_headers_, data, len);
}
void Downstream::clear_response_headers() {
Headers().swap(response_headers_);
http2::init_hdidx(response_hdidx_);
}
size_t Downstream::get_response_headers_sum() const {
return response_headers_sum_;
}
const Headers &Downstream::get_response_trailers() const {
return response_trailers_;
}
void Downstream::add_response_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token) {
add_header(response_headers_sum_, response_trailers_, name, namelen, value,
valuelen, no_index, -1);
}
unsigned int Downstream::get_response_http_status() const {
return response_http_status_;
}
void Downstream::add_response_trailer(std::string name, std::string value) {
add_header(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, std::move(name), std::move(value));
}
void Downstream::set_last_response_trailer_value(const char *data, size_t len) {
set_last_header_value(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
bool Downstream::get_response_trailer_key_prev() const {
return response_trailer_key_prev_;
}
void Downstream::append_last_response_trailer_key(const char *data,
size_t len) {
append_last_header_key(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
void Downstream::append_last_response_trailer_value(const char *data,
size_t len) {
append_last_header_value(response_trailer_key_prev_, response_headers_sum_,
response_trailers_, data, len);
}
void Downstream::set_response_http_status(unsigned int status) {
response_http_status_ = status;
}
void Downstream::set_response_major(int major) { response_major_ = major; }
void Downstream::set_response_minor(int minor) { response_minor_ = minor; }
int Downstream::get_response_major() const { return response_major_; }
int Downstream::get_response_minor() const { return response_minor_; }
int Downstream::get_response_version() const {
return response_major_ * 100 + response_minor_;
(*hd).value = std::move(new_uri);
}
bool Downstream::get_chunked_response() const { return chunked_response_; }
void Downstream::set_chunked_response(bool f) { chunked_response_ = f; }
bool Downstream::get_response_connection_close() const {
return response_connection_close_;
}
void Downstream::set_response_connection_close(bool f) {
response_connection_close_ = f;
}
int Downstream::on_read() {
if (!dconn_) {
DLOG(INFO, this) << "dconn_ is NULL";
@ -826,14 +645,6 @@ int64_t Downstream::get_response_sent_bodylen() const {
return response_sent_bodylen_;
}
int64_t Downstream::get_response_content_length() const {
return response_content_length_;
}
void Downstream::set_response_content_length(int64_t len) {
response_content_length_ = len;
}
bool Downstream::validate_request_bodylen() const {
if (req_.fs.content_length == -1) {
return true;
@ -852,14 +663,14 @@ bool Downstream::validate_request_bodylen() const {
}
bool Downstream::validate_response_bodylen() const {
if (!expect_response_body() || response_content_length_ == -1) {
if (!expect_response_body() || resp_.fs.content_length == -1) {
return true;
}
if (response_content_length_ != response_bodylen_) {
if (resp_.fs.content_length != response_bodylen_) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, this) << "response invalid bodylen: content-length="
<< response_content_length_
<< resp_.fs.content_length
<< ", received=" << response_bodylen_;
}
return false;
@ -874,12 +685,12 @@ int32_t Downstream::get_priority() const { return priority_; }
void Downstream::check_upgrade_fulfilled() {
if (req_.method == HTTP_CONNECT) {
upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300;
upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
return;
}
if (response_http_status_ == 101) {
if (resp_.http_status == 101) {
// TODO Do more strict checking for upgrade headers
upgraded_ = req_.upgrade_request;
@ -921,23 +732,23 @@ void Downstream::inspect_http1_request() {
}
void Downstream::inspect_http1_response() {
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];
if (idx != -1) {
response_content_length_ = -1;
if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) {
auto transfer_encoding = resp_.fs.header(http2::HD_TRANSFER_ENCODING);
if (transfer_encoding) {
resp_.fs.content_length = -1;
if (util::strifind(transfer_encoding->value.c_str(), "chunked")) {
chunked_response_ = true;
}
}
}
void Downstream::reset_response() {
response_http_status_ = 0;
response_major_ = 1;
response_minor_ = 1;
resp_.http_status = 0;
resp_.http_major = 1;
resp_.http_minor = 1;
}
bool Downstream::get_non_final_response() const {
return !upgraded_ && response_http_status_ / 100 == 1;
return !upgraded_ && resp_.http_status / 100 == 1;
}
bool Downstream::get_upgraded() const { return upgraded_; }
@ -1004,24 +815,7 @@ size_t Downstream::get_response_datalen() const { return response_datalen_; }
void Downstream::reset_response_datalen() { response_datalen_ = 0; }
bool Downstream::expect_response_body() const {
return http2::expect_response_body(req_.method, response_http_status_);
}
namespace {
bool pseudo_header_allowed(const Headers &headers) {
if (headers.empty()) {
return true;
}
return headers.back().name.c_str()[0] == ':';
}
} // namespace
bool Downstream::response_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(response_headers_)) {
return false;
}
return http2::check_http2_response_pseudo_header(response_hdidx_, token);
return http2::expect_response_body(req_.method, resp_.http_status);
}
namespace {
@ -1136,7 +930,7 @@ void Downstream::disable_downstream_wtimer() {
disable_timer(loop, &downstream_wtimer_);
}
bool Downstream::accesslog_ready() const { return response_http_status_ > 0; }
bool Downstream::accesslog_ready() const { return resp_.http_status > 0; }
void Downstream::add_retry() { ++num_retry_; }
@ -1176,7 +970,7 @@ BlockedLink *Downstream::detach_blocked_link() {
bool Downstream::can_detach_downstream_connection() const {
return dconn_ && response_state_ == Downstream::MSG_COMPLETE &&
request_state_ == Downstream::MSG_COMPLETE && !upgraded_ &&
!response_connection_close_;
!resp_.connection_close;
}
DefaultMemchunks Downstream::pop_response_buf() {

View File

@ -73,6 +73,7 @@ public:
// 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;
@ -156,6 +157,18 @@ struct Request {
bool http2_expect_body;
};
struct Response {
Response()
: fs(32), http_status(0), http_major(1), http_minor(1),
connection_close(false) {}
FieldStore fs;
// HTTP status code
unsigned int http_status;
int http_major, http_minor;
bool connection_close;
};
class Downstream {
public:
Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id,
@ -251,58 +264,17 @@ public:
bool get_request_pending() const;
// Returns true if request is ready to be submitted to downstream.
bool request_submission_ready() const;
// downstream response API
const Headers &get_response_headers() const;
Headers &get_response_headers();
// Lower the response header field names and indexes response
// headers. If there are invalid headers (e.g., multiple
// Content-Length with different values), returns -1.
int index_response_headers();
// Returns pointer to the response header with the name |name|. If
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.
// This function must be called after response headers are indexed.
const Headers::value_type *get_response_header(int16_t token) const;
Headers::value_type *get_response_header(int16_t token);
const Response &response() const { return resp_; }
Response &response() { return resp_; }
// Rewrites the location response header field.
void rewrite_location_response_header(const std::string &upstream_scheme);
void add_response_header(std::string name, std::string value);
void set_last_response_header_value(const char *data, size_t len);
void add_response_header(std::string name, std::string value, int16_t token);
void add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token);
bool get_response_header_key_prev() const;
void append_last_response_header_key(const char *data, size_t len);
void append_last_response_header_value(const char *data, size_t len);
// Empties response headers.
void clear_response_headers();
size_t get_response_headers_sum() const;
const Headers &get_response_trailers() const;
void add_response_trailer(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int16_t token);
void add_response_trailer(std::string name, std::string value);
void set_last_response_trailer_value(const char *data, size_t len);
bool get_response_trailer_key_prev() const;
void append_last_response_trailer_key(const char *data, size_t len);
void append_last_response_trailer_value(const char *data, size_t len);
unsigned int get_response_http_status() const;
void set_response_http_status(unsigned int status);
void set_response_major(int major);
void set_response_minor(int minor);
int get_response_major() const;
int get_response_minor() const;
int get_response_version() const;
bool get_chunked_response() const;
void set_chunked_response(bool f);
bool get_response_connection_close() const;
void set_response_connection_close(bool f);
void set_response_state(int state);
int get_response_state() const;
DefaultMemchunks *get_response_buf();
@ -311,8 +283,6 @@ public:
int64_t get_response_bodylen() const;
void add_response_sent_bodylen(size_t amount);
int64_t get_response_sent_bodylen() const;
int64_t get_response_content_length() const;
void set_response_content_length(int64_t len);
// Validates that received response body length and content-length
// matches.
bool validate_response_bodylen() const;
@ -331,7 +301,6 @@ public:
void dec_response_datalen(size_t len);
size_t get_response_datalen() const;
void reset_response_datalen();
bool response_pseudo_header_allowed(int16_t token) const;
// Call this method when there is incoming data in downstream
// connection.
@ -402,12 +371,7 @@ public:
private:
Request req_;
Headers response_headers_;
// trailer part. For HTTP/1.1, trailer part is only included with
// chunked encoding. For HTTP/2, there is no such limit.
Headers response_trailers_;
Response resp_;
std::chrono::high_resolution_clock::time_point request_start_time_;
@ -434,17 +398,12 @@ private:
// the length of response body sent to upstream client
int64_t response_sent_bodylen_;
// content-length of response body, -1 if it is unknown.
int64_t response_content_length_;
Upstream *upstream_;
std::unique_ptr<DownstreamConnection> dconn_;
// only used by HTTP/2 or SPDY upstream
BlockedLink *blocked_link_;
size_t response_headers_sum_;
// The number of bytes not consumed by the application yet.
size_t request_datalen_;
size_t response_datalen_;
@ -460,26 +419,17 @@ private:
uint32_t response_rst_stream_error_code_;
int request_state_;
int response_state_;
unsigned int response_http_status_;
int response_major_;
int response_minor_;
// only used by HTTP/2 or SPDY upstream
int dispatch_state_;
http2::HeaderIndex response_hdidx_;
// true if the connection is upgraded (HTTP Upgrade or CONNECT)
bool upgraded_;
bool chunked_request_;
bool chunked_response_;
bool response_connection_close_;
bool response_header_key_prev_;
bool response_trailer_key_prev_;
bool expect_final_response_;
// true if downstream request is pending because backend connection
// has not been established or should be checked before use;

View File

@ -32,17 +32,17 @@
namespace shrpx {
void test_downstream_index_request_headers(void) {
Request req;
req.fs.add_header("1", "0");
req.fs.add_header("2", "1");
req.fs.add_header("Charlie", "2");
req.fs.add_header("Alpha", "3");
req.fs.add_header("Delta", "4");
req.fs.add_header("BravO", "5");
req.fs.add_header(":method", "6");
req.fs.add_header(":authority", "7");
req.fs.index_headers();
void test_downstream_field_store_index_headers(void) {
FieldStore fs(0);
fs.add_header("1", "0");
fs.add_header("2", "1");
fs.add_header("Charlie", "2");
fs.add_header("Alpha", "3");
fs.add_header("Delta", "4");
fs.add_header("BravO", "5");
fs.add_header(":method", "6");
fs.add_header(":authority", "7");
fs.index_headers();
auto ans = Headers{{"1", "0"},
{"2", "1"},
@ -52,49 +52,23 @@ void test_downstream_index_request_headers(void) {
{"bravo", "5"},
{":method", "6"},
{":authority", "7"}};
CU_ASSERT(ans == req.fs.headers());
CU_ASSERT(ans == fs.headers());
}
void test_downstream_index_response_headers(void) {
Downstream d(nullptr, nullptr, 0, 0);
d.add_response_header("Charlie", "0");
d.add_response_header("Alpha", "1");
d.add_response_header("Delta", "2");
d.add_response_header("BravO", "3");
d.index_response_headers();
auto ans =
Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}};
CU_ASSERT(ans == d.get_response_headers());
}
void test_downstream_get_request_header(void) {
Request req;
req.fs.add_header("alpha", "0");
req.fs.add_header(":authority", "1");
req.fs.add_header("content-length", "2");
req.fs.index_headers();
void test_downstream_field_store_header(void) {
FieldStore fs(0);
fs.add_header("alpha", "0");
fs.add_header(":authority", "1");
fs.add_header("content-length", "2");
fs.index_headers();
// By token
CU_ASSERT(Header(":authority", "1") == *req.fs.header(http2::HD__AUTHORITY));
CU_ASSERT(nullptr == req.fs.header(http2::HD__METHOD));
CU_ASSERT(Header(":authority", "1") == *fs.header(http2::HD__AUTHORITY));
CU_ASSERT(nullptr == fs.header(http2::HD__METHOD));
// By name
CU_ASSERT(Header("alpha", "0") == *req.fs.header("alpha"));
CU_ASSERT(nullptr == req.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));
CU_ASSERT(Header("alpha", "0") == *fs.header("alpha"));
CU_ASSERT(nullptr == fs.header("bravo"));
}
void test_downstream_crumble_request_cookie(void) {
@ -154,24 +128,26 @@ void test_downstream_rewrite_location_response_header(void) {
{
Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
auto &resp = d.response();
d.set_request_downstream_host("localhost:3000");
req.fs.add_header("host", "localhost");
d.add_response_header("location", "http://localhost:3000/");
resp.fs.add_header("location", "http://localhost:3000/");
req.fs.index_headers();
d.index_response_headers();
resp.fs.index_headers();
d.rewrite_location_response_header("https");
auto location = d.get_response_header(http2::HD_LOCATION);
auto location = resp.fs.header(http2::HD_LOCATION);
CU_ASSERT("https://localhost/" == (*location).value);
}
{
Downstream d(nullptr, nullptr, 0, 0);
auto &req = d.request();
auto &resp = d.response();
d.set_request_downstream_host("localhost");
req.authority = "localhost";
d.add_response_header("location", "http://localhost:3000/");
d.index_response_headers();
resp.fs.add_header("location", "http://localhost:3000/");
resp.fs.index_headers();
d.rewrite_location_response_header("https");
auto location = d.get_response_header(http2::HD_LOCATION);
auto location = resp.fs.header(http2::HD_LOCATION);
CU_ASSERT("https://localhost/" == (*location).value);
}
}

View File

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

View File

@ -752,6 +752,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0;
}
auto &resp = downstream->response();
switch (frame->hd.type) {
case NGHTTP2_HEADERS: {
auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
@ -759,15 +761,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (trailer) {
// just store header fields for trailer part
downstream->add_response_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
resp.fs.add_trailer(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
return 0;
}
auto token = http2::lookup_token(name, namelen);
downstream->add_response_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
resp.fs.add_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
case NGHTTP2_PUSH_PROMISE: {
@ -857,18 +859,19 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
auto &resp = downstream->response();
auto &nva = downstream->get_response_headers();
auto &nva = resp.fs.headers();
downstream->set_expect_final_response(false);
auto status = downstream->get_response_header(http2::HD__STATUS);
auto status = resp.fs.header(http2::HD__STATUS);
// libnghttp2 guarantees this exists and can be parsed
auto status_code = http2::parse_http_status_code(status->value);
downstream->set_response_http_status(status_code);
downstream->set_response_major(2);
downstream->set_response_minor(0);
resp.http_status = status_code;
resp.http_major = 2;
resp.http_minor = 0;
if (LOG_ENABLED(INFO)) {
std::stringstream ss;
@ -904,7 +907,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
downstream->check_upgrade_fulfilled();
if (downstream->get_upgraded()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
// On upgrade sucess, both ends can send data
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
// If resume_read fails, just drop connection. Not ideal.
@ -917,27 +920,24 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
<< "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
}
} else {
auto content_length =
downstream->get_response_header(http2::HD_CONTENT_LENGTH);
auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH);
if (content_length) {
// libnghttp2 guarantees this can be parsed
auto len = util::parse_uint(content_length->value);
downstream->set_response_content_length(len);
resp.fs.content_length = util::parse_uint(content_length->value);
}
if (downstream->get_response_content_length() == -1 &&
downstream->expect_response_body()) {
if (resp.fs.content_length == -1 && downstream->expect_response_body()) {
// Here we have response body but Content-Length is not known in
// advance.
if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
// We simply close connection for pre-HTTP/1.1 in this case.
downstream->set_response_connection_close(true);
resp.connection_close = true;
} else {
// Otherwise, use chunked encoding to keep upstream connection
// open. In HTTP2, we are supporsed not to receive
// transfer-encoding.
downstream->add_response_header("transfer-encoding", "chunked",
http2::HD_TRANSFER_ENCODING);
resp.fs.add_header("transfer-encoding", "chunked",
http2::HD_TRANSFER_ENCODING);
downstream->set_chunked_response(true);
}
}

View File

@ -1242,6 +1242,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
assert(body);
auto dconn = downstream->get_downstream_connection();
const auto &resp = downstream->response();
if (body->rleft() == 0 && dconn &&
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
@ -1263,7 +1264,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
if (!downstream->get_upgraded()) {
auto &trailers = downstream->get_response_trailers();
const auto &trailers = resp.fs.trailers();
if (!trailers.empty()) {
std::vector<nghttp2_nv> nva;
nva.reserve(trailers.size());
@ -1303,18 +1304,19 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
data_prd_ptr = &data_prd;
}
auto &headers = downstream->get_response_headers();
const auto &resp = downstream->response();
const auto &headers = resp.fs.headers();
auto nva = std::vector<nghttp2_nv>();
// 2 for :status and server
nva.reserve(2 + headers.size());
std::string status_code_str;
auto response_status_const =
http2::stringify_status(downstream->get_response_http_status());
auto response_status_const = http2::stringify_status(resp.http_status);
if (response_status_const) {
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
} else {
status_code_str = util::utos(downstream->get_response_http_status());
status_code_str = util::utos(resp.http_status);
nva.push_back(http2::make_nv_ls(":status", status_code_str));
}
@ -1334,7 +1336,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index));
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
nva.push_back(
http2::make_nv_lc_nocopy("server", get_config()->server_name));
}
@ -1359,8 +1361,10 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
int Http2Upstream::error_reply(Downstream *downstream,
unsigned int status_code) {
int rv;
auto &resp = downstream->response();
auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
resp.http_status = status_code;
auto body = downstream->get_response_buf();
body->append(html.c_str(), html.size());
downstream->set_response_state(Downstream::MSG_COMPLETE);
@ -1429,6 +1433,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
int rv;
const auto &req = downstream->request();
auto &resp = downstream->response();
if (LOG_ENABLED(INFO)) {
if (downstream->get_non_final_response()) {
@ -1462,25 +1467,24 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
}
#endif // HAVE_MRUBY
size_t nheader = downstream->get_response_headers().size();
auto nva = std::vector<nghttp2_nv>();
// 4 means :status and possible server, via and x-http2-push header
// field.
nva.reserve(nheader + 4 + get_config()->add_response_headers.size());
nva.reserve(resp.fs.headers().size() + 4 +
get_config()->add_response_headers.size());
std::string via_value;
std::string response_status;
auto response_status_const =
http2::stringify_status(downstream->get_response_http_status());
auto response_status_const = http2::stringify_status(resp.http_status);
if (response_status_const) {
nva.push_back(http2::make_nv_lc_nocopy(":status", response_status_const));
} else {
response_status = util::utos(downstream->get_response_http_status());
response_status = util::utos(resp.http_status);
nva.push_back(http2::make_nv_ls(":status", response_status));
}
if (downstream->get_non_final_response()) {
http2::copy_headers_to_nva(nva, downstream->get_response_headers());
http2::copy_headers_to_nva(nva, resp.fs.headers());
if (LOG_ENABLED(INFO)) {
log_response_headers(downstream, nva);
@ -1490,7 +1494,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
downstream->get_stream_id(), nullptr,
nva.data(), nva.size(), nullptr);
downstream->clear_response_headers();
resp.fs.clear_headers();
if (rv != 0) {
ULOG(FATAL, this) << "nghttp2_submit_headers() failed";
@ -1500,19 +1504,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
return 0;
}
http2::copy_headers_to_nva_nocopy(nva, downstream->get_response_headers());
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
nva.push_back(
http2::make_nv_lc_nocopy("server", get_config()->server_name));
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
nva.push_back(http2::make_nv_ls_nocopy("server", (*server).value));
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
@ -1522,8 +1526,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
via_value = (*via).value;
via_value += ", ";
}
via_value += http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor());
via_value +=
http::create_via_header_value(resp.http_major, resp.http_minor);
nva.push_back(http2::make_nv_ls("via", via_value));
}
@ -1575,9 +1579,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
nghttp2_session_get_remote_settings(session_,
NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 &&
!get_config()->http2_proxy && !get_config()->client_proxy &&
(downstream->get_stream_id() % 2) &&
downstream->get_response_header(http2::HD_LINK) &&
downstream->get_response_http_status() == 200 &&
(downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) &&
resp.http_status == 200 &&
(req.method == HTTP_GET || req.method == HTTP_POST)) {
if (prepare_push_promise(downstream) != 0) {
@ -1619,9 +1622,11 @@ int Http2Upstream::on_downstream_body_complete(Downstream *downstream) {
DLOG(INFO, downstream) << "HTTP response completed";
}
auto &resp = downstream->response();
if (!downstream->validate_response_bodylen()) {
rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_connection_close(true);
resp.connection_close = true;
return 0;
}
@ -1762,13 +1767,14 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
size_t baselen;
const auto &req = downstream->request();
const auto &resp = downstream->response();
rv = http2::get_pure_path_component(&base, &baselen, req.path);
if (rv != 0) {
return 0;
}
for (auto &kv : downstream->get_response_headers()) {
for (auto &kv : resp.fs.headers()) {
if (kv.token != http2::HD_LINK) {
continue;
}

View File

@ -54,9 +54,10 @@ void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
auto downstream = dconn->get_downstream();
auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
auto &resp = downstream->response();
// Do this so that dconn is not pooled
downstream->set_response_connection_close(true);
resp.connection_close = true;
if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) {
delete handler;
@ -482,13 +483,14 @@ int htp_hdrs_completecb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream();
const auto &req = downstream->request();
auto &resp = downstream->response();
int rv;
downstream->set_response_http_status(htp->status_code);
downstream->set_response_major(htp->http_major);
downstream->set_response_minor(htp->http_minor);
resp.http_status = htp->status_code;
resp.http_major = htp->http_major;
resp.http_minor = htp->http_minor;
if (downstream->index_response_headers() != 0) {
if (resp.fs.index_headers() != 0) {
downstream->set_response_state(Downstream::MSG_BAD_HEADER);
return -1;
}
@ -500,7 +502,7 @@ int htp_hdrs_completecb(http_parser *htp) {
if (downstream->get_non_final_response()) {
// Reset content-length because we reuse same Downstream for the
// next response.
downstream->set_response_content_length(-1);
resp.fs.content_length = -1;
// For non-final response code, we just call
// on_downstream_header_complete() without changing response
// state.
@ -514,13 +516,13 @@ int htp_hdrs_completecb(http_parser *htp) {
return 1;
}
downstream->set_response_connection_close(!http_should_keep_alive(htp));
resp.connection_close = !http_should_keep_alive(htp);
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->inspect_http1_response();
if (downstream->get_upgraded()) {
// content-length must be ignored for upgraded connection.
downstream->set_response_content_length(-1);
downstream->set_response_connection_close(true);
resp.fs.content_length = -1;
resp.connection_close = true;
// transfer-encoding not applied to upgraded connection
downstream->set_chunked_response(false);
}
@ -540,7 +542,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
}
unsigned int status = downstream->get_response_http_status();
auto status = resp.http_status;
// Ignore the response body. HEAD response may contain
// Content-Length or Transfer-Encoding: chunked. Some server send
// 304 status code with nonzero Content-Length, but without response
@ -559,18 +561,20 @@ int htp_hdrs_completecb(http_parser *htp) {
namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response();
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) {
downstream->append_last_response_header_key(data, len);
if (resp.fs.header_key_prev()) {
resp.fs.append_last_header_key(data, len);
} else {
downstream->add_response_header(std::string(data, len), "");
resp.fs.add_header(std::string(data, len), "");
}
} else {
// trailer part
if (downstream->get_response_trailer_key_prev()) {
downstream->append_last_response_trailer_key(data, len);
if (resp.fs.trailer_key_prev()) {
resp.fs.append_last_trailer_key(data, len);
} else {
downstream->add_response_trailer(std::string(data, len), "");
resp.fs.add_trailer(std::string(data, len), "");
}
}
return 0;
@ -580,18 +584,12 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data);
auto &resp = downstream->response();
if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) {
downstream->set_last_response_header_value(data, len);
} else {
downstream->append_last_response_header_value(data, len);
}
resp.fs.append_last_header_value(data, len);
} else {
if (downstream->get_response_trailer_key_prev()) {
downstream->set_last_response_trailer_value(data, len);
} else {
downstream->append_last_response_trailer_value(data, len);
}
resp.fs.append_last_trailer_value(data, len);
}
return 0;
}

View File

@ -247,6 +247,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
auto downstream = upstream->get_downstream();
auto &req = downstream->request();
auto &resp = downstream->response();
req.http_major = htp->http_major;
req.http_minor = htp->http_minor;
@ -325,7 +326,7 @@ int htp_hdrs_completecb(http_parser *htp) {
auto mruby_ctx = worker->get_mruby_context();
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
downstream->set_response_http_status(500);
resp.http_status = 500;
return -1;
}
#endif // HAVE_MRUBY
@ -514,7 +515,7 @@ int HttpsUpstream::on_read() {
if (htperr == HPE_INVALID_METHOD) {
status_code = 501;
} else if (downstream) {
status_code = downstream->get_response_http_status();
status_code = downstream->response().http_status;
if (status_code == 0) {
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
status_code = 503;
@ -558,6 +559,7 @@ int HttpsUpstream::on_write() {
auto dconn = downstream->get_downstream_connection();
auto output = downstream->get_response_buf();
const auto &resp = downstream->response();
if (output->rleft() == 0 && dconn &&
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
@ -578,7 +580,7 @@ int HttpsUpstream::on_write() {
// We need to postpone detachment until all data are sent so that
// we can notify nghttp2 library all data consumed.
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if (downstream->get_response_connection_close() ||
if (resp.connection_close ||
downstream->get_request_state() != Downstream::MSG_COMPLETE) {
// Connection close
downstream->pop_downstream_connection();
@ -754,30 +756,30 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
size_t bodylen) {
const auto &req = downstream->request();
auto &resp = downstream->response();
auto connection_close = false;
if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
connection_close = true;
} else {
auto c = downstream->get_response_header(http2::HD_CONNECTION);
auto c = resp.fs.header(http2::HD_CONNECTION);
if (c && util::strieq_l("close", c->value)) {
connection_close = true;
}
}
if (connection_close) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
handler_->set_should_close_after_write(true);
}
auto output = downstream->get_response_buf();
output->append("HTTP/1.1 ");
output->append(
http2::get_status_string(downstream->get_response_http_status()));
output->append(http2::get_status_string(resp.http_status));
output->append("\r\n");
for (auto &kv : downstream->get_response_headers()) {
for (auto &kv : resp.fs.headers()) {
if (kv.name.empty() || kv.name[0] == ':') {
continue;
}
@ -787,7 +789,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
output->append("\r\n");
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
output->append("Server: ");
output->append(get_config()->server_name,
strlen(get_config()->server_name));
@ -814,10 +816,12 @@ void HttpsUpstream::error_reply(unsigned int status_code) {
downstream = get_downstream();
}
downstream->set_response_http_status(status_code);
auto &resp = downstream->response();
resp.http_status = status_code;
// we are going to close connection for both frontend and backend in
// error condition. This is safest option.
downstream->set_response_connection_close(true);
resp.connection_close = true;
handler_->set_should_close_after_write(true);
auto output = downstream->get_response_buf();
@ -883,6 +887,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
const auto &req = downstream->request();
auto &resp = downstream->response();
#ifdef HAVE_MRUBY
if (!downstream->get_non_final_response()) {
@ -909,7 +914,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append(".");
buf->append(util::utos(req.http_minor));
buf->append(" ");
buf->append(http2::get_status_string(downstream->get_response_http_status()));
buf->append(http2::get_status_string(resp.http_status));
buf->append("\r\n");
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
@ -918,8 +923,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
get_client_handler()->get_upstream_scheme());
}
http2::build_http1_headers_from_headers(buf,
downstream->get_response_headers());
http2::build_http1_headers_from_headers(buf, resp.fs.headers());
if (downstream->get_non_final_response()) {
buf->append("\r\n");
@ -928,7 +932,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
log_response_headers(buf);
}
downstream->clear_response_headers();
resp.fs.clear_headers();
return 0;
}
@ -938,12 +942,12 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
// after graceful shutdown commenced, add connection: close header
// field.
if (worker->get_graceful_shutdown()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
}
// We check downstream->get_response_connection_close() in case when
// the Content-Length is not available.
if (!req.connection_close && !downstream->get_response_connection_close()) {
if (!req.connection_close && !resp.connection_close) {
if (req.http_major <= 0 || req.http_minor <= 0) {
// We add this header for HTTP/1.0 or HTTP/0.9 clients
buf->append("Connection: Keep-Alive\r\n");
@ -953,14 +957,14 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
if (!connect_method && downstream->get_upgraded()) {
auto connection = downstream->get_response_header(http2::HD_CONNECTION);
auto connection = resp.fs.header(http2::HD_CONNECTION);
if (connection) {
buf->append("Connection: ");
buf->append((*connection).value);
buf->append("\r\n");
}
auto upgrade = downstream->get_response_header(http2::HD_UPGRADE);
auto upgrade = resp.fs.header(http2::HD_UPGRADE);
if (upgrade) {
buf->append("Upgrade: ");
buf->append((*upgrade).value);
@ -968,7 +972,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
if (!downstream->get_response_header(http2::HD_ALT_SVC)) {
if (!resp.fs.header(http2::HD_ALT_SVC)) {
// We won't change or alter alt-svc from backend for now
if (!get_config()->altsvcs.empty()) {
buf->append("Alt-Svc: ");
@ -988,7 +992,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append(get_config()->server_name, strlen(get_config()->server_name));
buf->append("\r\n");
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
buf->append("Server: ");
buf->append((*server).value);
@ -996,7 +1000,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
buf->append("Via: ");
@ -1009,8 +1013,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append((*via).value);
buf->append(", ");
}
buf->append(http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor()));
buf->append(
http::create_via_header_value(resp.http_major, resp.http_minor));
buf->append("\r\n");
}
@ -1055,10 +1059,11 @@ int HttpsUpstream::on_downstream_body(Downstream *downstream,
int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
const auto &req = downstream->request();
auto &resp = downstream->response();
if (downstream->get_chunked_response()) {
auto output = downstream->get_response_buf();
auto &trailers = downstream->get_response_trailers();
const auto &trailers = resp.fs.trailers();
if (trailers.empty()) {
output->append("0\r\n\r\n");
} else {
@ -1072,10 +1077,10 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
}
if (!downstream->validate_response_bodylen()) {
downstream->set_response_connection_close(true);
resp.connection_close = true;
}
if (req.connection_close || downstream->get_response_connection_close()) {
if (req.connection_close || resp.connection_close) {
auto handler = get_client_handler();
handler->set_should_close_after_write(true);
}

View File

@ -100,7 +100,7 @@ int MRubyContext::run_app(Downstream *downstream, int phase) {
}
if (data.response_headers_dirty) {
downstream->index_response_headers();
downstream->response().fs.index_headers();
}
return rv;

View File

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

View File

@ -820,10 +820,11 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
data_prd_ptr = &data_prd;
}
auto status_string =
http2::get_status_string(downstream->get_response_http_status());
const auto &resp = downstream->response();
auto &headers = downstream->get_response_headers();
auto status_string = http2::get_status_string(resp.http_status);
const auto &headers = resp.fs.headers();
auto nva = std::vector<const char *>();
// 3 for :status, :version and server
@ -849,7 +850,7 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
nva.push_back(kv.value.c_str());
}
if (!downstream->get_response_header(http2::HD_SERVER)) {
if (!resp.fs.header(http2::HD_SERVER)) {
nva.push_back("server");
nva.push_back(get_config()->server_name);
}
@ -876,8 +877,10 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
int SpdyUpstream::error_reply(Downstream *downstream,
unsigned int status_code) {
int rv;
auto &resp = downstream->response();
auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
resp.http_status = status_code;
auto body = downstream->get_response_buf();
body->append(html.c_str(), html.size());
downstream->set_response_state(Downstream::MSG_COMPLETE);
@ -938,11 +941,13 @@ void SpdyUpstream::remove_downstream(Downstream *downstream) {
// WARNING: Never call directly or indirectly spdylay_session_send or
// spdylay_session_recv. These calls may delete downstream.
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
auto &resp = downstream->response();
if (downstream->get_non_final_response()) {
// SPDY does not support non-final response. We could send it
// with HEADERS and final response in SYN_REPLY, but it is not
// official way.
downstream->clear_response_headers();
resp.fs.clear_headers();
return 0;
}
@ -974,20 +979,20 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
!get_config()->no_location_rewrite) {
downstream->rewrite_location_response_header(req.scheme);
}
size_t nheader = downstream->get_response_headers().size();
// 8 means server, :status, :version and possible via header field.
auto nv = make_unique<const char *[]>(
nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1);
resp.fs.headers().size() * 2 + 8 +
get_config()->add_response_headers.size() * 2 + 1);
size_t hdidx = 0;
std::string via_value;
std::string status_string =
http2::get_status_string(downstream->get_response_http_status());
auto status_string = http2::get_status_string(resp.http_status);
nv[hdidx++] = ":status";
nv[hdidx++] = status_string.c_str();
nv[hdidx++] = ":version";
nv[hdidx++] = "HTTP/1.1";
for (auto &hd : downstream->get_response_headers()) {
for (auto &hd : resp.fs.headers()) {
if (hd.name.empty() || hd.name.c_str()[0] == ':') {
continue;
}
@ -1009,14 +1014,14 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
nv[hdidx++] = "server";
nv[hdidx++] = get_config()->server_name;
} else {
auto server = downstream->get_response_header(http2::HD_SERVER);
auto server = resp.fs.header(http2::HD_SERVER);
if (server) {
nv[hdidx++] = "server";
nv[hdidx++] = server->value.c_str();
}
}
auto via = downstream->get_response_header(http2::HD_VIA);
auto via = resp.fs.header(http2::HD_VIA);
if (get_config()->no_via) {
if (via) {
nv[hdidx++] = "via";
@ -1027,8 +1032,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
via_value = via->value;
via_value += ", ";
}
via_value += http::create_via_header_value(
downstream->get_response_major(), downstream->get_response_minor());
via_value +=
http::create_via_header_value(resp.http_major, resp.http_minor);
nv[hdidx++] = "via";
nv[hdidx++] = via_value.c_str();
}
@ -1086,9 +1091,11 @@ int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) {
DLOG(INFO, downstream) << "HTTP response completed";
}
auto &resp = downstream->response();
if (!downstream->validate_response_bodylen()) {
rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
downstream->set_response_connection_close(true);
resp.connection_close = true;
return 0;
}