nghttpx: Throw away request and response headers when they are done

This commit is contained in:
Tatsuhiro Tsujikawa 2014-06-15 16:14:00 +09:00
parent 86956db27f
commit 94b9c3771d
8 changed files with 135 additions and 102 deletions

View File

@ -58,6 +58,8 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
response_minor_(1),
upgrade_request_(false),
upgraded_(false),
http2_upgrade_seen_(false),
http2_settings_seen_(false),
chunked_request_(false),
request_connection_close_(false),
request_expect_100_continue_(false),
@ -117,34 +119,6 @@ void Downstream::force_resume_read()
}
}
namespace {
void check_header_field(bool *result, const Headers::value_type &item,
const char *name, const char *value)
{
if(util::strieq(item.name.c_str(), name)) {
if(util::strifind(item.value.c_str(), value)) {
*result = true;
}
}
}
} // namespace
namespace {
void check_transfer_encoding_chunked(bool *chunked,
const Headers::value_type &item)
{
return check_header_field(chunked, item, "transfer-encoding", "chunked");
}
} // namespace
namespace {
void check_expect_100_continue(bool *res,
const Headers::value_type& item)
{
return check_header_field(res, item, "expect", "100-continue");
}
} // namespace
namespace {
Headers::const_iterator get_norm_header(const Headers& headers,
const std::string& name)
@ -272,8 +246,6 @@ void Downstream::set_last_request_header_value(std::string value)
request_headers_sum_ += value.size();
Headers::value_type &item = request_headers_.back();
item.value = std::move(value);
check_transfer_encoding_chunked(&chunked_request_, item);
check_expect_100_continue(&request_expect_100_continue_, item);
}
void Downstream::split_add_request_header
@ -307,6 +279,11 @@ void Downstream::append_last_request_header_value(const char *data, size_t len)
item.value.append(data, len);
}
void Downstream::clear_request_headers()
{
Headers().swap(request_headers_);
}
size_t Downstream::get_request_headers_sum() const
{
return request_headers_sum_;
@ -531,8 +508,6 @@ void Downstream::add_response_header(std::string name, std::string value)
response_header_key_prev_ = true;
response_headers_sum_ += name.size() + value.size();
response_headers_.emplace_back(std::move(name), std::move(value));
check_transfer_encoding_chunked(&chunked_response_,
response_headers_.back());
}
void Downstream::set_last_response_header_value(std::string value)
@ -541,7 +516,6 @@ void Downstream::set_last_response_header_value(std::string value)
response_headers_sum_ += value.size();
auto& item = response_headers_.back();
item.value = std::move(value);
check_transfer_encoding_chunked(&chunked_response_, item);
}
void Downstream::split_add_response_header
@ -576,6 +550,11 @@ void Downstream::append_last_response_header_value(const char *data,
item.value.append(data, len);
}
void Downstream::clear_response_headers()
{
Headers().swap(response_headers_);
}
size_t Downstream::get_response_headers_sum() const
{
return response_headers_sum_;
@ -694,14 +673,66 @@ void Downstream::check_upgrade_fulfilled()
{
if(request_method_ == "CONNECT") {
upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300;
} else {
return;
}
if(response_http_status_ == 101) {
// TODO Do more strict checking for upgrade headers
if(response_http_status_ == 101) {
for(auto& hd : request_headers_) {
if(util::strieq("upgrade", hd.name.c_str())) {
upgraded_ = true;
break;
}
upgraded_ = upgrade_request_;
return;
}
}
void Downstream::inspect_http2_request()
{
if(request_method_ == "CONNECT") {
upgrade_request_ = true;
}
}
void Downstream::inspect_http1_request()
{
if(request_method_ == "CONNECT") {
upgrade_request_ = true;
}
for(auto& hd : request_headers_) {
if(!upgrade_request_ && util::strieq("upgrade", hd.name.c_str())) {
// TODO Perform more strict checking for upgrade headers
upgrade_request_ = true;
if(util::streq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID,
hd.value.c_str(), hd.value.size())) {
http2_upgrade_seen_ = true;
}
} else if(!http2_settings_seen_ &&
util::strieq(hd.name.c_str(), "http2-settings")) {
http2_settings_seen_ = true;
http2_settings_ = hd.value;
} else if(!chunked_request_ &&
util::strieq(hd.name.c_str(), "transfer-encoding")) {
if(util::strifind(hd.value.c_str(), "chunked")) {
chunked_request_ = true;
}
} else if(!request_expect_100_continue_ &&
util::strieq(hd.name.c_str(), "expect")) {
if(util::strifind(hd.value.c_str(), "100-continue")) {
request_expect_100_continue_ = true;
}
}
}
}
void Downstream::inspect_http1_response()
{
for(auto& hd : response_headers_) {
if(!chunked_response_ &&
util::strieq(hd.name.c_str(), "transfer-encoding")) {
if(util::strifind(hd.value.c_str(), "chunked")) {
chunked_response_ = true;
}
}
}
@ -712,47 +743,19 @@ bool Downstream::get_upgraded() const
return upgraded_;
}
void Downstream::check_upgrade_request()
{
if(request_method_ == "CONNECT") {
upgrade_request_ = true;
} else {
// TODO Do more strict checking for upgrade headers
for(auto& hd : request_headers_) {
if(util::strieq("upgrade", hd.name.c_str())) {
upgrade_request_ = true;
break;
}
}
}
}
bool Downstream::get_upgrade_request() const
{
return upgrade_request_;
}
bool Downstream::http2_upgrade_request() const
bool Downstream::get_http2_upgrade_request() const
{
if(request_bodylen_ != 0) {
return false;
}
bool upgrade_seen = false;
bool http2_settings_seen = false;
for(auto& hd : request_headers_) {
// For now just check NGHTTP2_CLEARTEXT_PROTO_VERSION_ID in
// Upgrade header field and existence of HTTP2-Settings header
// field.
if(util::strieq(hd.name.c_str(), "upgrade")) {
if(util::strieq(hd.value.c_str(),
NGHTTP2_CLEARTEXT_PROTO_VERSION_ID)) {
upgrade_seen = true;
}
} else if(util::strieq(hd.name.c_str(), "http2-settings")) {
http2_settings_seen = true;
}
}
return upgrade_seen && http2_settings_seen;
return request_bodylen_ == 0 && http2_upgrade_seen_ && http2_settings_seen_;
}
const std::string& Downstream::get_http2_settings() const
{
return http2_settings_;
}
void Downstream::set_downstream_stream_id(int32_t stream_id)

View File

@ -71,16 +71,20 @@ public:
bool get_output_buffer_full();
// Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
void check_upgrade_fulfilled();
// Checks request headers whether the request is upgrade request or
// not.
void check_upgrade_request();
// Returns true if the request is upgrade.
bool get_upgrade_request() const;
// Returns true if the upgrade is succeded as a result of the call
// check_upgrade_fulfilled().
bool get_upgraded() const;
// Inspects HTTP/2 request.
void inspect_http2_request();
// Inspects HTTP/1 request. This checks whether the request is
// upgrade request and tranfer-encoding etc.
void inspect_http1_request();
// Returns true if the request is HTTP Upgrade for HTTP/2
bool http2_upgrade_request() const;
bool get_http2_upgrade_request() const;
// Returns the value of HTTP2-Settings request header field.
const std::string& get_http2_settings() const;
// downstream request API
const Headers& get_request_headers() const;
void crumble_request_cookie();
@ -109,6 +113,8 @@ public:
bool get_request_header_key_prev() const;
void append_last_request_header_key(const char *data, size_t len);
void append_last_request_header_value(const char *data, size_t len);
// Empties request headers.
void clear_request_headers();
size_t get_request_headers_sum() const;
@ -178,6 +184,8 @@ public:
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;
@ -198,6 +206,8 @@ public:
evbuffer* get_response_body_buf();
nghttp2_error_code get_response_rst_stream_error_code() const;
void set_response_rst_stream_error_code(nghttp2_error_code error_code);
// Inspects HTTP/1 response. This checks tranfer-encoding etc.
void inspect_http1_response();
// Call this method when there is incoming data in downstream
// connection.
@ -217,6 +227,8 @@ private:
std::string request_http2_scheme_;
std::string request_http2_authority_;
std::string assembled_request_cookie_;
std::string http2_settings_;
// the length of request body
int64_t request_bodylen_;
@ -252,6 +264,9 @@ private:
// true if the connection is upgraded (HTTP Upgrade or CONNECT)
bool upgraded_;
bool http2_upgrade_seen_;
bool http2_settings_seen_;
bool chunked_request_;
bool request_connection_close_;
bool request_expect_100_continue_;

View File

@ -429,6 +429,9 @@ int Http2DownstreamConnection::push_request_headers()
DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
return -1;
}
downstream_->clear_request_headers();
http2session_->notify();
return 0;
}

View File

@ -938,6 +938,16 @@ int on_response_headers(Http2Session *http2session,
downstream->set_response_major(2);
downstream->set_response_minor(0);
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
for(auto& nv : nva) {
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
}
SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
<< frame->hd.stream_id
<< "\n" << ss.str();
}
auto content_length = http2::get_header(nva, "content-length");
if(!content_length && downstream->get_request_method() != "HEAD" &&
downstream->get_request_method() != "CONNECT") {
@ -956,20 +966,11 @@ int on_response_headers(Http2Session *http2session,
// connection open. In HTTP2, we are supporsed not to
// receive transfer-encoding.
downstream->add_response_header("transfer-encoding", "chunked");
downstream->set_chunked_response(true);
}
}
}
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
for(auto& nv : nva) {
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
}
SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
<< frame->hd.stream_id
<< "\n" << ss.str();
}
auto upstream = downstream->get_upstream();
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->check_upgrade_fulfilled();

View File

@ -112,16 +112,14 @@ int on_stream_close_callback
int Http2Upstream::upgrade_upstream(HttpsUpstream *http)
{
int rv;
std::string settings_payload;
auto downstream = http->get_downstream();
for(auto& hd : downstream->get_request_headers()) {
if(util::strieq(hd.name.c_str(), "http2-settings")) {
auto val = hd.value;
util::to_base64(val);
settings_payload = base64::decode(std::begin(val), std::end(val));
break;
}
}
auto http2_settings = downstream->get_http2_settings();
util::to_base64(http2_settings);
auto settings_payload = base64::decode(std::begin(http2_settings),
std::end(http2_settings));
rv = nghttp2_session_upgrade
(session_,
reinterpret_cast<const uint8_t*>(settings_payload.c_str()),
@ -334,7 +332,7 @@ int on_request_headers(Http2Upstream *upstream,
downstream->set_request_http2_authority(http2::value_to_str(authority));
downstream->set_request_path(http2::value_to_str(path));
downstream->check_upgrade_request();
downstream->inspect_http2_request();
auto dconn = upstream->get_client_handler()->get_downstream_connection();
rv = dconn->attach_downstream(downstream);
@ -1147,6 +1145,9 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
downstream->get_response_http_status(),
downstream);
}
downstream->clear_response_headers();
return 0;
}

View File

@ -252,6 +252,8 @@ int HttpDownstreamConnection::push_request_headers()
&get_config()->downstream_read_timeout,
&get_config()->downstream_write_timeout);
downstream_->clear_request_headers();
return 0;
}
@ -389,6 +391,7 @@ int htp_hdrs_completecb(http_parser *htp)
downstream->set_response_minor(htp->http_minor);
downstream->set_response_connection_close(!http_should_keep_alive(htp));
downstream->set_response_state(Downstream::HEADER_COMPLETE);
downstream->inspect_http1_response();
downstream->check_upgrade_fulfilled();
if(downstream->get_upgraded()) {
downstream->set_response_connection_close(true);

View File

@ -150,7 +150,8 @@ int htp_hdrs_completecb(http_parser *htp)
downstream->set_request_connection_close(!http_should_keep_alive(htp));
downstream->check_upgrade_request();
downstream->inspect_http1_request();
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
ss << downstream->get_request_method() << " "
@ -359,7 +360,7 @@ int HttpsUpstream::on_read()
}
if(handler->get_http2_upgrade_allowed() &&
downstream->http2_upgrade_request()) {
downstream->get_http2_upgrade_request()) {
if(handler->perform_http2_upgrade(this) != 0) {
return -1;
@ -827,6 +828,9 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
upstream_response(this->get_client_handler()->get_ipaddr(),
downstream->get_response_http_status(), downstream);
}
downstream->clear_response_headers();
return 0;
}

View File

@ -204,7 +204,7 @@ void on_ctrl_recv_callback
downstream->set_request_path(path);
}
downstream->check_upgrade_request();
downstream->inspect_http2_request();
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
@ -940,6 +940,9 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
downstream->get_response_http_status(),
downstream);
}
downstream->clear_response_headers();
return 0;
}