diff options
author | Konstantin Khlebnikov <khlebnikov@tracto.ai> | 2025-03-13 22:56:58 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2025-03-13 23:12:53 +0300 |
commit | 422642b601155a296cb0a69eb9b1f7ba146ffa49 (patch) | |
tree | 32d7c95f2e9953e24fb35f5fa8530ed832c06255 | |
parent | 00bdf496f47811681a11178c105cbfe9ad5659bc (diff) | |
download | ydb-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.cpp | 19 | ||||
-rw-r--r-- | yt/yt/core/http/unittests/http_ut.cpp | 15 |
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()); + } } //////////////////////////////////////////////////////////////////////////////// |