nghttpx: Store empty string to path for server-wide OPTIONS request

This change is required to show path attribute to mruby script.  It is
desirable to construct URI from parts.  Just checking method and path
is "*" is awkward.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-09-04 00:14:09 +09:00
parent 02bb2c3e83
commit 200217d8ea
6 changed files with 60 additions and 62 deletions

View File

@ -739,41 +739,23 @@ namespace {
// HttpDownstreamConnection::push_request_headers(), but vastly // HttpDownstreamConnection::push_request_headers(), but vastly
// simplified since we only care about absolute URI. // simplified since we only care about absolute URI.
std::string construct_absolute_request_uri(Downstream *downstream) { std::string construct_absolute_request_uri(Downstream *downstream) {
const char *authority = nullptr, *host = nullptr; auto &authority = downstream->get_request_http2_authority();
if (!downstream->get_request_http2_authority().empty()) { if (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) {
return downstream->get_request_path(); return downstream->get_request_path();
} }
std::string uri; 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., // We may have to log the request which lacks scheme (e.g.,
// http/1.1 with origin form). // http/1.1 with origin form).
uri += "http://"; uri += "http://";
} else { } else {
uri += downstream->get_request_http2_scheme(); uri += scheme;
uri += "://"; uri += "://";
} }
if (authority) {
uri += authority; uri += authority;
} else {
uri += host;
}
// 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(); uri += downstream->get_request_path();
}
return uri; return uri;
} }
} // namespace } // namespace
@ -787,11 +769,14 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
downstream, ipaddr_.c_str(), downstream, ipaddr_.c_str(),
http2::to_method_string(downstream->get_request_method()), http2::to_method_string(downstream->get_request_method()),
(downstream->get_request_method() != HTTP_CONNECT && downstream->get_request_method() == HTTP_CONNECT
(get_config()->http2_proxy || get_config()->client_proxy)) ? downstream->get_request_http2_authority().c_str()
: (get_config()->http2_proxy || get_config()->client_proxy)
? construct_absolute_request_uri(downstream).c_str() ? construct_absolute_request_uri(downstream).c_str()
: downstream->get_request_path().empty() : downstream->get_request_path().empty()
? downstream->get_request_http2_authority().c_str() ? downstream->get_request_method() == HTTP_OPTIONS
? "*"
: "-"
: downstream->get_request_path().c_str(), : downstream->get_request_path().c_str(),
alpn_.c_str(), alpn_.c_str(),

View File

@ -251,10 +251,10 @@ int Http2DownstreamConnection::push_request_headers() {
downstream_->set_request_pending(false); downstream_->set_request_pending(false);
auto method = downstream_->get_request_method();
auto no_host_rewrite = get_config()->no_host_rewrite || auto no_host_rewrite = get_config()->no_host_rewrite ||
get_config()->http2_proxy || get_config()->http2_proxy ||
get_config()->client_proxy || get_config()->client_proxy || method == HTTP_CONNECT;
downstream_->get_request_method() == HTTP_CONNECT;
// http2session_ has already in CONNECTED state, so we can get // http2session_ has already in CONNECTED state, so we can get
// addr_idx here. // addr_idx here.
@ -299,17 +299,23 @@ int Http2DownstreamConnection::push_request_headers() {
nva.reserve(nheader + 8 + cookies.size() + nva.reserve(nheader + 8 + cookies.size() +
get_config()->add_request_headers.size()); get_config()->add_request_headers.size());
nva.push_back(http2::make_nv_lc( nva.push_back(http2::make_nv_lc(":method", http2::to_method_string(method)));
":method", http2::to_method_string(downstream_->get_request_method())));
auto &scheme = downstream_->get_request_http2_scheme(); auto &scheme = downstream_->get_request_http2_scheme();
nva.push_back(http2::make_nv_lc(":authority", authority)); nva.push_back(http2::make_nv_lc(":authority", authority));
if (downstream_->get_request_method() != HTTP_CONNECT) { if (method != HTTP_CONNECT) {
assert(!scheme.empty()); assert(!scheme.empty());
nva.push_back(http2::make_nv_ls(":scheme", scheme)); 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()); http2::copy_headers_to_nva(nva, downstream_->get_request_headers());

View File

@ -299,7 +299,9 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
downstream->set_request_http2_authority(http2::value_to_str(authority)); downstream->set_request_http2_authority(http2::value_to_str(authority));
if (path) { 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)); downstream->set_request_path(http2::value_to_str(path));
} else { } else {
auto &value = path->value; auto &value = path->value;

View File

@ -213,7 +213,8 @@ int HttpDownstreamConnection::push_request_headers() {
->downstream_addr_groups[group_] ->downstream_addr_groups[group_]
.addrs[addr_idx_] .addrs[addr_idx_]
.hostport.get(); .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 // For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless. // case, we use backend server's host nonetheless.
@ -232,10 +233,11 @@ int HttpDownstreamConnection::push_request_headers() {
downstream_->assemble_request_cookie(); downstream_->assemble_request_cookie();
// Assume that method and request path do not contain \r\n. // 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 += ' '; hdrs += ' ';
auto &scheme = downstream_->get_request_http2_scheme(); auto &scheme = downstream_->get_request_http2_scheme();
auto &path = downstream_->get_request_path();
if (connect_method) { if (connect_method) {
hdrs += authority; hdrs += authority;
@ -246,19 +248,12 @@ int HttpDownstreamConnection::push_request_headers() {
hdrs += scheme; hdrs += scheme;
hdrs += "://"; hdrs += "://";
hdrs += authority; hdrs += authority;
hdrs += path;
// Server-wide OPTIONS takes following form in proxy request: } else if (method == HTTP_OPTIONS && path.empty()) {
// // Server-wide OPTIONS
// OPTIONS http://example.org HTTP/1.1 hdrs += "*";
//
// 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();
}
} else { } else {
// No proxy case. hdrs += path;
hdrs += downstream_->get_request_path();
} }
hdrs += " HTTP/1.1\r\nHost: "; hdrs += " HTTP/1.1\r\nHost: ";
hdrs += authority; hdrs += authority;

View File

@ -224,7 +224,7 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
// //
// Notice that no slash after authority. See // Notice that no slash after authority. See
// http://tools.ietf.org/html/rfc7230#section-5.3.4 // http://tools.ietf.org/html/rfc7230#section-5.3.4
downstream->set_request_path("*"); downstream->set_request_path("");
// we ignore query component here // we ignore query component here
return; return;
} else { } else {
@ -258,10 +258,13 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->set_request_connection_close(!http_should_keep_alive(htp)); downstream->set_request_connection_close(!http_should_keep_alive(htp));
auto method = downstream->get_request_method();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
std::stringstream ss; std::stringstream ss;
ss << http2::to_method_string(downstream->get_request_method()) << " " ss << http2::to_method_string(method) << " "
<< downstream->get_request_path() << " " << (method == HTTP_CONNECT ? downstream->get_request_http2_authority()
: downstream->get_request_path()) << " "
<< "HTTP/" << downstream->get_request_major() << "." << "HTTP/" << downstream->get_request_major() << "."
<< downstream->get_request_minor() << "\n"; << downstream->get_request_minor() << "\n";
const auto &headers = downstream->get_request_headers(); const auto &headers = downstream->get_request_headers();
@ -284,13 +287,12 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->inspect_http1_request(); downstream->inspect_http1_request();
if (downstream->get_request_method() != HTTP_CONNECT) { if (method != HTTP_CONNECT) {
http_parser_url u{}; http_parser_url u{};
// make a copy of request path, since we may set request path // make a copy of request path, since we may set request path
// while we are refering to original request path. // while we are refering to original request path.
auto uri = downstream->get_request_path(); auto path = downstream->get_request_path();
rv = http_parser_parse_url(uri.c_str(), rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u);
downstream->get_request_path().size(), 0, &u);
if (rv != 0) { if (rv != 0) {
// Expect to respond with 400 bad request // Expect to respond with 400 bad request
return -1; return -1;
@ -302,8 +304,12 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1; return -1;
} }
if (method == HTTP_OPTIONS && path == "*") {
downstream->set_request_path("");
} else {
downstream->set_request_path( downstream->set_request_path(
http2::rewrite_clean_path(std::begin(uri), std::end(uri))); http2::rewrite_clean_path(std::begin(path), std::end(path)));
}
auto host = downstream->get_request_header(http2::HD_HOST); auto host = downstream->get_request_header(http2::HD_HOST);
if (host) { if (host) {
@ -316,7 +322,7 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->set_request_http2_scheme("http"); downstream->set_request_http2_scheme("http");
} }
} else { } 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; return -1;
} }
// mruby hook may change method value
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0; return 0;
} }

View File

@ -227,6 +227,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
downstream->set_request_http2_authority(host->value); downstream->set_request_http2_authority(host->value);
if (get_config()->http2_proxy || get_config()->client_proxy) { if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(path->value); downstream->set_request_path(path->value);
} else if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty.
} else { } else {
downstream->set_request_path(http2::rewrite_clean_path( downstream->set_request_path(http2::rewrite_clean_path(
std::begin(path->value), std::end(path->value))); std::begin(path->value), std::end(path->value)));