-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
🐛 Fix routing with mount and static #3454
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
Conversation
""" WalkthroughThis change removes route position and route count tracking from the application core and its mounting logic. Associated atomic operations and tests relying on these fields are deleted. Route grouping and static registration are refactored, and a new test is added to verify static file serving works when mounting sub-apps. Changes
Sequence Diagram(s)sequenceDiagram
participant MainApp
participant SubApp
participant StaticServer
participant Client
Client->>MainApp: GET /subApp/public/test.txt
MainApp->>SubApp: Forward request
SubApp->>StaticServer: Serve static file /public/test.txt
StaticServer-->>SubApp: File content or error
SubApp-->>MainApp: Response
MainApp-->>Client: Response
Assessment against linked issues
Suggested reviewers
Poem
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (13)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
// merge global tree routes in current tree stack | ||
tsMap[treePart] = uniqueRouteStack(append(tsMap[treePart], tsMap[""]...)) | ||
// if it's a global route, assign to every bucket | ||
if routePath == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can also combine this IF statement, I only split it up for readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
mount.go (1)
177-178
: Remove/format leftover commented code to satisfy linter and avoid noiseThe commented‐out declaration
//var routePos uint32
has no functional purpose after the route-position logic was removed and is currently triggeringgocritic
(commentFormatting: put a space between // and comment text
).
Either delete the line or keep it properly formatted with a leading space and a short explanation so it does not look like forgotten code.- //var routePos uint32 + // var routePos uint32 // kept for historical reference – delete once route-position tracking is fully removed🧰 Tools
🪛 GitHub Check: lint
[failure] 177-177:
commentFormatting: put a space between//
and comment text (gocritic)router.go (2)
49-57
: Comment style & deadpos
field create maintenance debt
- Comment starts with triple
###
which violates Go comment conventions and will be flagged by linters (gocritic
,golint
).- The
pos
field is now unused since sorting and assignment were deleted. Keeping an unused 8-byte field in everyRoute
instance wastes memory (~0.8 MB per 100 k routes) and confuses future readers.Consider deleting the field completely (plus all mentions) or at least marking it
// deprecated
until it is removed.- // ### important: always keep in sync with the copy method "app.copyRoute" and all creations of Route struct### - pos uint32 // Position in stack -> important for the sort of the matched routes + // NOTE: keep this struct in sync with app.copyRoute and every direct Route initialisation. + // Deprecated – `pos` is no longer used after removal of route-sorting logic. + // pos uint32 // deprecated🧰 Tools
🪛 GitHub Check: lint
[failure] 51-51:
fieldpos
is unused (unused)
451-466
: Duplicate-route merge only checks the last entry – edge cases slip through
addRoute
merges handlers when the immediately preceding route matches (app.stack[m][l-1]
).
If the same route/path was registered earlier but not in the last position, duplication is silently allowed and the stack grows unnecessarily.Consider using a map keyed by
(path,use,mount)
per method or scanning the slice in reverse until the first non-middleware mismatch:- l := len(app.stack[m]) - if l > 0 && app.stack[m][l-1].Path == route.Path && route.use == app.stack[m][l-1].use && !route.mount && !app.stack[m][l-1].mount { + for i := len(app.stack[m]) - 1; i >= 0; i-- { + prev := app.stack[m][i] + if prev.Path == route.Path && prev.use == route.use && !prev.mount && !route.mount { + prev.Handlers = append(prev.Handlers, route.Handlers...) + return + } + }This prevents silent duplication and keeps the stack compact.
router_test.go (1)
514-538
: Strengthen new integration test with content assertion
Test_Router_Mount_n_Static
only checks the status code.
Adding a quick body assertion ensures we’re not getting a 200 with an unexpected body (e.g., the catch-all middleware).resp, err := app.Test(httptest.NewRequest(MethodGet, "/static/style.css", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + +body, err := io.ReadAll(resp.Body) +utils.AssertEqual(t, nil, err, "read body") +utils.AssertEqual(t, true, strings.Contains(string(body), "color"), "static file contents")While at it, you might also assert that
/mount/test
returns 200 to cover the mounted sub-app.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
app.go
(0 hunks)mount.go
(1 hunks)mount_test.go
(0 hunks)router.go
(6 hunks)router_test.go
(3 hunks)
💤 Files with no reviewable changes (2)
- app.go
- mount_test.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
router.go (2)
helpers.go (1)
MethodGet
(819-819)app.go (1)
App
(88-118)
router_test.go (3)
app.go (2)
New
(497-590)Static
(401-444)helpers.go (2)
StatusNotFound
(890-890)MethodGet
(819-819)utils/assertions.go (1)
AssertEqual
(19-68)
🪛 GitHub Check: lint
mount.go
[failure] 177-177:
commentFormatting: put a space between //
and comment text (gocritic)
⏰ Context from checks skipped due to timeout of 90000ms (12)
- GitHub Check: Analyse
- GitHub Check: Build (1.20.x, macos-latest)
- GitHub Check: Build (1.19.x, macos-latest)
- GitHub Check: Build (1.21.x, windows-latest)
- GitHub Check: Build (1.21.x, macos-latest)
- GitHub Check: Build (1.23.x, ubuntu-latest)
- GitHub Check: Build (1.18.x, windows-latest)
- GitHub Check: Build (1.22.x, windows-latest)
- GitHub Check: Build (1.20.x, windows-latest)
- GitHub Check: Build (1.23.x, windows-latest)
- GitHub Check: Build (1.18.x, macos-latest)
- GitHub Check: Compare
🔇 Additional comments (2)
router.go (1)
485-531
: Nested loops in newbuildTree()
may regress startup time for large route setsThe second pass iterates every discovered 3-char bucket (
treePath
) × every route, resulting inO(buckets × routes)
complexity (potentially millions of iterations for 10 k+ routes).A lighter approach:
- Compute each route’s bucket once and append directly (
map[bucket][]*Route
).- After population, copy global routes into every bucket with a single loop.
This keeps the algorithm
O(routes + buckets + routes_global × buckets)
.No immediate change required, but worth profiling when apps register many routes.
router_test.go (1)
477-478
: Nice! Tests run in parallelAdding
t.Parallel()
improves overall test-suite throughput.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes issues where static routes become inaccessible when mounting sub-apps by refactoring route registration, removing legacy position-based tracking, and adding a dedicated test for the mount+static scenario.
- Extracted
cssDir
constant and updated static tests to use it - Removed
routesCount
/pos
fields and adjustedaddRoute
/buildTree
to rely on prefix buckets - Added
Test_Router_Mount_n_Static
to cover combined mount and static routing
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
router_test.go | Introduced cssDir , cleaned up static tests, and added new mount+static test |
router.go | Removed position tracking (pos , routesCount ), refactored addRoute signature and buildTree logic |
mount_test.go | Removed outdated assertions for routesCount and pos |
mount.go | Dropped legacy routesCount /pos adjustments in sub-app processing |
app.go | Removed routesCount field |
Comments suppressed due to low confidence (4)
router_test.go:537
- Consider adding assertions to verify the response body/content of the static file to ensure the correct file is served.
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
router_test.go:515
- This test sets up a mounted subapp but doesn't verify requests to the mounted route (
/mount/test
). Consider adding a request and assertion for the mount endpoint to cover that behavior.
func Test_Router_Mount_n_Static(t *testing.T) {
router.go:450
- The
addRoute
method signature no longer accepts theisMounted
flag, which may cause mounted routes to be treated as non-mounted. Consider restoring the flag or leveragingroute.mount
to preserve mount semantics.
func (app *App) addRoute(method string, route *Route) {
router.go:435
- [nitpick] Explicitly assigning
group: nil
is redundant since the zero value is alreadynil
. You can remove this line to reduce noise.
group: nil,
Fixes #3104 #3442
[Bug]: Static server in sub app does not work #3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442