nghttpx: Normalize path when setting it to Downstream

This commit is contained in:
Tatsuhiro Tsujikawa 2015-07-11 16:12:35 +09:00
parent d62e4dbc5e
commit 19e47a1922
9 changed files with 74 additions and 29 deletions

View File

@ -334,6 +334,21 @@ std::string normalize_path(InputIt first, InputIt last) {
nullptr, 0); nullptr, 0);
} }
template <typename InputIt>
std::string rewrite_clean_path(InputIt first, InputIt last) {
if (first == last || *first != '/') {
return std::string(first, last);
}
// probably, not necessary most of the case, but just in case.
auto fragment = std::find(first, last, '#');
auto query = std::find(first, fragment, '?');
auto path = normalize_path(first, query);
if (query != fragment) {
path.append(query, fragment);
}
return path;
}
} // namespace http2 } // namespace http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -856,4 +856,29 @@ void test_http2_normalize_path(void) {
CU_ASSERT("/" == http2::normalize_path(std::begin(src), std::end(src))); CU_ASSERT("/" == http2::normalize_path(std::begin(src), std::end(src)));
} }
void test_http2_rewrite_clean_path(void) {
std::string src;
// unreserved characters
src = "/alpha/%62ravo/";
CU_ASSERT("/alpha/bravo/" ==
http2::rewrite_clean_path(std::begin(src), std::end(src)));
// percent-encoding is converted to upper case.
src = "/delta%3a";
CU_ASSERT("/delta%3A" ==
http2::rewrite_clean_path(std::begin(src), std::end(src)));
// path component is normalized before mathcing
src = "/alpha/charlie/%2e././bravo/delta/..";
CU_ASSERT("/alpha/bravo/" ==
http2::rewrite_clean_path(std::begin(src), std::end(src)));
src = "alpha%3a";
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
src = "";
CU_ASSERT(src == http2::rewrite_clean_path(std::begin(src), std::end(src)));
}
} // namespace shrpx } // namespace shrpx

View File

@ -46,6 +46,7 @@ void test_http2_mandatory_request_headers_presence(void);
void test_http2_parse_link_header(void); void test_http2_parse_link_header(void);
void test_http2_path_join(void); void test_http2_path_join(void);
void test_http2_normalize_path(void); void test_http2_normalize_path(void);
void test_http2_rewrite_clean_path(void);
} // namespace shrpx } // namespace shrpx

View File

@ -98,6 +98,8 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) || !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) ||
!CU_add_test(pSuite, "http2_normalize_path", !CU_add_test(pSuite, "http2_normalize_path",
shrpx::test_http2_normalize_path) || shrpx::test_http2_normalize_path) ||
!CU_add_test(pSuite, "http2_rewrite_clean_path",
shrpx::test_http2_rewrite_clean_path) ||
!CU_add_test(pSuite, "downstream_index_request_headers", !CU_add_test(pSuite, "downstream_index_request_headers",
shrpx::test_downstream_index_request_headers) || shrpx::test_downstream_index_request_headers) ||
!CU_add_test(pSuite, "downstream_index_response_headers", !CU_add_test(pSuite, "downstream_index_response_headers",

View File

@ -1438,9 +1438,9 @@ ssize_t match(const std::string &path,
namespace { namespace {
size_t match_downstream_addr_group_host( size_t match_downstream_addr_group_host(
const std::string &host, const std::string &raw_path, const std::string &host, const std::string &path,
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) { const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
if (raw_path == "*") { if (path.empty() || path[0] != '/') {
auto group = match(host + "/", groups); auto group = match(host + "/", groups);
if (group != -1) { if (group != -1) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -1452,11 +1452,6 @@ size_t match_downstream_addr_group_host(
return catch_all; return catch_all;
} }
// probably, not necessary most of the case, but just in case.
auto fragment = std::find(std::begin(raw_path), std::end(raw_path), '#');
auto query = std::find(std::begin(raw_path), fragment, '?');
auto path = http2::normalize_path(std::begin(raw_path), query);
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Perform mapping selection, using host=" << host LOG(INFO) << "Perform mapping selection, using host=" << host
<< ", path=" << path; << ", path=" << path;
@ -1490,13 +1485,21 @@ size_t match_downstream_addr_group_host(
size_t match_downstream_addr_group( size_t match_downstream_addr_group(
const std::string &hostport, const std::string &raw_path, const std::string &hostport, const std::string &raw_path,
const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) { const std::vector<DownstreamAddrGroup> &groups, size_t catch_all) {
if (hostport.empty() || if (std::find(std::begin(hostport), std::end(hostport), '/') !=
std::find(std::begin(hostport), std::end(hostport), '/') != std::end(hostport)) {
std::end(hostport)) {
// We use '/' specially, and if '/' is included in host, it breaks // We use '/' specially, and if '/' is included in host, it breaks
// our code. Select catch-all case. // our code. Select catch-all case.
return catch_all; return catch_all;
} }
auto fragment = std::find(std::begin(raw_path), std::end(raw_path), '#');
auto query = std::find(std::begin(raw_path), fragment, '?');
auto path = std::string(std::begin(raw_path), query);
if (hostport.empty()) {
return match_downstream_addr_group_host(hostport, path, groups, catch_all);
}
std::string host; std::string host;
if (hostport[0] == '[') { if (hostport[0] == '[') {
// assume this is IPv6 numeric address // assume this is IPv6 numeric address
@ -1517,7 +1520,7 @@ size_t match_downstream_addr_group(
} }
util::inp_strlower(host); util::inp_strlower(host);
return match_downstream_addr_group_host(host, raw_path, groups, catch_all); return match_downstream_addr_group_host(host, path, groups, catch_all);
} }
} // namespace shrpx } // namespace shrpx

View File

@ -203,12 +203,8 @@ void test_shrpx_config_match_downstream_addr_group(void) {
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/Alpha/bravo", CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/Alpha/bravo",
groups, 255)); groups, 255));
// unreserved characters are decoded before matching
CU_ASSERT(1 == match_downstream_addr_group("nghttp2.org", "/alpha/%62ravo/",
groups, 255));
CU_ASSERT(1 == match_downstream_addr_group( CU_ASSERT(1 == match_downstream_addr_group(
"nghttp2.org", "/alpha/%62ravo/charlie", groups, 255)); "nghttp2.org", "/alpha/bravo/charlie", groups, 255));
CU_ASSERT(2 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie", CU_ASSERT(2 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie",
groups, 255)); groups, 255));
@ -218,20 +214,13 @@ void test_shrpx_config_match_downstream_addr_group(void) {
CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie/", CU_ASSERT(0 == match_downstream_addr_group("nghttp2.org", "/alpha/charlie/",
groups, 255)); groups, 255));
// percent-encoding is normalized to upper case hex digits.
CU_ASSERT(3 == match_downstream_addr_group("nghttp2.org", "/delta%3a", groups,
255));
// path component is normalized before mathcing
CU_ASSERT(1 == match_downstream_addr_group(
"nghttp2.org", "/alpha/charlie/%2e././bravo/delta/..",
groups, 255));
CU_ASSERT(255 == CU_ASSERT(255 ==
match_downstream_addr_group("example.org", "/", groups, 255)); match_downstream_addr_group("example.org", "/", groups, 255));
CU_ASSERT(255 == match_downstream_addr_group("", "/", groups, 255)); CU_ASSERT(255 == match_downstream_addr_group("", "/", groups, 255));
CU_ASSERT(255 == match_downstream_addr_group("", "alpha", groups, 255));
CU_ASSERT(255 == match_downstream_addr_group("foo/bar", "/", groups, 255)); CU_ASSERT(255 == match_downstream_addr_group("foo/bar", "/", groups, 255));
// If path is "*", only match with host + "/". // If path is "*", only match with host + "/".

View File

@ -300,7 +300,11 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
downstream->set_request_method(method_token); downstream->set_request_method(method_token);
downstream->set_request_http2_scheme(http2::value_to_str(scheme)); downstream->set_request_http2_scheme(http2::value_to_str(scheme));
downstream->set_request_http2_authority(http2::value_to_str(authority)); downstream->set_request_http2_authority(http2::value_to_str(authority));
downstream->set_request_path(http2::value_to_str(path)); if (path) {
auto &value = path->value;
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(value), std::end(value)));
}
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
downstream->set_request_http2_expect_body(true); downstream->set_request_http2_expect_body(true);
@ -541,7 +545,8 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
{nv.value, nv.value + nv.valuelen}); {nv.value, nv.value + nv.valuelen});
break; break;
case http2::HD__PATH: case http2::HD__PATH:
downstream->set_request_path({nv.value, nv.value + nv.valuelen}); downstream->set_request_path(
http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen));
break; break;
} }
downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen,

View File

@ -218,7 +218,8 @@ void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
path += '?'; path += '?';
path.append(uri + fdata.off, fdata.len); path.append(uri + fdata.off, fdata.len);
} }
downstream->set_request_path(std::move(path)); downstream->set_request_path(
http2::rewrite_clean_path(std::begin(path), std::end(path)));
std::string scheme; std::string scheme;
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri); http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
@ -286,6 +287,9 @@ int htp_hdrs_completecb(http_parser *htp) {
return -1; return -1;
} }
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(uri), std::end(uri)));
if (upstream->get_client_handler()->get_ssl()) { if (upstream->get_client_handler()->get_ssl()) {
downstream->set_request_http2_scheme("https"); downstream->set_request_http2_scheme("https");
} else { } else {

View File

@ -229,7 +229,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
} else { } else {
downstream->set_request_http2_scheme(scheme->value); downstream->set_request_http2_scheme(scheme->value);
downstream->set_request_http2_authority(host->value); downstream->set_request_http2_authority(host->value);
downstream->set_request_path(path->value); downstream->set_request_path(http2::rewrite_clean_path(
std::begin(path->value), std::end(path->value)));
} }
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {