Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: clarify the use of "/" as a prefix_rewrite #3160

Merged
merged 11 commits into from
Apr 30, 2018
6 changes: 6 additions & 0 deletions source/common/config/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,11 @@ Grpc::AsyncClientFactoryPtr Utility::factoryForGrpcApiConfigSource(
return async_client_manager.factoryForGrpcService(grpc_service, scope, false);
}

std::string Utility::normalizePath(absl::string_view path) {
std::vector<absl::string_view> segments = StringUtil::splitToken(path, "/", false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: const here and next line

bool has_trailing_slash = path.back() == '/';
return absl::StrCat("/", absl::StrJoin(segments, "/"), has_trailing_slash ? "/" : "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: I feel like this is a pretty heavy hammer from a perf perspective for solving a specific issue (duplicate '/' after rewrite). Could we detect the duplicate '/' and copy strings in a way in which this would perform better? cc @jmarantz who likes these types of things. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Probably most of the time the use-case is to have duplicates '/' at the beginning of the path.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I have functional concerns about this PR first.

}

} // namespace Config
} // namespace Envoy
7 changes: 7 additions & 0 deletions source/common/config/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ class Utility {
factoryForGrpcApiConfigSource(Grpc::AsyncClientManager& async_client_manager,
const envoy::api::v2::core::ApiConfigSource& api_config_source,
Stats::Scope& scope);

/**
* Normalize URI path. Currently it only handles to remove repeated slashes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "Currently it only handles removing repeated slashes."

* @param path supplies the path to be normalized.
* @return std::string the normalized path.
*/
static std::string normalizePath(absl::string_view path);
};

} // namespace Config
Expand Down
3 changes: 2 additions & 1 deletion source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ void RouteEntryImplBase::finalizePathHeader(Http::HeaderMap& headers,
std::string path = headers.Path()->value().c_str();
headers.insertEnvoyOriginalPath().value(*headers.Path());
ASSERT(StringUtil::startsWith(path.c_str(), matched_path, case_sensitive_));
headers.Path()->value(path.replace(0, matched_path.size(), rewrite));
headers.Path()->value(
Envoy::Config::Utility::normalizePath(path.replace(0, matched_path.size(), rewrite)));
}

std::string RouteEntryImplBase::newPath(const Http::HeaderMap& headers) const {
Expand Down
12 changes: 12 additions & 0 deletions test/common/config/utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ TEST(UtilityTest, CheckFilesystemSubscriptionBackingPath) {
Utility::checkFilesystemSubscriptionBackingPath(test_path);
}

TEST(UtilityTest, NormalizePath) {
EXPECT_EQ("/ok/a/b/c", Utility::normalizePath("/ok/a/b/c"));
EXPECT_EQ("/ok/a/b/c", Utility::normalizePath("/ok//a//b//c"));
EXPECT_EQ("/ok/a/b/c", Utility::normalizePath("/ok///a///b///c"));
EXPECT_EQ("/ok/a/b/c", Utility::normalizePath("////ok/a/b/c"));

// Maintain the trailing slash.
EXPECT_EQ("/ok/a/b/c/", Utility::normalizePath("/ok/a/b/c/"));
EXPECT_EQ("/ok/a/b/c/", Utility::normalizePath("///ok/a/b/c///"));
EXPECT_EQ("/ok/a/b/c/", Utility::normalizePath("///ok////a////b////c///"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think normalization should be this aggressive, messing with the slashes in the middle of the path.

I think the beginning of the path is a special case, and "scheme://origin/" should be treated the same as "scheme://origin". But I don't think that URL normalizers usually mess with anything after the "/".

The motivation force in the bug the fixes was really the behavior of replacing prefix "/foo" with "/". WDYT of handling this at the parsing of that command, where a trailing slash is appended to src or dest if they don't have them? Then the swap can be done fairly cleanly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, that's basically my thinking as well.

Copy link
Member Author

@dio dio Apr 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for overthinking it. What do you think of handling this “/“ specifically: treating that as replacing with empty string? https://github.com/dio/envoy/blob/fix-2956/source/common/router/config_impl.cc#L375

}

// TEST(UtilityTest, FactoryForGrpcApiConfigSource) should catch misconfigured
// API configs along the dimension of ApiConfigSource type.
TEST(UtilityTest, FactoryForGrpcApiConfigSource) {
Expand Down
10 changes: 10 additions & 0 deletions test/common/router/config_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3925,6 +3925,8 @@ name: AllRedirects
redirect: { prefix_rewrite: "/new/regex-prefix/" }
- match: { prefix: "/http/prefix"}
redirect: { prefix_rewrite: "/https/prefix" , https_redirect: true }
- match: { prefix: "/ignore-this"}
redirect: { prefix_rewrite: "/" }
)EOF";

NiceMock<Runtime::MockLoader> runtime;
Expand Down Expand Up @@ -3969,6 +3971,14 @@ name: AllRedirects
redirect->rewritePathHeader(headers);
EXPECT_EQ("https://redirect.lyft.com/https/prefix/", redirect->newPath(headers));
}
{
Http::TestHeaderMapImpl headers = genRedirectHeaders(
"redirect.lyft.com", "/ignore-this/however/use/the/rest/of/this/path", false, false);
const DirectResponseEntry* redirect = config.route(headers, 0)->directResponseEntry();
redirect->rewritePathHeader(headers);
EXPECT_EQ("http://redirect.lyft.com/however/use/the/rest/of/this/path",
redirect->newPath(headers));
}
}

// Test to check Strip Query for redirect messages
Expand Down