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) || shrpx::test_http2_get_pure_path_component) ||
!CU_add_test(pSuite, "http2_construct_push_component", !CU_add_test(pSuite, "http2_construct_push_component",
shrpx::test_http2_construct_push_component) || shrpx::test_http2_construct_push_component) ||
!CU_add_test(pSuite, "downstream_index_request_headers", !CU_add_test(pSuite, "downstream_field_store_index_headers",
shrpx::test_downstream_index_request_headers) || shrpx::test_downstream_field_store_index_headers) ||
!CU_add_test(pSuite, "downstream_index_response_headers", !CU_add_test(pSuite, "downstream_field_store_header",
shrpx::test_downstream_index_response_headers) || shrpx::test_downstream_field_store_header) ||
!CU_add_test(pSuite, "downstream_get_request_header",
shrpx::test_downstream_get_request_header) ||
!CU_add_test(pSuite, "downstream_get_response_header",
shrpx::test_downstream_get_response_header) ||
!CU_add_test(pSuite, "downstream_crumble_request_cookie", !CU_add_test(pSuite, "downstream_crumble_request_cookie",
shrpx::test_downstream_crumble_request_cookie) || shrpx::test_downstream_crumble_request_cookie) ||
!CU_add_test(pSuite, "downstream_assemble_request_cookie", !CU_add_test(pSuite, "downstream_assemble_request_cookie",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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