Skip to content

🔥 feat: Add support for DisableAutoRegister of HEAD routes#3817

Merged
ReneWerner87 merged 9 commits intomainfrom
codex/2025-10-24-09-10-22
Oct 29, 2025
Merged

🔥 feat: Add support for DisableAutoRegister of HEAD routes#3817
ReneWerner87 merged 9 commits intomainfrom
codex/2025-10-24-09-10-22

Conversation

@ReneWerner87
Copy link
Member

Summary

  • rename the auto HEAD cleanup helper to pruneAutoHeadRouteLocked and document its purpose
  • note that fasthttp omits bodies for HEAD responses so the copied GET handlers remain safe

Testing

  • make audit (fails: govulncheck cannot reach vuln.go.dev)
  • make generate
  • make betteralign
  • make modernize
  • make format
  • make test

https://chatgpt.com/codex/tasks/task_e_68f8f6f6db7c83268b3ab6a8adabddc8

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds automatic HEAD route generation for GET routes (marked with autoHead), a new config flag Config.DisableHeadAutoRegister, pruning for overridden/removed auto HEAD routes, startup initialization across mounted apps, test updates, and new documentation describing the feature.

Changes

Cohort / File(s) Summary
Configuration & Startup
app.go
Added exported DisableHeadAutoRegister bool (json:"disable_head_auto_register") to Config; call to ensureAutoHeadRoutesLocked() in startupProcess; iterate mounted sub-apps and call subApp.ensureAutoHeadRoutes() for non-root prefixes.
Router core
router.go
Added Route.autoHead bool and preserve it in copyRoute; added pruneAutoHeadRouteLocked(path string), ensureAutoHeadRoutes(), and ensureAutoHeadRoutesLocked(); clone eligible GET routes into HEAD routes (marking autoHead), and prune auto HEADs when HEAD is explicitly added or GET removed.
Tests
router_test.go, app_test.go, mount_test.go
Added TestAutoRegisterHeadRoutes tests and helpers; updated expectations to include HEAD in Allow headers and increased handler/stack counts; tests call startup/init helpers (startupProcess / ensureAutoHeadRoutes) to reflect auto-registered HEAD routes.
Documentation
docs/guide/routing.md, docs/api/fiber.md, docs/whats_new.md
New "Automatic HEAD routes" docs and examples; document DisableHeadAutoRegister option and behavior of generated HEAD routes in route listings and routing scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant Startup as Startup
    participant App as App
    participant Router as Router
    participant Tree as RouteTree

    Startup->>App: startupProcess()
    App->>Router: ensureAutoHeadRoutesLocked()
    Router->>Tree: scan GET routes
    alt DisableHeadAutoRegister == false
        Tree-->>Router: GET route(s) found
        Router->>Tree: clone GET -> HEAD (autoHead=true)
        Tree->>Tree: insert HEAD route (suppress body)
        Router->>Router: update handlersCount & hooks
    end
    Note over App,Router: For mounted sub-apps with non-root prefix
    App->>Router: subApp.ensureAutoHeadRoutes()
    Note over Router,Tree: On add/delete lifecycle
    Router->>Tree: addRoute / deleteRoute
    Router->>Tree: pruneAutoHeadRouteLocked(path) when HEAD added or GET removed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Potential focus areas:

  • router.go: correctness of cloning GET -> HEAD (handler chain order, middleware, params, static handlers) and marking autoHead.
  • pruneAutoHeadRouteLocked interactions with addRoute/deleteRoute and concurrency/locking.
  • app.go startup: mounted sub-app iteration and timing of ensureAutoHeadRoutes invocation.
  • Tests: updated handler/stack counts and Allow header expectations.

Possibly related PRs

Suggested reviewers

  • sixcolors
  • efectn
  • gaby

Poem

🐰 I hop through routes at early light,

I clone each GET but hide the bite.
A tiny flag, an opt-out gate,
Mounted friends get copies straight—
I tidy headers and then delight.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The provided pull request description is largely incomplete compared to the repository's description template. While the description mentions renaming a helper and documenting fasthttp behavior, it fails to comprehensively explain the main feature being added—automatic HEAD route support with a disable option—or justify its purpose and benefits. The description completely omits critical sections from the template including "Changes introduced" with details on documentation updates, "Type of change" selection, the required checklist, and any examples or migration guidance. The description appears to focus only on minor implementation details while missing the substantial feature work documented in the raw summary. The description should be significantly expanded to follow the repository template. It should include a clear problem statement and explain why automatic HEAD route support is being added, detail all "Changes introduced" with sections for documentation updates (docs/api/fiber.md, docs/guide/routing.md, docs/whats_new.md), testing, and examples, select the appropriate "Type of change" (likely "New feature"), and complete the checklist items confirming documentation was updated and tests were added. The current description should be replaced with a comprehensive explanation matching the scope of changes evident in the raw summary.
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Add support for DisableAutoRegister of HEAD routes" accurately describes the main change in the changeset. The PR implements automatic HEAD route generation for every GET route and introduces a configuration option to disable this behavior, which aligns with what the title conveys. While the title uses slightly simplified terminology compared to the actual config field name DisableHeadAutoRegister, it still clearly communicates the core feature being added. The use of emoji and "feat:" prefix follows the repository's contribution conventions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/2025-10-24-09-10-22

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ReneWerner87, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to how Fiber handles HEAD requests by automatically generating HEAD routes for all GET routes. This ensures that HEAD requests consistently reflect the metadata (headers, status codes) of their GET counterparts without sending a response body, leveraging fasthttp's behavior. Developers can override these auto-generated routes with custom HEAD handlers or disable the feature entirely via a new configuration option. The changes also include internal refactoring for better code clarity and comprehensive test coverage for the new functionality.

Highlights

  • Automatic HEAD Route Registration: Fiber now automatically registers a HEAD route for every GET route, mirroring the GET handler chain but suppressing the response body.
  • Configurable Disablement: A new DisableAutoRegister option in fiber.Config allows global opt-out of this automatic HEAD route generation.
  • Explicit Override: Explicitly defined HEAD handlers will always take precedence over the auto-generated ones for the same path.
  • Internal Clarity: Renamed an internal helper function to pruneAutoHeadRouteLocked and added documentation to clarify its purpose and the mechanism by which fasthttp handles HEAD responses without bodies.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

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 enhances Fiber's routing system by automatically registering HEAD routes for every GET route, ensuring HEAD requests mirror GET behavior (status codes and headers) without sending a response body. The implementation adds a new configuration option to disable this feature and properly handles explicit HEAD registrations that override auto-generated ones.

Key changes:

  • Introduced DisableAutoRegister config option to control automatic HEAD route generation
  • Added autoHead flag to Route struct to identify auto-generated HEAD routes
  • Implemented pruneAutoHeadRouteLocked helper to remove auto-HEAD routes when explicit HEAD handlers are registered

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
router.go Core auto-HEAD logic: added ensureAutoHeadRoutes functions and pruning helpers
router_test.go Comprehensive test suite covering auto-HEAD behavior in various scenarios
app.go Added DisableAutoRegister config field and integrated auto-HEAD generation into startup
app_test.go Updated existing tests to account for auto-generated HEAD routes
mount_test.go Adjusted handler count assertions for mounted apps with auto-HEAD routes
docs/whats_new.md Documented the new automatic HEAD routes feature
docs/guide/routing.md Added routing guide section explaining auto-HEAD behavior
docs/api/fiber.md Documented the DisableAutoRegister configuration option

@codecov
Copy link

codecov bot commented Oct 24, 2025

Codecov Report

❌ Patch coverage is 91.04478% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.25%. Comparing base (534aa88) to head (131a936).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
router.go 90.32% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3817      +/-   ##
==========================================
+ Coverage   91.91%   92.25%   +0.34%     
==========================================
  Files         115      115              
  Lines       12377     9734    -2643     
==========================================
- Hits        11376     8980    -2396     
+ Misses        729      480     -249     
- Partials      272      274       +2     
Flag Coverage Δ
unittests 92.25% <91.04%> (+0.34%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new feature to automatically register HEAD routes for every GET route, which is a great enhancement for REST API compliance. The implementation is thorough, including configuration options to disable the feature, and updates to documentation and tests. My review focuses on ensuring the new logic correctly handles different router configurations, specifically case-insensitivity. I've found a couple of places where using the original path instead of the normalized path could lead to incorrect behavior, such as creating duplicate routes. I've provided suggestions to address these issues. Additionally, I recommend adding test cases for case-insensitive routing to ensure the feature is robust.

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: 1

🧹 Nitpick comments (5)
router.go (2)

481-503: Normalize path when pruning to avoid trailing-slash/case variants.

Comparing by raw Route.Path can miss pruning when HEAD/GET are registered with equivalent paths that differ by casing or trailing slashes under non-strict/case-insensitive configs. Compare against the normalized route.path instead.

Apply this diff:

 func (app *App) pruneAutoHeadRouteLocked(path string) {
-	headIndex := app.methodInt(MethodHead)
+	headIndex := app.methodInt(MethodHead)
 	if headIndex == -1 {
 		return
 	}
 
-	headStack := app.stack[headIndex]
+	// Normalize for consistent comparison with route.path
+	norm := app.normalizePath(path)
+	headStack := app.stack[headIndex]
 	for i := len(headStack) - 1; i >= 0; i-- {
 		headRoute := headStack[i]
-		if headRoute.Path != path || headRoute.mount || headRoute.use || !headRoute.autoHead {
+		if headRoute.mount || headRoute.use || !headRoute.autoHead || headRoute.path != norm {
 			continue
 		}
 
 		app.stack[headIndex] = append(headStack[:i], headStack[i+1:]...)
 		app.routesRefreshed = true
 		atomic.AddUint32(&app.handlersCount, ^uint32(len(headRoute.Handlers)-1)) //nolint:gosec // Not a concern
 		return
 	}
 }

620-674: De-dup and lookup by normalized path to avoid duplicate auto HEADs.

Use route.path (normalized) instead of Route.Path (raw) when tracking existing HEADs and checking GETs. Prevents duplicates when GET/HEAD are registered with equivalent-but-different raw strings (e.g., "/users" vs "/users/").

Apply this diff:

 func (app *App) ensureAutoHeadRoutesLocked() {
@@
-	headStack := app.stack[headIndex]
-	existing := make(map[string]struct{}, len(headStack))
-	for _, route := range headStack {
+	headStack := app.stack[headIndex]
+	existing := make(map[string]struct{}, len(headStack))
+	for _, route := range headStack {
 		if route.mount || route.use {
 			continue
 		}
-		existing[route.Path] = struct{}{}
+		existing[route.path] = struct{}{}
 	}
@@
-	for _, route := range app.stack[getIndex] {
+	for _, route := range app.stack[getIndex] {
 		if route.mount || route.use {
 			continue
 		}
-		if _, ok := existing[route.Path]; ok {
+		if _, ok := existing[route.path]; ok {
 			continue
 		}
@@
 		headStack = append(headStack, headRoute)
-		existing[route.Path] = struct{}{}
+		existing[route.path] = struct{}{}
 		app.routesRefreshed = true

Consider adding a small test for trailing-slash/case-insensitive variants to lock this in.

router_test.go (2)

320-543: Auto-HEAD suite: strong coverage; add two edge cases for path normalization.

Great end-to-end validations. To catch trailing-slash and case-insensitive variants, add focused tests so pruning/dedup works when raw paths differ but normalize to the same value.

Suggested additions:

+func TestAutoHead_PruneTrailingSlashVariant(t *testing.T) {
+  t.Parallel()
+  app := New(Config{StrictRouting: false})
+  app.Get("/trail/", func(c Ctx) error { return c.SendString("ok") })
+  // Ensure auto HEAD exists
+  app.startupProcess()
+  // Add explicit HEAD without trailing slash; should prune auto one
+  app.Head("/trail", func(c Ctx) error {
+    c.Set("X-Explicit", "1")
+    return c.SendStatus(StatusAccepted)
+  })
+  resp, err := app.Test(httptest.NewRequest(MethodHead, "/trail", nil))
+  require.NoError(t, err)
+  defer resp.Body.Close()
+  require.Equal(t, StatusAccepted, resp.StatusCode)
+  require.Equal(t, "1", resp.Header.Get("X-Explicit"))
+}
+
+func TestAutoHead_CaseInsensitiveMatch(t *testing.T) {
+  t.Parallel()
+  app := New(Config{CaseSensitive: false})
+  app.Get("/Users", func(c Ctx) error { return c.SendString("ok") })
+  app.startupProcess()
+  // Explicit HEAD with different casing should override auto HEAD
+  app.Head("/users", func(c Ctx) error { return c.SendStatus(StatusNoContent) })
+  resp, err := app.Test(httptest.NewRequest(MethodHead, "/USERS", nil))
+  require.NoError(t, err)
+  defer resp.Body.Close()
+  require.Equal(t, StatusNoContent, resp.StatusCode)
+}

1872-1873: Make Allow header assertion order-agnostic.

RequestMethods order can be customized; assert membership instead of exact order.

Apply this diff:

-	app.ensureAutoHeadRoutes()
+	app.ensureAutoHeadRoutes()
 	app.RebuildTree()
@@
-	require.Equal(t, "GET, HEAD", allow)
+	parts := strings.Split(strings.ReplaceAll(allow, " ", ""), ",")
+	require.ElementsMatch(t, []string{"GET", "HEAD"}, parts)

Also applies to: 1886-1887

docs/whats_new.md (1)

359-390: Consider adding fasthttp context to clarify the safety of HEAD route copying.

The documentation explains that the response body "remains empty" for auto-generated HEAD routes, but doesn't explicitly mention that this behavior is guaranteed by fasthttp (which omits bodies for HEAD responses per HTTP spec). The PR objectives include documenting this technical detail and the safety rationale for copying GET handlers.

Consider adding a brief technical note clarifying that fasthttp handles body omission automatically:

 Fiber now auto-registers a `HEAD` route whenever you add a `GET` route. The generated handler chain matches the `GET` chain so status codes and headers stay in sync while the response body remains empty, ensuring HEAD clients observe the same metadata as a GET consumer.
+
+This approach is safe because fasthttp automatically omits response bodies for HEAD requests, eliminating the need for handler-specific HEAD logic.

This addition would more explicitly satisfy the PR objective to "document that fasthttp omits bodies for HEAD responses" and clarify why copying GET handlers for HEAD is safe.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f7836e and d8bb310.

📒 Files selected for processing (8)
  • app.go (2 hunks)
  • app_test.go (3 hunks)
  • docs/api/fiber.md (1 hunks)
  • docs/guide/routing.md (1 hunks)
  • docs/whats_new.md (1 hunks)
  • mount_test.go (5 hunks)
  • router.go (5 hunks)
  • router_test.go (7 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Apply formatting using gofumpt (Make target: format)
Optimize struct field alignment using betteralign (Make target: betteralign)
Modernize code using gopls modernize (Make target: modernize)

Files:

  • mount_test.go
  • app.go
  • app_test.go
  • router_test.go
  • router.go
docs/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Review and update the contents of the docs folder if necessary when modifying code

Files:

  • docs/guide/routing.md
  • docs/whats_new.md
  • docs/api/fiber.md
🧠 Learnings (1)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
PR: gofiber/fiber#3807
File: adapter_test.go:118-144
Timestamp: 2025-10-16T07:15:26.529Z
Learning: In Fiber v3, net/http handlers (http.Handler, http.HandlerFunc, or raw func(http.ResponseWriter, *http.Request)) can be passed directly to routing methods like app.Get(), app.Post(), etc. The framework automatically detects and wraps them internally via toFiberHandler/collectHandlers. The github.com/gofiber/fiber/v3/middleware/adaptor package is legacy and should not be suggested for tests or code using native net/http handler support.

Applied to files:

  • docs/guide/routing.md
  • docs/whats_new.md
🧬 Code graph analysis (3)
app_test.go (1)
constants.go (4)
  • HeaderAllow (231-231)
  • MethodPatch (9-9)
  • MethodGet (5-5)
  • MethodHead (6-6)
router_test.go (4)
app.go (2)
  • New (521-634)
  • Config (113-424)
middleware/static/static.go (1)
  • New (83-229)
ctx_interface_gen.go (1)
  • Ctx (18-426)
constants.go (7)
  • MethodHead (6-6)
  • StatusOK (52-52)
  • MethodGet (5-5)
  • StatusMethodNotAllowed (78-78)
  • StatusNoContent (56-56)
  • StatusNotFound (77-77)
  • StatusAccepted (54-54)
router.go (2)
constants.go (2)
  • MethodGet (5-5)
  • MethodHead (6-6)
app.go (1)
  • App (68-110)
🪛 GitHub Actions: markdownlint
docs/api/fiber.md

[error] 56-56: markdownlint: MD055 table pipe style violation. Expected leading_and_trailing pipes; found leading_only; missing trailing pipe.

🪛 GitHub Check: markdownlint
docs/api/fiber.md

[failure] 57-57: Code block style
docs/api/fiber.md:57 MD046/code-block-style Code block style [Expected: fenced; Actual: indented] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md046.md


[failure] 56-56: Tables should be surrounded by blank lines
docs/api/fiber.md:56 MD058/blanks-around-tables Tables should be surrounded by blank lines [Context: "| <Reference id="disablekeepal..."] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md058.md


[failure] 56-56: Table column count
docs/api/fiber.md:56:96 MD056/table-column-count Table column count [Expected: 4; Actual: 2; Too few cells, row will be missing data] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md056.md


[failure] 56-56: Table pipe style
docs/api/fiber.md:56:96 MD055/table-pipe-style Table pipe style [Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md055.md

🪛 LanguageTool
docs/api/fiber.md

[grammar] ~60-~60: Use a hyphen to join words.
Context: ... | Will not pre parse Multipart Form data if set to true...

(QB_NEW_EN_HYPHEN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Compare
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: unit (1.25.x, macos-13)
  • GitHub Check: lint
  • GitHub Check: repeated
🔇 Additional comments (7)
router.go (5)

57-64: Route.autoHead flag: clear intent and minimal footprint.

Field addition is appropriate and constrained to routing metadata. Keep as is.


371-376: copyRoute correctly preserves autoHead.

Good call keeping autoHead in sync across copies.


473-476: Prune auto HEAD when deleting a GET route.

Correct invocation point and lock context; avoids stale auto HEADs.


588-591: Prune before adding explicit HEAD.

Good prevention of duplicate handler chains on HEAD overrides.


613-619: Thread-safe helper wrapper looks good.

Locking discipline is clear; wrapper keeps callsites tidy.

router_test.go (2)

1194-1194: Updated handler counts look consistent with auto-HEAD registrations.

Numbers align with added HEAD routes and merges. No change requested.

Also applies to: 1198-1198, 1201-1201, 1203-1203, 1206-1206


1312-1315: startupProcess invocation and counts: OK.

Ensures auto-HEAD setup before assertions; counts match expectations.

Also applies to: 1314-1314, 1338-1338

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

♻️ Duplicate comments (2)
router.go (1)

659-662: Doc comment on fasthttp HEAD behavior is precise

Wording matches transmission‑phase suppression; no body while preserving headers/length.

router_test.go (1)

15-16: Run project formatters after import change

After adding path/filepath, run make format to enforce gofumpt import ordering.

🧹 Nitpick comments (4)
router.go (3)

390-404: Path normalization helper matches register semantics

normalizePath mirrors register’s casing and trailing‑slash rules and removes escapes. Consider reusing this helper inside register to reduce duplication and drift risk.


480-503: Pruning logic is correct and case/strict aware

  • Normalizes the incoming path, then removes only mount=false,use=false,autoHead=true entries.
  • Updates routesRefreshed and handlersCount.

Nit: the comment could mention it’s also used when a GET route is deleted, not only when an explicit HEAD is registered.

-// pruneAutoHeadRouteLocked removes an automatically generated HEAD route so a
-// later explicit registration can take its place without duplicating handler
-// chains. The caller must already hold app.mutex.
+// pruneAutoHeadRouteLocked removes an automatically generated HEAD route.
+// Used when (a) an explicit HEAD is registered, allowing it to take precedence,
+// or (b) the originating GET route is removed, to avoid stale HEAD entries.
+// The caller must already hold app.mutex.

621-679: Auto‑HEAD generation uses normalized keys and respects config

  • Skips when DisableAutoRegister is true.
  • Uses route.path (normalized) for de‑duplication in existing map — correct for case‑insensitive/strict routing setups.
  • Updates handlersCount, routesRefreshed, latestRoute, and onRoute hooks.

Minor: assigning headStack back only when added is a micro‑optimization; setting unconditionally is also fine.

router_test.go (1)

320-562: Excellent coverage for auto‑HEAD lifecycle

Covers enable/disable, override with explicit HEAD, groups, static files, 404, precedence, route listing, and no‑body semantics.

Optional enhancement: add a case‑insensitive scenario to guard against duplicates (e.g., Config{CaseSensitive:false}: GET /foo + explicit HEAD /Foo → single HEAD, no auto‑HEAD).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4051b7 and 011bacc.

📒 Files selected for processing (3)
  • docs/guide/routing.md (1 hunks)
  • router.go (5 hunks)
  • router_test.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/guide/routing.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Apply formatting using gofumpt (Make target: format)
Optimize struct field alignment using betteralign (Make target: betteralign)
Modernize code using gopls modernize (Make target: modernize)

Files:

  • router.go
  • router_test.go
🧠 Learnings (1)
📚 Learning: 2025-07-19T15:13:10.652Z
Learnt from: CR
PR: gofiber/fiber#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-19T15:13:10.652Z
Learning: Run `make format` to format the project when modifying code

Applied to files:

  • router_test.go
🧬 Code graph analysis (2)
router.go (2)
constants.go (2)
  • MethodGet (5-5)
  • MethodHead (6-6)
app.go (1)
  • App (68-110)
router_test.go (2)
app.go (2)
  • New (521-634)
  • Config (113-424)
constants.go (7)
  • MethodHead (6-6)
  • StatusOK (52-52)
  • MethodGet (5-5)
  • StatusMethodNotAllowed (78-78)
  • StatusNoContent (56-56)
  • StatusNotFound (77-77)
  • StatusAccepted (54-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Compare
  • GitHub Check: lint
  • GitHub Check: unit (1.25.x, macos-13)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: repeated
🔇 Additional comments (11)
router.go (6)

59-64: Good addition: explicit marker for auto‑generated HEAD routes

The autoHead flag is the right minimal state to distinguish generated vs explicit HEAD entries. Keeps pruning safe.


371-376: copyRoute now preserves autoHead

Propagation of autoHead avoids misclassifying cloned routes later. LGTM.


410-416: RemoveRoute compares normalized, private path

Using app.normalizePath and r.path ensures consistency with CaseSensitive/StrictRouting.


473-476: Prune auto HEAD when a GET route is removed

This avoids orphaned auto HEAD routes. Explicit HEAD remains untouched, as intended.

Please confirm we also want to prune auto HEAD when deleting GET via RemoveRouteFunc matchers; current call site covers it because prune is inside deleteRoute loop.


589-591: Head registration prunes stale auto HEAD first

Defensive and idempotent. Prevents duplicate HEAD stacks.


614-620: Thread‑safe wrapper is fine

ensureAutoHeadRoutes acquires app.mutex and delegates.

router_test.go (5)

1214-1227: Updated handler counts reflect auto‑HEAD generation

Numbers now include the additional HEAD handlers. Keep these assertions in sync if RequestMethods or auto‑register defaults change.


1332-1335: Explicit startupProcess ensures auto‑HEADs exist before assertions

Good call to avoid flakiness in tests that inspect app state (GetRoutes/handlersCount).


1476-1478: Allow header now includes HEAD

Expectation "GET, HEAD" aligns with next() behavior after auto‑HEAD registration.


1892-1893: ensureAutoHeadRoutes before RebuildTree for Allow header

This guarantees HEAD is advertised in MethodNotAllowed path for custom ctx.


1230-1301: normalizePath tests are thorough

Exercising empty path, leading slash, strict routing, case sensitivity, and escape removal validates parity with register rules.

Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gaby gaby changed the title Clarify auto HEAD pruning 🔥 feat: Add support for DisableAutoRegister of Head routes Oct 25, 2025
@gaby gaby changed the title 🔥 feat: Add support for DisableAutoRegister of Head routes 🔥 feat: Add support for DisableAutoRegister of HEAD routes Oct 25, 2025
app.go Outdated
// every GET route.
//
// Default: false
DisableAutoRegister bool `json:"disable_auto_register"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe more explicit naming like DisableHeadAutoRegister or DisableHeadAutoRegistration?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DisableAutoHeadRoutes ?

@gaby gaby requested a review from Copilot October 28, 2025 01:20
Copy link
Contributor

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

// Fasthttp automatically omits response bodies when transmitting
// HEAD responses, so the copied GET handler stack can execute
// unchanged while still producing an empty body on the wire.

Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] While the comment explains fasthttp's body omission behavior, it should also clarify that this means Content-Length and other headers calculated by the GET handler remain accurate even though no body is transmitted.

Suggested change
// Note: Headers such as Content-Length and others calculated by the GET handler
// remain accurate and are transmitted, even though no body is sent.

Copilot uses AI. Check for mistakes.
@gaby
Copy link
Member

gaby commented Oct 28, 2025

@coderabbitai suggest alternative names to config option DisableAutoRegister

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Tip

For best results, initiate chat on the files or code changes.

@gaby, here are some alternative names for the DisableAutoRegister configuration option:

More specific negative options (disable/suppress):

  1. DisableAutoHeadRoutes - Explicitly mentions HEAD routes, making it immediately clear what auto-registration is being disabled
  2. DisableAutoHead - Shorter while still being specific about the feature
  3. DisableHeadAutoRegistration - Very descriptive, follows "verb-noun" pattern
  4. SuppressAutoHeadRoutes - Alternative verb that might feel less permanent

Positive boolean options (inverted logic):
5. AutoRegisterHead - Positive form; when true, auto-registers HEAD routes (requires inverting default to true)
6. EnableAutoHeadRoutes - Explicit positive form
7. AutoHeadRoutes - Simplest positive form, clean and concise

My recommendation: I'd suggest DisableAutoHead as it strikes a good balance between:

  • Being specific enough (mentions HEAD explicitly)
  • Keeping consistency with the negative disable pattern
  • Remaining concise
  • Clearly communicating the feature being controlled

Alternatively, if you prefer positive boolean logic (which often reads more intuitively), AutoHeadRoutes would be the cleanest option, though you'd need to invert the default value to true.

Would any of these work better for your use case?

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

♻️ Duplicate comments (2)
docs/whats_new.md (1)

359-395: Backticks + example look good

Backticks for “HEAD” and “GET” and the defined handler make this section copy/paste‑safe.

docs/guide/routing.md (1)

19-55: Section reads well

Backticks and examples are consistent; override/disable behavior is clear.

🧹 Nitpick comments (5)
docs/whats_new.md (1)

359-395: Clarify HEAD body omission (fasthttp) and headers

Add a short note that fasthttp omits bodies on HEAD responses while preserving headers like Content-Length computed by the GET chain. Improves copy/paste confidence.

- Fiber now auto-registers a `HEAD` route whenever you add a `GET` route. The generated handler chain matches the `GET` chain so status codes and headers stay in sync while the response body remains empty, ensuring `HEAD` clients observe the same metadata as a `GET` consumer.
+ Fiber now auto-registers a `HEAD` route whenever you add a `GET` route. The generated handler chain matches the `GET` chain so status codes and headers stay in sync while the response body remains empty. Fasthttp automatically suppresses response bodies for `HEAD` responses during transmission, so headers (including `Content-Length`) computed by the `GET` chain are still sent while the payload is omitted.
router.go (1)

621-679: Auto-HEAD generation logic: solid; tiny comment polish

Implementation looks correct: skips use/mount, uses normalized route.path for case-insensitive apps, updates counts, and triggers hooks. Consider expanding the fasthttp note to mention header accuracy.

-        // Fasthttp automatically omits response bodies when transmitting
-        // HEAD responses, so the copied GET handler stack can execute
-        // unchanged while still producing an empty body on the wire.
+        // Fasthttp automatically omits response bodies when transmitting HEAD responses,
+        // so the copied GET handler stack can execute unchanged while still producing
+        // an empty body on the wire. Headers (e.g., Content-Length, ETag) computed
+        // by the GET chain remain accurate and are sent.

Also optional: keeping Name identical means RemoveRouteByName(name) without methods will remove both GET and generated HEAD. If this is undesirable, consider suffixing or omitting Name for auto routes.

Would you like a follow-up patch to differentiate route names for auto-generated HEAD entries?

docs/guide/routing.md (1)

19-55: Add one sentence on fasthttp HEAD behavior

Mirror the “What’s New” note so readers know headers remain while the body is suppressed by fasthttp.

-Fiber automatically registers a `HEAD` route for every `GET` route you add. The generated handler chain mirrors the `GET` chain, so `HEAD` requests reuse middleware, status codes, and headers while the response body is suppressed.
+Fiber automatically registers a `HEAD` route for every `GET` route you add. The generated handler chain mirrors the `GET` chain, so `HEAD` requests reuse middleware, status codes, and headers while the response body is suppressed. Fasthttp omits bodies for `HEAD` responses during transmission, so headers like `Content-Length` computed by the `GET` chain are still sent.
router_test.go (2)

1475-1476: Make Allow header assertion order-insensitive

RequestMethods order could change; compare as a set to avoid flaky failures.

- require.Equal(b, MethodGet+", "+MethodHead, string(c.Response.Header.Peek("Allow")))
+ allow := string(c.Response.Header.Peek("Allow"))
+ parts := strings.Split(allow, ", ")
+ require.ElementsMatch(b, []string{MethodGet, MethodHead}, parts)
- require.Equal(t, "GET, HEAD", allow)
+ parts := strings.Split(allow, ", ")
+ require.ElementsMatch(t, []string{MethodGet, MethodHead}, parts)

Remember to import strings in the test file scope if not already.

Would you like me to push a patch updating both sites?

Also applies to: 1904-1905


384-388: Optional: Assert Allow header in 405 case

You can assert Allow includes GET when auto-register is disabled to strengthen the contract.

 resp, err := app.Test(httptest.NewRequest(MethodHead, "/", nil))
 require.NoError(t, err)
 registerCleanup(t, resp.Body)
 require.Equal(t, StatusMethodNotAllowed, resp.StatusCode)
+require.Contains(t, resp.Header.Get("Allow"), MethodGet)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17d91c0 and 131a936.

📒 Files selected for processing (6)
  • app.go (2 hunks)
  • docs/api/fiber.md (1 hunks)
  • docs/guide/routing.md (1 hunks)
  • docs/whats_new.md (1 hunks)
  • router.go (5 hunks)
  • router_test.go (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/api/fiber.md
  • app.go
🧰 Additional context used
📓 Path-based instructions (2)
docs/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Review and update the contents of the docs folder if necessary when modifying code

Files:

  • docs/guide/routing.md
  • docs/whats_new.md
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Apply formatting using gofumpt (Make target: format)
Optimize struct field alignment using betteralign (Make target: betteralign)
Modernize code using gopls modernize (Make target: modernize)

Files:

  • router.go
  • router_test.go
🧠 Learnings (2)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
PR: gofiber/fiber#3807
File: adapter_test.go:118-144
Timestamp: 2025-10-16T07:15:26.529Z
Learning: In Fiber v3, net/http handlers (http.Handler, http.HandlerFunc, or raw func(http.ResponseWriter, *http.Request)) can be passed directly to routing methods like app.Get(), app.Post(), etc. The framework automatically detects and wraps them internally via toFiberHandler/collectHandlers. The github.com/gofiber/fiber/v3/middleware/adaptor package is legacy and should not be suggested for tests or code using native net/http handler support.

Applied to files:

  • docs/guide/routing.md
  • docs/whats_new.md
📚 Learning: 2025-07-19T15:13:10.652Z
Learnt from: CR
PR: gofiber/fiber#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-19T15:13:10.652Z
Learning: Run `make format` to format the project when modifying code

Applied to files:

  • router_test.go
🧬 Code graph analysis (2)
router.go (2)
constants.go (2)
  • MethodGet (5-5)
  • MethodHead (6-6)
app.go (1)
  • App (68-110)
router_test.go (2)
app.go (2)
  • New (521-634)
  • Config (113-424)
constants.go (7)
  • MethodHead (6-6)
  • StatusOK (52-52)
  • MethodGet (5-5)
  • StatusMethodNotAllowed (78-78)
  • StatusNoContent (56-56)
  • StatusNotFound (77-77)
  • StatusAccepted (54-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Compare
  • GitHub Check: unit (1.25.x, macos-13)
  • GitHub Check: repeated
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: unit (1.25.x, macos-latest)
🔇 Additional comments (9)
router.go (6)

57-64: Route flag addition is appropriate

autoHead clearly distinguishes generated HEAD routes from explicit ones; used correctly in pruning.


371-376: Copy includes autoHead

Good to keep parity when cloning; later overrides to true for generated HEAD are explicit.


473-476: Prune auto HEAD on GET removal

Correctly avoids pruning for use/mount; ensures counts/routesRefreshed update.


480-503: Path normalization respected; safe pruning

Normalizing the incoming path and checking autoHead avoids deleting explicit HEAD routes and respects CaseSensitive/StrictRouting.


589-592: Prune before explicit HEAD add

Prevents duplicate handler chains when an explicit HEAD overrides an auto one.


614-619: Thread-safe wrapper

ensureAutoHeadRoutes() correctly gates the locked variant.

router_test.go (3)

319-560: Great coverage of auto-HEAD behavior

Table-driven runners hit the key paths (default, disabled, override, groups, static, 404, ordering, listings, 204). Nice.


1330-1333: Handler count expectations updated for auto-HEAD

Counts account for generated HEAD routes after startupProcess(); looks consistent. If RequestMethods is customized (e.g., without HEAD), these assertions will change—consider gating by config in future refactors.

Also applies to: 1356-1357, 1213-1225


1891-1891: Explicit ensure before tree rebuild

Calling ensureAutoHeadRoutes() prior to RebuildTree() in this custom-ctx test removes order ambiguity.

github-actions[bot]

This comment was marked as outdated.

@ReneWerner87 ReneWerner87 merged commit eb677ac into main Oct 29, 2025
13 of 15 checks passed
@ReneWerner87 ReneWerner87 deleted the codex/2025-10-24-09-10-22 branch October 29, 2025 14:10
@github-project-automation github-project-automation bot moved this to Done in v3 Oct 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: GET routes should automatically register corresponding HEAD routes

4 participants