diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index 83301f53..b8412d23 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -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) diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index 28555014..28a28504 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -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_; diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 96d25002..3e7f2cd8 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -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; } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 95cb69eb..b7fda983 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -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(); diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 3a42f155..284f6239 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -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(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; } diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 36741adf..576e4f3d 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -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); diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index b09f9fcc..5ce8f244 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -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; } diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 9a285b31..2694ea95 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -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; }