diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 2e156f1d..f65b8002 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -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", diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 1bea4e1b..7e7a750d 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -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, }); diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index 44f5db9e..ddb501a1 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -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(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() { diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index b13c27e4..577b53c8 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -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 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; diff --git a/src/shrpx_downstream_test.cc b/src/shrpx_downstream_test.cc index e1673880..fb493457 100644 --- a/src/shrpx_downstream_test.cc +++ b/src/shrpx_downstream_test.cc @@ -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); } } diff --git a/src/shrpx_downstream_test.h b/src/shrpx_downstream_test.h index 67e8f6bd..13b33744 100644 --- a/src/shrpx_downstream_test.h +++ b/src/shrpx_downstream_test.h @@ -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); diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 1ec18175..477aeda2 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -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); } } diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index fd56841f..dc227613 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -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 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(); // 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(); // 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; } diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index cac728b9..fee19826 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -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(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(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(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; } diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 3d9bdc2d..2ca2fe8f 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -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); } diff --git a/src/shrpx_mruby.cc b/src/shrpx_mruby.cc index befd19a2..5b1b3967 100644 --- a/src/shrpx_mruby.cc +++ b/src/shrpx_mruby.cc @@ -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; diff --git a/src/shrpx_mruby_module_response.cc b/src/shrpx_mruby_module_response.cc index 63cc422e..5b88f879 100644 --- a/src/shrpx_mruby_module_response.cc +++ b/src/shrpx_mruby_module_response.cc @@ -49,7 +49,8 @@ namespace { mrb_value response_get_http_version_major(mrb_state *mrb, mrb_value self) { auto data = static_cast(mrb->ud); auto downstream = data->downstream; - return mrb_fixnum_value(downstream->get_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(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(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(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(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(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(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(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(); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 5f112991..0fb7fd59 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -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(); // 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( - 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; }