aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Khlebnikov <khlebnikov@tracto.ai>2025-03-13 22:56:58 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2025-03-13 23:12:53 +0300
commit422642b601155a296cb0a69eb9b1f7ba146ffa49 (patch)
tree32d7c95f2e9953e24fb35f5fa8530ed832c06255
parent00bdf496f47811681a11178c105cbfe9ad5659bc (diff)
downloadydb-422642b601155a296cb0a69eb9b1f7ba146ffa49.tar.gz
yt/core/http: handle end wildcard "/{$}" in http path pattern
Pattern "/path/{$}" matches only "/path/" and "/path". Required for proper directory handling without careless matching everything. Signed-off-by: Konstantin Khlebnikov <khlebnikov@tracto.ai> --- Pull Request resolved: https://github.com/ytsaurus/ytsaurus/pull/1109 commit_hash:096a09ff25498226420f9a5ed40b7b1b91328b7f
-rw-r--r--yt/yt/core/http/server.cpp19
-rw-r--r--yt/yt/core/http/unittests/http_ut.cpp15
2 files changed, 33 insertions, 1 deletions
diff --git a/yt/yt/core/http/server.cpp b/yt/yt/core/http/server.cpp
index 43ca58c795..feaad321da 100644
--- a/yt/yt/core/http/server.cpp
+++ b/yt/yt/core/http/server.cpp
@@ -537,13 +537,30 @@ IServerPtr CreateServer(
////////////////////////////////////////////////////////////////////////////////
+/*!
+ * Path matching semantic is copied from go standard library.
+ * See https://golang.org/pkg/net/http/#ServeMux
+ *
+ * Supported features:
+ * - matching path exactly: "/path/name"
+ * - matching path prefix: "/path/" matches all with prefix "/path/"
+ * - trailing-slash redirection: matching "/path/" implies "/path"
+ * - end of path wildcard: "/path/{$}" matches only "/path/" and "/path"
+ */
void TRequestPathMatcher::Add(const TString& pattern, const IHttpHandlerPtr& handler)
{
if (pattern.empty()) {
THROW_ERROR_EXCEPTION("Empty pattern is invalid");
}
- if (pattern.back() == '/') {
+ if (pattern.EndsWith("/{$}")) {
+ auto withoutWildcard = pattern.substr(0, pattern.size() - 3);
+
+ Exact_[withoutWildcard] = handler;
+ if (withoutWildcard.size() > 1) {
+ Exact_[withoutWildcard.substr(0, withoutWildcard.size() - 1)] = handler;
+ }
+ } else if (pattern.back() == '/') {
Subtrees_[pattern] = handler;
auto withoutSlash = pattern.substr(0, pattern.size() - 1);
diff --git a/yt/yt/core/http/unittests/http_ut.cpp b/yt/yt/core/http/unittests/http_ut.cpp
index 7030a2350f..4849ed0333 100644
--- a/yt/yt/core/http/unittests/http_ut.cpp
+++ b/yt/yt/core/http/unittests/http_ut.cpp
@@ -1377,6 +1377,21 @@ TEST(THttpHandlerMatchingTest, Simple)
EXPECT_EQ(h3.Get(), handlers3->Match(TStringBuf("/a")).Get());
EXPECT_EQ(h2.Get(), handlers3->Match(TStringBuf("/a/")).Get());
EXPECT_EQ(h2.Get(), handlers3->Match(TStringBuf("/a/b")).Get());
+
+ {
+ auto handlers = New<TRequestPathMatcher>();
+ handlers->Add("/{$}", h1);
+ handlers->Add("/a/{$}", h2);
+ handlers->Add("/a/b", h3);
+
+ EXPECT_EQ(h1.Get(), handlers->Match(TStringBuf("/")).Get());
+ EXPECT_EQ(h2.Get(), handlers->Match(TStringBuf("/a")).Get());
+ EXPECT_EQ(h2.Get(), handlers->Match(TStringBuf("/a/")).Get());
+ EXPECT_EQ(h3.Get(), handlers->Match(TStringBuf("/a/b")).Get());
+ EXPECT_FALSE(handlers->Match(TStringBuf("/a/b/")).Get());
+ EXPECT_FALSE(handlers->Match(TStringBuf("/a/c")).Get());
+ EXPECT_FALSE(handlers->Match(TStringBuf("/d")).Get());
+ }
}
////////////////////////////////////////////////////////////////////////////////