Fix HttpService route precedence so Any("*") no longer shadows specific handlers#832
Open
Copilot wants to merge 3 commits into
Open
Fix HttpService route precedence so Any("*") no longer shadows specific handlers#832Copilot wants to merge 3 commits into
Any("*") no longer shadows specific handlers#832Copilot wants to merge 3 commits into
Conversation
Agent-Logs-Url: https://github.com/ithewei/libhv/sessions/1d649b64-5e89-4b6a-9049-5853eec30954 Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
Agent-Logs-Url: https://github.com/ithewei/libhv/sessions/1d649b64-5e89-4b6a-9049-5853eec30954 Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix HttpService route matching for specific handlers
Fix HttpService route precedence so May 18, 2026
Any("*") no longer shadows specific handlers
ithewei
reviewed
May 20, 2026
| } | ||
| return lhs->first < rhs->first; | ||
| }; | ||
| std::sort(restful_iters.begin(), restful_iters.end(), route_compare); |
Owner
There was a problem hiding this comment.
GetRoute的调用频率很高,每次都筛选出restful和wildcard再排序,影响性能,能在AddRoute时就自动分类好,来提高GetRoute效率吗
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR makes HttpService::GetRoute deterministic and precedence-aware so catch-all routes like Any("*") no longer unpredictably shadow more specific handlers, and adds a regression test to lock the behavior.
Changes:
- Added exact-path lookup before pattern matching, with deterministic RESTful-then-wildcard matching order.
- Introduced stable ordering of pattern candidates (longer-first, then lexical) to avoid
unordered_mapiteration effects. - Added a focused regression test executable and integrated it into the unittest CMake targets.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
http/server/HttpService.cpp |
Deterministic route selection with explicit precedence and extracted path-matching helper. |
unittest/http_service_route_test.cpp |
Regression test ensuring /ping beats Any("*") and unknown paths still hit the catch-all. |
unittest/CMakeLists.txt |
Wires the new route precedence regression test into the unittest build. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+124
to
+140
| std::vector<http_path_handlers::const_iterator> restful_iters; | ||
| std::vector<http_path_handlers::const_iterator> wildcard_iters; | ||
| for (auto it = pathHandlers.begin(); it != pathHandlers.end(); ++it) { | ||
| if (it->first.find('*') != std::string::npos) { | ||
| wildcard_iters.push_back(it); | ||
| } else if (is_restful_route(it->first)) { | ||
| restful_iters.push_back(it); | ||
| } | ||
| } | ||
| auto route_compare = [](const http_path_handlers::const_iterator& lhs, const http_path_handlers::const_iterator& rhs) { | ||
| if (lhs->first.size() != rhs->first.size()) { | ||
| return lhs->first.size() > rhs->first.size(); | ||
| } | ||
| return lhs->first < rhs->first; | ||
| }; | ||
| std::sort(restful_iters.begin(), restful_iters.end(), route_compare); | ||
| std::sort(wildcard_iters.begin(), wildcard_iters.end(), route_compare); |
Comment on lines
+111
to
+122
| auto iter = pathHandlers.find(path); | ||
| if (iter != pathHandlers.end()) { | ||
| auto method_handlers = iter->second; | ||
| for (auto mh = method_handlers->begin(); mh != method_handlers->end(); ++mh) { | ||
| if (mh->method == req->method) { | ||
| if (handler) *handler = &mh->handler; | ||
| return 0; | ||
| } | ||
| } | ||
| if (handler) *handler = NULL; | ||
| return HTTP_STATUS_METHOD_NOT_ALLOWED; | ||
| } |
Comment on lines
+28
to
+40
| req.path = "/ping"; | ||
| req.method = HTTP_GET; | ||
| if (service.GetRoute(&req, &handler) != 0 || handler == NULL || handler->sync_handler(&req, &resp) != 200) { | ||
| fprintf(stderr, "specific route should win over '*' catch-all\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| req.path = "/not-found"; | ||
| req.method = HTTP_GET; | ||
| if (service.GetRoute(&req, &handler) != 0 || handler == NULL || handler->sync_handler(&req, &resp) != 418) { | ||
| fprintf(stderr, "catch-all should match unknown path\n"); | ||
| return -2; | ||
| } |
| @@ -0,0 +1,43 @@ | |||
| #include <stdio.h> | |||
| service.Any("*", fallback_handler); | ||
| service.GET("/ping", specific_handler); | ||
|
|
||
| http_handler* handler = NULL; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HttpService::GetRoute(HttpRequest*, ...)iteratedpathHandlersviastd::unordered_map, so match order was non-deterministic. In practice, a catch-allAny("*")could win before concrete routes (e.g./ping), breaking expected router behavior.Routing precedence in
HttpService::GetRoute(HttpRequest*, ...)pathHandlers.find(path)), with normal method resolution./:id,/{id}) first*) afterRouting matcher extraction
match_route_path) to keep wildcard + RESTful matching behavior consistent while making precedence explicit.Focused regression test
unittest/http_service_route_test.cppand wired it intounittest/CMakeLists.txt./pingis selected overAny("*")