diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 37293a5e..87c1659e 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -739,41 +739,23 @@ namespace { // HttpDownstreamConnection::push_request_headers(), but vastly // simplified since we only care about absolute URI. std::string construct_absolute_request_uri(Downstream *downstream) { - const char *authority = nullptr, *host = nullptr; - if (!downstream->get_request_http2_authority().empty()) { - authority = downstream->get_request_http2_authority().c_str(); - } - auto h = downstream->get_request_header(http2::HD_HOST); - if (h) { - host = h->value.c_str(); - } - if (!authority && !host) { + auto &authority = downstream->get_request_http2_authority(); + if (authority.empty()) { return downstream->get_request_path(); } std::string uri; - if (downstream->get_request_http2_scheme().empty()) { + auto &scheme = downstream->get_request_http2_scheme(); + if (scheme.empty()) { // We may have to log the request which lacks scheme (e.g., // http/1.1 with origin form). uri += "http://"; } else { - uri += downstream->get_request_http2_scheme(); + uri += scheme; uri += "://"; } - if (authority) { - uri += authority; - } else { - uri += host; - } + uri += authority; + uri += downstream->get_request_path(); - // Server-wide OPTIONS takes following form in proxy request: - // - // OPTIONS http://example.org HTTP/1.1 - // - // Notice that no slash after authority. See - // http://tools.ietf.org/html/rfc7230#section-5.3.4 - if (downstream->get_request_path() != "*") { - uri += downstream->get_request_path(); - } return uri; } } // namespace @@ -787,12 +769,15 @@ void ClientHandler::write_accesslog(Downstream *downstream) { downstream, ipaddr_.c_str(), http2::to_method_string(downstream->get_request_method()), - (downstream->get_request_method() != HTTP_CONNECT && - (get_config()->http2_proxy || get_config()->client_proxy)) - ? construct_absolute_request_uri(downstream).c_str() - : downstream->get_request_path().empty() - ? downstream->get_request_http2_authority().c_str() - : downstream->get_request_path().c_str(), + downstream->get_request_method() == HTTP_CONNECT + ? downstream->get_request_http2_authority().c_str() + : (get_config()->http2_proxy || get_config()->client_proxy) + ? construct_absolute_request_uri(downstream).c_str() + : downstream->get_request_path().empty() + ? downstream->get_request_method() == HTTP_OPTIONS + ? "*" + : "-" + : downstream->get_request_path().c_str(), alpn_.c_str(), nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl), diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 7e7d9f20..86e200f6 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -251,10 +251,10 @@ int Http2DownstreamConnection::push_request_headers() { downstream_->set_request_pending(false); + auto method = downstream_->get_request_method(); auto no_host_rewrite = get_config()->no_host_rewrite || get_config()->http2_proxy || - get_config()->client_proxy || - downstream_->get_request_method() == HTTP_CONNECT; + get_config()->client_proxy || method == HTTP_CONNECT; // http2session_ has already in CONNECTED state, so we can get // addr_idx here. @@ -299,17 +299,23 @@ int Http2DownstreamConnection::push_request_headers() { nva.reserve(nheader + 8 + cookies.size() + get_config()->add_request_headers.size()); - nva.push_back(http2::make_nv_lc( - ":method", http2::to_method_string(downstream_->get_request_method()))); + nva.push_back(http2::make_nv_lc(":method", http2::to_method_string(method))); auto &scheme = downstream_->get_request_http2_scheme(); nva.push_back(http2::make_nv_lc(":authority", authority)); - if (downstream_->get_request_method() != HTTP_CONNECT) { + if (method != HTTP_CONNECT) { assert(!scheme.empty()); + nva.push_back(http2::make_nv_ls(":scheme", scheme)); - nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path())); + + auto &path = downstream_->get_request_path(); + if (method == HTTP_OPTIONS && path.empty()) { + nva.push_back(http2::make_nv_ll(":path", "*")); + } else { + nva.push_back(http2::make_nv_ls(":path", path)); + } } http2::copy_headers_to_nva(nva, downstream_->get_request_headers()); diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 245208e0..5a0e075e 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -299,7 +299,9 @@ int Http2Upstream::on_request_headers(Downstream *downstream, downstream->set_request_http2_authority(http2::value_to_str(authority)); if (path) { - if (get_config()->http2_proxy || get_config()->client_proxy) { + if (method_token == HTTP_OPTIONS && path->value == "*") { + // Server-wide OPTIONS request. Path is empty. + } else if (get_config()->http2_proxy || get_config()->client_proxy) { downstream->set_request_path(http2::value_to_str(path)); } else { auto &value = path->value; diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index b039d4fa..8ce8904f 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -213,7 +213,8 @@ int HttpDownstreamConnection::push_request_headers() { ->downstream_addr_groups[group_] .addrs[addr_idx_] .hostport.get(); - auto connect_method = downstream_->get_request_method() == HTTP_CONNECT; + auto method = downstream_->get_request_method(); + auto connect_method = method == HTTP_CONNECT; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. @@ -232,10 +233,11 @@ int HttpDownstreamConnection::push_request_headers() { downstream_->assemble_request_cookie(); // Assume that method and request path do not contain \r\n. - std::string hdrs = http2::to_method_string(downstream_->get_request_method()); + std::string hdrs = http2::to_method_string(method); hdrs += ' '; auto &scheme = downstream_->get_request_http2_scheme(); + auto &path = downstream_->get_request_path(); if (connect_method) { hdrs += authority; @@ -246,19 +248,12 @@ int HttpDownstreamConnection::push_request_headers() { hdrs += scheme; hdrs += "://"; hdrs += authority; - - // Server-wide OPTIONS takes following form in proxy request: - // - // OPTIONS http://example.org HTTP/1.1 - // - // Notice that no slash after authority. See - // http://tools.ietf.org/html/rfc7230#section-5.3.4 - if (downstream_->get_request_path() != "*") { - hdrs += downstream_->get_request_path(); - } + hdrs += path; + } else if (method == HTTP_OPTIONS && path.empty()) { + // Server-wide OPTIONS + hdrs += "*"; } else { - // No proxy case. - hdrs += downstream_->get_request_path(); + hdrs += path; } hdrs += " HTTP/1.1\r\nHost: "; hdrs += authority; diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index d6ab64e1..60d5e28f 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -224,7 +224,7 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, // // Notice that no slash after authority. See // http://tools.ietf.org/html/rfc7230#section-5.3.4 - downstream->set_request_path("*"); + downstream->set_request_path(""); // we ignore query component here return; } else { @@ -258,10 +258,13 @@ int htp_hdrs_completecb(http_parser *htp) { downstream->set_request_connection_close(!http_should_keep_alive(htp)); + auto method = downstream->get_request_method(); + if (LOG_ENABLED(INFO)) { std::stringstream ss; - ss << http2::to_method_string(downstream->get_request_method()) << " " - << downstream->get_request_path() << " " + ss << http2::to_method_string(method) << " " + << (method == HTTP_CONNECT ? downstream->get_request_http2_authority() + : downstream->get_request_path()) << " " << "HTTP/" << downstream->get_request_major() << "." << downstream->get_request_minor() << "\n"; const auto &headers = downstream->get_request_headers(); @@ -284,13 +287,12 @@ int htp_hdrs_completecb(http_parser *htp) { downstream->inspect_http1_request(); - if (downstream->get_request_method() != HTTP_CONNECT) { + if (method != HTTP_CONNECT) { http_parser_url u{}; // make a copy of request path, since we may set request path // while we are refering to original request path. - auto uri = downstream->get_request_path(); - rv = http_parser_parse_url(uri.c_str(), - downstream->get_request_path().size(), 0, &u); + auto path = downstream->get_request_path(); + rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u); if (rv != 0) { // Expect to respond with 400 bad request return -1; @@ -302,8 +304,12 @@ int htp_hdrs_completecb(http_parser *htp) { return -1; } - downstream->set_request_path( - http2::rewrite_clean_path(std::begin(uri), std::end(uri))); + if (method == HTTP_OPTIONS && path == "*") { + downstream->set_request_path(""); + } else { + downstream->set_request_path( + http2::rewrite_clean_path(std::begin(path), std::end(path))); + } auto host = downstream->get_request_header(http2::HD_HOST); if (host) { @@ -316,7 +322,7 @@ int htp_hdrs_completecb(http_parser *htp) { downstream->set_request_http2_scheme("http"); } } else { - rewrite_request_host_path_from_uri(downstream, uri.c_str(), u); + rewrite_request_host_path_from_uri(downstream, path.c_str(), u); } } @@ -331,6 +337,8 @@ int htp_hdrs_completecb(http_parser *htp) { return -1; } + // mruby hook may change method value + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { return 0; } diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 9a31e033..4f932431 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -227,6 +227,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, downstream->set_request_http2_authority(host->value); if (get_config()->http2_proxy || get_config()->client_proxy) { downstream->set_request_path(path->value); + } else if (method_token == HTTP_OPTIONS && path->value == "*") { + // Server-wide OPTIONS request. Path is empty. } else { downstream->set_request_path(http2::rewrite_clean_path( std::begin(path->value), std::end(path->value)));