Skip to content

🐛 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

Merged
merged 6 commits into from
May 16, 2025
Merged

🐛 Fix routing with mount and static #3454

merged 6 commits into from
May 16, 2025

Conversation

ReneWerner87
Copy link
Member

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

[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
@ReneWerner87 ReneWerner87 added this to the Next Release milestone May 15, 2025
@ReneWerner87 ReneWerner87 requested a review from a team as a code owner May 15, 2025 13:43
@ReneWerner87 ReneWerner87 requested a review from gaby May 15, 2025 13:43
@ReneWerner87 ReneWerner87 requested review from sixcolors and efectn May 15, 2025 13:43
Copy link
Contributor

coderabbitai bot commented May 15, 2025

"""

Walkthrough

This 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

File(s) Change Summary
app.go Removed the routesCount field from the App struct.
mount.go Removed routePos variable and all logic related to route positions and route count updating during app mounting.
mount_test.go Deleted assertions checking routesCount and route position (pos) in tests; retained other assertions.
router.go Removed route position tracking, simplified addRoute and copyRoute, refactored route grouping in buildTree, and updated static route registration.
router_test.go Added Test_Router_Mount_n_Static to test static file serving in mounted sub-apps; enabled parallel execution for two existing tests; replaced hardcoded static directory strings with a variable.

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
Loading

Assessment against linked issues

Objective Addressed Explanation
Ensure static file serving works in sub-apps after mounting (#3104)
Remove route position and route count tracking from core and tests (#3104)
Refactor route grouping and static registration logic for clarity and correctness (#3104)

Suggested reviewers

  • gaby
  • sixcolors

Poem

In fields of code where routes once grew,
The counters gone, the paths anew.
Static files now serve with cheer,
Even in sub-apps, far and near!
A bunny hops through logic neat—
Every bug, a tasty treat!
🐇✨
"""

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit 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.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit 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 Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ee047d and a25c30f.

📒 Files selected for processing (1)
  • router_test.go (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • router_test.go
⏰ Context from checks skipped due to timeout of 90000ms (13)
  • GitHub Check: Build (1.23.x, ubuntu-latest)
  • GitHub Check: Build (1.22.x, macos-latest)
  • GitHub Check: Build (1.23.x, macos-latest)
  • GitHub Check: Build (1.23.x, windows-latest)
  • GitHub Check: Build (1.22.x, windows-latest)
  • GitHub Check: Build (1.17.x, macos-latest)
  • GitHub Check: Build (1.20.x, windows-latest)
  • GitHub Check: Build (1.19.x, windows-latest)
  • GitHub Check: Build (1.19.x, macos-latest)
  • GitHub Check: Build (1.21.x, windows-latest)
  • GitHub Check: Build (1.17.x, ubuntu-latest)
  • GitHub Check: Build (1.17.x, windows-latest)
  • GitHub Check: Compare
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

// 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 == "" {
Copy link
Member Author

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 noise

The commented‐out declaration //var routePos uint32 has no functional purpose after the route-position logic was removed and is currently triggering gocritic (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 & dead pos field create maintenance debt

  1. Comment starts with triple ### which violates Go comment conventions and will be flagged by linters (gocritic, golint).
  2. The pos field is now unused since sorting and assignment were deleted. Keeping an unused 8-byte field in every Route 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:
field pos 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

📥 Commits

Reviewing files that changed from the base of the PR and between fccff19 and 83ccba4.

📒 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 new buildTree() may regress startup time for large route sets

The second pass iterates every discovered 3-char bucket (treePath) × every route, resulting in O(buckets × routes) complexity (potentially millions of iterations for 10 k+ routes).

A lighter approach:

  1. Compute each route’s bucket once and append directly (map[bucket][]*Route).
  2. 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 parallel

Adding t.Parallel() improves overall test-suite throughput.

[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
[Bug]: Static server in sub app does not work #3104
#3104
[Bug]: When mounting a subapp with mount, the static route is inaccessible. #3442
#3442
Copy link
Contributor

@Copilot Copilot AI left a 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 adjusted addRoute/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 the isMounted flag, which may cause mounted routes to be treated as non-mounted. Consider restoring the flag or leveraging route.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 already nil. You can remove this line to reduce noise.
group: nil,

@ReneWerner87 ReneWerner87 merged commit 94e30d7 into v2 May 16, 2025
27 checks passed
@ReneWerner87 ReneWerner87 deleted the mountAppWithStatic branch July 30, 2025 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: When mounting a subapp with mount, the static route is inaccessible. 🐛 [Bug]: Static server in sub app does not work
1 participant