Skip to content

use reflect.TypeAssert#3700

Closed
ReneWerner87 wants to merge 3 commits into
mainfrom
codex/2025-08-21-11-09-58
Closed

use reflect.TypeAssert#3700
ReneWerner87 wants to merge 3 commits into
mainfrom
codex/2025-08-21-11-09-58

Conversation

@ReneWerner87
Copy link
Copy Markdown
Member

Summary

  • replace direct type assertions with reflect.TypeAssert in middleware, request helpers, and logging utilities
  • expand reflection-based checks for sessions, context locals, and pooled resources
  • update utilities and cache managers to use reflect.TypeAssert

Testing

  • make audit (fails: EncodeMsg passes lock by value)
  • make generate
  • make betteralign (fails: package requires newer Go version go1.25 (application built with go1.24))
  • make modernize (fails: package requires newer Go version go1.25 (application built with go1.24))
  • make format
  • make test (fails: Test_SendFile_withRoutes)

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

Copilot AI review requested due to automatic review settings August 21, 2025 11:10
@ReneWerner87 ReneWerner87 requested a review from a team as a code owner August 21, 2025 11:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 21, 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

Replaces numerous direct type assertions with nil-checked reflect.TypeAssert[T] conversions across helpers, request locals, and multiple middleware modules. No public API or function signatures changed; internal control flows now perform defensive reflection checks with preserved behavior and fallbacks.

Changes

Cohort / File(s) Summary
Core helpers & request locals
helpers.go, req.go
Replace direct assertions on pooled header params and Locals[V] with nil-checked reflect.TypeAssert[T] conversions; parsing and control flow unchanged.
Auth & key middleware
middleware/basicauth/basicauth.go, middleware/keyauth/keyauth.go
Username/token retrieval from Ctx.Locals now uses nil-check + reflect.TypeAssert[string] instead of direct . (string) assertions.
CSRF middleware
middleware/csrf/csrf.go, middleware/csrf/session_manager.go
Token/Handler/session token retrieval switched to nil-check + reflect.TypeAssert[T]; verification and expiry logic preserved.
Logger & tags
middleware/logger/default_logger.go, middleware/logger/tags.go
Timestamp and tag extraction moved to nil-checked reflect.TypeAssert for string/[]byte; fallbacks and formatting preserved.
Request ID
middleware/requestid/requestid.go
FromContext now nil-checks Locals and uses reflect.TypeAssert[string] to obtain the request ID.
Cache internals
middleware/cache/heap.go, middleware/cache/manager.go
Heap entries and in-memory items/bytes retrieved via reflect.TypeAssert instead of direct assertions; push/remove/get/acquire paths unchanged.
Session middleware (pool/context/store/data/middleware/session)
middleware/session/*.go, middleware/session/*.go
Pool acquisitions, Locals lookups, and session middleware checks use nil-checks + reflect.TypeAssert (e.g., *Session, *Middleware, *data); original panics/error branches preserved.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant LocalsPool as Locals / Pool
  participant Code as Caller
  participant Reflect as reflect.TypeAssert
  participant Consumer as Consumer

  Note over LocalsPool,Code: Old flow (direct assertion)
  LocalsPool->>Code: value := Locals(key) / Get()
  Code->>Code: v := value.(T)
  Code->>Consumer: use v

  Note over LocalsPool,Reflect: New flow (nil-check + reflect)
  LocalsPool->>Code: raw := Locals(key) / Get()
  Code->>Code: if raw == nil -> zero/early return
  Code->>Reflect: reflect.TypeAssert[T](reflect.ValueOf(raw))
  Reflect-->>Code: (v, ok)
  alt ok
    Code->>Consumer: use v
  else not ok
    Code->>Code: zero value or fallback
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🧹 Updates

Suggested reviewers

  • gaby
  • sixcolors
  • efectn

Poem

I hop where types once leapt and fell,
I check each burrow, know it well.
With gentle reflect I peek inside,
No sudden panic, safe the ride.
Logs, cache and sessions — tidy warren stride. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a92bf12 and 76019d8.

📒 Files selected for processing (1)
  • helpers.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • helpers.go
⏰ 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). (6)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: unit (1.25.x, macos-13)
  • GitHub Check: lint
  • GitHub Check: Compare
  • GitHub Check: repeated
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/2025-08-21-11-09-58

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
🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

Copy link
Copy Markdown
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.

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 refactors the codebase to replace direct type assertions with reflect.TypeAssert across various modules, including middleware, request helpers, logging utilities, and session management. The primary goal is to enhance type safety and provide more explicit control over type conversions, especially when dealing with values retrieved from fiber.Ctx.Locals and sync.Pool instances. This change aims to make the codebase more robust by leveraging Go's reflection capabilities for type checking and conversion.

Highlights

  • Migration to reflect.TypeAssert: The primary change across the codebase is the replacement of direct type assertions (e.g., value.(Type)) with the more explicit and potentially safer reflect.TypeAssert[Type](reflect.ValueOf(value)) pattern. This change is applied consistently where values are retrieved from generic any interfaces.
  • Enhanced Type Safety for Context Locals: Numerous instances where values are retrieved from fiber.Ctx.Locals (which returns any) have been updated. This includes functions like UsernameFromContext, TokenFromContext, HandlerFromContext, and the generic Locals helper, ensuring more robust type checking and explicit handling of nil values.
  • Safer sync.Pool Object Retrieval: The pull request refactors how objects are acquired from sync.Pool instances. Previously, direct type assertions were used, but now reflect.TypeAssert is employed, often with additional nil checks, to ensure that pooled objects are correctly cast to their expected types (e.g., in cache managers and session data/middleware pools).
  • Updates in Cache and Logging Utilities: Changes extend to various utility components, including the cache heap and manager, as well as logger components. Type assertions for cache entries, items, and logger data (like timestamps and local variables) are now handled using reflect.TypeAssert for improved consistency and type safety.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Aug 21, 2025

Codecov Report

❌ Patch coverage is 71.42857% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.62%. Comparing base (edd7083) to head (76019d8).

Files with missing lines Patch % Lines
middleware/session/session.go 16.66% 7 Missing and 3 partials ⚠️
middleware/logger/tags.go 69.23% 3 Missing and 1 partial ⚠️
middleware/cache/manager.go 70.00% 3 Missing ⚠️
middleware/requestid/requestid.go 40.00% 2 Missing and 1 partial ⚠️
middleware/basicauth/basicauth.go 66.66% 1 Missing and 1 partial ⚠️
middleware/cache/heap.go 33.33% 2 Missing ⚠️
middleware/session/data.go 33.33% 1 Missing and 1 partial ⚠️
middleware/session/middleware.go 77.77% 1 Missing and 1 partial ⚠️
middleware/csrf/csrf.go 90.90% 1 Missing ⚠️
middleware/keyauth/keyauth.go 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3700      +/-   ##
==========================================
- Coverage   91.79%   91.62%   -0.18%     
==========================================
  Files         114      114              
  Lines       11536    11592      +56     
==========================================
+ Hits        10590    10621      +31     
- Misses        684      701      +17     
- Partials      262      270       +8     
Flag Coverage Δ
unittests 91.62% <71.42%> (-0.18%) ⬇️

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
Copy Markdown
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 replaces direct type assertions with reflect.TypeAssert throughout the codebase to improve type safety and error handling. The changes systematically update middleware components, request helpers, and utility functions to use reflection-based type assertions instead of direct casting.

Key changes:

  • Replace direct type assertions (value.(Type)) with reflect.TypeAssert[Type](reflect.ValueOf(value))
  • Add nil checks before performing type assertions
  • Update session middleware, logging, authentication, and caching components

Reviewed Changes

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

Show a summary per file
File Description
req.go Updates generic Locals function to use reflect.TypeAssert
middleware/session/*.go Replaces type assertions in session management and pooled resource handling
middleware/requestid/requestid.go Updates context value extraction with reflection-based assertions
middleware/logger/*.go Converts logging tag handlers and timestamp handling to use reflect.TypeAssert
middleware/keyauth/keyauth.go Updates token extraction from context
middleware/csrf/*.go Converts CSRF token and handler retrieval to use reflection
middleware/cache/*.go Updates cache manager and heap operations with reflect.TypeAssert
middleware/basicauth/basicauth.go Updates username extraction from context
helpers.go Converts header parameter pool retrieval

obj := sessionPool.Get()
if obj == nil {
panic("unexpected type in session pool")
}
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The nil check for sessionPool.Get() is incorrect. sync.Pool.Get() never returns nil - it either returns a pooled object or calls the New function. This check will never trigger and the panic message is misleading.

Suggested change
}

Copilot uses AI. Check for mistakes.
func acquireData() *data {
obj := dataPool.Get()
if d, ok := obj.(*data); ok {
if obj == nil {
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The nil check for dataPool.Get() is incorrect. sync.Pool.Get() never returns nil - it either returns a pooled object or calls the New function. This check will never trigger and the panic message is misleading.

Copilot uses AI. Check for mistakes.
if v == nil {
panic(ErrTypeAssertionFailed.Error())
}
m, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v))
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The nil check for middlewarePool.Get() is incorrect. sync.Pool.Get() never returns nil - it either returns a pooled object or calls the New function. This check will never trigger and the panic message is misleading.

Suggested change
m, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v))
m, ok := v.(*Middleware)

Copilot uses AI. Check for mistakes.
Comment thread middleware/cache/manager.go Outdated
func (m *manager) acquire() *item {
return m.pool.Get().(*item) //nolint:forcetypeassert,errcheck // We store nothing else in the pool
obj := m.pool.Get()
it, _ := reflect.TypeAssert[*item](reflect.ValueOf(obj)) //nolint:errcheck // We store nothing else in the pool
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The error from reflect.TypeAssert is ignored, but if the type assertion fails, it will be nil and calling methods on it will cause a panic. The original code was safer with a direct type assertion that would panic immediately if the type was wrong.

Suggested change
it, _ := reflect.TypeAssert[*item](reflect.ValueOf(obj)) //nolint:errcheck // We store nothing else in the pool
it := obj.(*item) // We store nothing else in the pool

Copilot uses AI. Check for mistakes.
Comment thread helpers.go Outdated
}
} else {
params, _ = headerParamPool.Get().(headerParams) //nolint:errcheck // only contains headerParams
params, _ = reflect.TypeAssert[headerParams](reflect.ValueOf(headerParamPool.Get())) //nolint:errcheck // only contains headerParams
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The error from reflect.TypeAssert is ignored, but if the type assertion fails, params will be a zero value (nil map), and subsequent operations like delete(params, k) will panic. The original code was safer with a direct type assertion.

Suggested change
params, _ = reflect.TypeAssert[headerParams](reflect.ValueOf(headerParamPool.Get())) //nolint:errcheck // only contains headerParams
params = headerParamPool.Get().(headerParams) // only contains headerParams

Copilot uses AI. Check for mistakes.
Comment thread middleware/cache/heap.go Outdated
Comment thread middleware/cache/heap.go Outdated
if data.ChainErr != nil {
formatErr = colors.Red + " | " + data.ChainErr.Error() + colors.Reset
}
ts, _ := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

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

The error from reflect.TypeAssert is ignored. If the type assertion fails, ts will be an empty string, but the code doesn't handle this case properly and may produce incorrect log output.

Suggested change
ts, _ := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
ts, ok := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
if !ok {
ts = "unknown"
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
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 systematically replaces standard Go type assertions with a custom reflect.TypeAssert function. While I appreciate the effort to standardize type assertions, this approach has some significant drawbacks. In many places, the original code used safe and idiomatic two-value type assertions (v, ok := x.(T)) or type switches, which are both readable and performant. The new implementation using reflect.TypeAssert introduces reflection, which adds runtime overhead and can negatively impact performance, especially in hot paths like request handling and middleware. It also often makes the code more verbose and harder to read. I've left a few specific comments highlighting these issues. I recommend reconsidering this global refactoring and using standard Go constructs where they are safe and clear.

Comment thread helpers.go Outdated
Comment on lines 83 to 92
func UsernameFromContext(c fiber.Ctx) string {
username, ok := c.Locals(usernameKey).(string)
if !ok {
v := c.Locals(usernameKey)
if v == nil {
return ""
}
return username
if username, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
return username
}
return ""
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The original implementation using a two-value type assertion was already safe, idiomatic, and more performant. This change to use reflect.TypeAssert adds unnecessary complexity, verbosity, and reflection overhead without providing any additional safety. The original code handled the case where the local was not a string (or was nil) correctly by the !ok check.

func UsernameFromContext(c fiber.Ctx) string {
	username, ok := c.Locals(usernameKey).(string)
	if !ok {
		return ""
	}
	return username
}

Comment thread middleware/logger/tags.go
Comment on lines 171 to 183
TagLocals: func(output Buffer, c fiber.Ctx, _ *Data, extraParam string) (int, error) {
switch v := c.Locals(extraParam).(type) {
case []byte:
return output.Write(v)
case string:
return output.WriteString(v)
case nil:
v := c.Locals(extraParam)
if v == nil {
return 0, nil
default:
return output.WriteString(fmt.Sprintf("%v", v))
}
if b, ok := reflect.TypeAssert[[]byte](reflect.ValueOf(v)); ok {
return output.Write(b)
}
if s, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
return output.WriteString(s)
}
return output.WriteString(fmt.Sprintf("%v", v))
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The original type switch is more idiomatic, readable, and likely more performant than a series of if statements with reflection-based type assertions. A type switch is the standard Go way to handle a value of an unknown type when there are several possibilities.

		TagLocals: func(output Buffer, c fiber.Ctx, _ *Data, extraParam string) (int, error) {
			switch v := c.Locals(extraParam).(type) {
			case []byte:
				return output.Write(v)
			case string:
				return output.WriteString(v)
			case nil:
				return 0, nil
			default:
				return output.WriteString(fmt.Sprintf("%v", v))
			}
		},

Copy link
Copy Markdown
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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
middleware/session/middleware.go (1)

5-11: Pin Go toolchain to 1.25+ before using reflect.TypeAssert

We didn’t find any module directive, CI workflow, or Makefile pin for Go 1.25, which is required for reflect.TypeAssert (added in Go 1.25). Please address the following:

• go.mod
– No go.mod detected in the repo. Add a top-level go.mod (or update your existing one) with:
```go
module your/module/path

go 1.25
```  

• CI pipelines
– No GitHub Actions workflows under .github/workflows/ or other CI YAML files in the repo root.
– If you use GitHub Actions, CircleCI, Travis, etc., make sure the Go version is bumped to 1.25+.
For example, in GitHub Actions:
yaml uses: actions/setup-go@v3 with: go-version: '1.25'

• Makefile
– The Makefile at ./Makefile doesn’t pin a Go version. Add or update a variable, e.g.:
makefile GO_VERSION ?= 1.25

Let me know if you’d like me to prepare a follow-up PR that adds/updates the go directive in go.mod, bumps your CI to Go 1.25, and pins the Makefile.

🧹 Nitpick comments (21)
middleware/requestid/requestid.go (1)

49-56: Prefer using the generic fiber.Locals helper to avoid reflection and keep consistency

reflect.TypeAssert here is unnecessary; req.go exposes fiber.Locals[V] which already handles zero-value fallback and keeps call-sites uniform. This also lets you drop the reflect import.

Apply this diff within the function body:

 func FromContext(c fiber.Ctx) string {
-	v := c.Locals(requestIDKey)
-	if v == nil {
-		return ""
-	}
-	if rid, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
-		return rid
-	}
-	return ""
+	return fiber.Locals[string](c, requestIDKey)
 }

Additionally, update imports to remove reflect:

 import (
-	"reflect"
 
 	"github.com/gofiber/fiber/v3"
 )
middleware/cache/heap.go (1)

45-45: Fix linter noise: remove unused //nolint:errcheck directives

nolintlint flags these as unused because there is no error to check. The diffs above remove them.

Also applies to: 82-82

middleware/logger/tags.go (1)

172-183: Minor: avoid fmt.Sprintf("%v", v) for the fallback path

fmt.Sprint(v) is simpler and marginally cheaper than Sprintf("%v", v) for the same result.

Apply this diff:

-			return output.WriteString(fmt.Sprintf("%v", v))
+			return output.WriteString(fmt.Sprint(v))
middleware/session/data.go (1)

34-42: Improve diagnostics on unexpected pool type; consider avoiding panic for nil from Pool

With sync.Pool{New: ...}, Get() should not return nil. If it did, panicking with the actual type aids debugging. Optionally, create a new entry instead of panicking on nil for extra resilience.

Apply this diff to enrich the panic and handle nil defensively:

 func acquireData() *data {
 	obj := dataPool.Get()
-	if obj == nil {
-		panic("unexpected type in data pool")
-	}
-	if d, ok := reflect.TypeAssert[*data](reflect.ValueOf(obj)); ok {
+	if obj == nil {
+		// Defensive: should not happen because Pool.New is set.
+		d := new(data)
+		d.Data = make(map[any]any)
+		return d
+	}
+	if d, ok := reflect.TypeAssert[*data](reflect.ValueOf(obj)); ok {
 		return d
 	}
 	// Handle unexpected type in the pool
-	panic("unexpected type in data pool")
+	panic(fmt.Sprintf("unexpected type in data pool: %T", obj))
 }

And add the import:

 import (
+	"fmt"
 	"reflect"
 	"sync"
 )
req.go (1)

8-9: Guard the new dependency on reflect.TypeAssert behind a compatibility shim (keeps Go 1.24 builds working)

Build logs indicate “requires newer Go version go1.25”. If reflect.TypeAssert is only available in Go 1.25, consider a tiny wrapper that maps to stdlib on 1.25+ and falls back to x.(V) on older toolchains. This centralizes the change and avoids touching every call-site in a rollback.

Example wrapper:

// internal/xreflect/typeassert_go1.25.go
//go:build go1.25

package xreflect

import "reflect"

func TypeAssert[V any](x any) (V, bool) {
	if x == nil {
		var zero V
		return zero, false
	}
	return reflect.TypeAssert[V](reflect.ValueOf(x))
}
// internal/xreflect/typeassert_legacy.go
//go:build !go1.25

package xreflect

func TypeAssert[V any](x any) (V, bool) {
	var zero V
	if x == nil {
		return zero, false
	}
	v, ok := x.(V)
	return v, ok
}

Then replace usages:

- v, ok = reflect.TypeAssert[V](reflect.ValueOf(raw))
+ v, ok = xreflect.TypeAssert[V](raw)

If you want, I can generate a patch that updates all affected files in this PR.

Also applies to: 527-540

middleware/basicauth/basicauth.go (1)

5-5: Prefer fiber.Locals[T] over manual reflect.TypeAssert; drop reflect import

Use the shared generic helper to avoid duplicating reflection logic and importing reflect here. Behavior stays the same and code gets simpler.

Apply:

 import (
 	"encoding/base64"
-	"reflect"
 	"strings"

 	"github.com/gofiber/fiber/v3"
 	"github.com/gofiber/utils/v2"
 )
@@
 // UsernameFromContext returns the username found in the context
 // returns an empty string if the username does not exist
 func UsernameFromContext(c fiber.Ctx) string {
-	v := c.Locals(usernameKey)
-	if v == nil {
-		return ""
-	}
-	if username, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
-		return username
-	}
-	return ""
+	return fiber.Locals[string](c, usernameKey)
 }

Also applies to: 84-92

middleware/logger/default_logger.go (2)

39-43: Colored path: guard the type-assertion and use a placeholder for alignment

Today an assertion failure yields an empty timestamp printed before the delimiter. Use the boolean return and a stable placeholder to keep columns aligned.

-			ts, _ := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
+			ts, ok := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
+			if !ok || ts == "" {
+				ts = "-"
+			}
 			buf.WriteString(
 				fmt.Sprintf("%s |%s %3d %s| %13v | %15s |%s %-7s %s| %-"+data.ErrPaddingStr+"s %s\n",
 					ts,

71-74: Non-colored path: mirror the same fallback for timestamp

Currently we skip writing a timestamp if the assertion fails, which shifts the first delimiter left. Emit a placeholder instead to maintain fixed columns.

-			if ts, ok := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load())); ok {
-				buf.WriteString(ts)
-			}
+			ts, ok := reflect.TypeAssert[string](reflect.ValueOf(data.Timestamp.Load()))
+			if !ok || ts == "" {
+				ts = "-"
+			}
+			buf.WriteString(ts)
middleware/keyauth/keyauth.go (1)

6-6: Use fiber.Locals[T] to fetch the token; remove reflect import

This keeps the pattern consistent with the shared helper and avoids importing reflect here.

 import (
 	"errors"
 	"fmt"
-	"reflect"
 
 	"github.com/gofiber/fiber/v3"
 )
@@
 // TokenFromContext returns the bearer token from the request context.
 // returns an empty string if the token does not exist
 func TokenFromContext(c fiber.Ctx) string {
-	v := c.Locals(tokenKey)
-	if v == nil {
-		return ""
-	}
-	if token, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
-		return token
-	}
-	return ""
+	return fiber.Locals[string](c, tokenKey)
 }

Also applies to: 61-69

middleware/session/middleware.go (2)

142-151: Avoid reflection in hot-path pool acquisition; direct cast is faster and already safe

sync.Pool here is initialized with New: func() any { return &Middleware{} }, so it only stores *Middleware. Reflection adds overhead on every request. Consider reverting to a direct type assertion.

Apply:

-func acquireMiddleware() *Middleware {
-	v := middlewarePool.Get()
-	if v == nil {
-		panic(ErrTypeAssertionFailed.Error())
-	}
-	m, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v))
-	if !ok {
-		panic(ErrTypeAssertionFailed.Error())
-	}
-	return m
-}
+func acquireMiddleware() *Middleware {
+	return middlewarePool.Get().(*Middleware) //nolint:forcetypeassert // pool only stores *Middleware
+}

Note: If you adopt this, ErrTypeAssertionFailed and the errors import become unused in this file—remove them as part of the cleanup.


184-193: Use fiber.Locals[T] helper instead of ad-hoc reflection

You already added a generic Locals helper in req.go. Using it here removes reflection and consolidates behavior.

-func FromContext(c fiber.Ctx) *Middleware {
-	v := c.Locals(middlewareContextKey)
-	if v == nil {
-		return nil
-	}
-	m, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v))
-	if !ok {
-		return nil
-	}
-	return m
-}
+func FromContext(c fiber.Ctx) *Middleware {
+	return fiber.Locals[*Middleware](c, middlewareContextKey)
+}

If you also apply the pool suggestion above, you can drop the reflect import from this file.

middleware/csrf/session_manager.go (2)

42-46: Reduce per-request reflection by using a small helper for casting

The same reflect.TypeAssertToken pattern appears multiple times. A tiny helper keeps this localized and avoids repetition.

-	if sess != nil {
-		if v := sess.Get(sessionKey); v != nil {
-			token, ok = reflect.TypeAssert[Token](reflect.ValueOf(v))
-		}
-	}
+	if sess != nil {
+		if v := sess.Get(sessionKey); v != nil {
+			token, ok = castToken(v)
+		}
+	}

Add once in this file (outside the method):

// castToken attempts to cast v to Token using reflect.TypeAssert.
func castToken(v any) (Token, bool) {
	return reflect.TypeAssert[Token](reflect.ValueOf(v))
}
```<!-- review_comment_end -->

---

`53-55`: **DRY the store-path cast using the same helper**

Mirror the same helper usage here for consistency and easier maintenance.


```diff
-	if v := storeSess.Get(sessionKey); v != nil {
-		token, ok = reflect.TypeAssert[Token](reflect.ValueOf(v))
-	}
+	if v := storeSess.Get(sessionKey); v != nil {
+		token, ok = castToken(v)
+	}
```<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>middleware/csrf/csrf.go (2)</summary><blockquote>

`195-203`: **Prefer fiber.Locals[string] over reflect for context token retrieval**

This avoids reflection entirely and centralizes behavior in one place.


```diff
-func TokenFromContext(c fiber.Ctx) string {
-	v := c.Locals(tokenKey)
-	if v == nil {
-		return ""
-	}
-	if token, ok := reflect.TypeAssert[string](reflect.ValueOf(v)); ok {
-		return token
-	}
-	return ""
-}
+func TokenFromContext(c fiber.Ctx) string {
+	return fiber.Locals[string](c, tokenKey)
+}

If this is the only use of reflect in this file after changes, drop the reflect import.


208-217: *Likewise for Handler: use fiber.Locals[Handler] to remove reflection

Keeps it consistent with TokenFromContext and the new generic helper.

-func HandlerFromContext(c fiber.Ctx) *Handler {
-	v := c.Locals(handlerKey)
-	if v == nil {
-		return nil
-	}
-	handler, ok := reflect.TypeAssert[*Handler](reflect.ValueOf(v))
-	if !ok {
-		return nil
-	}
-	return handler
-}
+func HandlerFromContext(c fiber.Ctx) *Handler {
+	return fiber.Locals[*Handler](c, handlerKey)
+}
```<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>middleware/session/store.go (3)</summary><blockquote>

`8-12`: **Reflection import only used for TypeAssert: consider removing by using internal helpers**

If you switch to FromContext and fiber.Locals[T] (see below), reflect import becomes unnecessary in this file.
<!-- review_comment_end -->

---

`99-103`: **Use session.FromContext(c) instead of manual reflection**

It’s in the same package and encapsulates the lookup logic. Also removes the need for reflect here.


```diff
-	if v := c.Locals(middlewareContextKey); v != nil {
-		if _, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v)); ok {
-			return nil, ErrSessionAlreadyLoadedByMiddleware
-		}
-	}
+	if FromContext(c) != nil {
+		return nil, ErrSessionAlreadyLoadedByMiddleware
+	}
```<!-- review_comment_end -->

---

`127-131`: **Use fiber.Locals[string] and detect presence without reflection**

This keeps casting uniform with the rest of the codebase and avoids reflection. Presence can be detected via a raw nil check.


```diff
-	var id string
-	var ok bool
-	if v := c.Locals(sessionIDContextKey); v != nil {
-		id, ok = reflect.TypeAssert[string](reflect.ValueOf(v))
-	}
+	raw := c.Locals(sessionIDContextKey)
+	id, ok := fiber.Locals[string](c, sessionIDContextKey), raw != nil

Note: Behavior remains equivalent for “fresh” detection below, since Locals will only be populated when this request has already created an ID.

middleware/session/session.go (3)

8-15: Import reflect only if you actually need it

If you adopt the suggestions below (no reflection in acquireSession/Save), you can remove the reflect import to reduce dependencies.


57-64: Pool hot-path: direct cast is safe here and faster than reflection

Per the retrieved learnings for this codebase, sessionPool only stores *Session. Using a direct assertion avoids per-request reflection cost.

-func acquireSession() *Session {
-	obj := sessionPool.Get()
-	if obj == nil {
-		panic("unexpected type in session pool")
-	}
-	s, ok := reflect.TypeAssert[*Session](reflect.ValueOf(obj))
-	if !ok {
-		panic("unexpected type in session pool")
-	}
+func acquireSession() *Session {
+	s := sessionPool.Get().(*Session) //nolint:forcetypeassert // pool only stores *Session
 	if s.data == nil {
 		s.data = acquireData()
 	}
 	s.fresh = true
 	return s
 }

If you adopt this, remove the reflect import.


307-314: Prefer FromContext helper to avoid duplicating reflection logic

Reuse the package-level FromContext to check whether the session is in use by the middleware.

-	if v := s.ctx.Locals(middlewareContextKey); v != nil {
-		if m, ok := reflect.TypeAssert[*Middleware](reflect.ValueOf(v)); ok {
-			if m.Session == s {
-				// Session is in use, so we do nothing and return
-				return nil
-			}
-		}
-	}
+	if m := FromContext(s.ctx); m != nil && m.Session == s {
+		// Session is in use, so we do nothing and return
+		return nil
+	}

Again, this helps drop the reflect import from this file.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bbfb091 and 03d4f9f.

📒 Files selected for processing (15)
  • helpers.go (1 hunks)
  • middleware/basicauth/basicauth.go (2 hunks)
  • middleware/cache/heap.go (3 hunks)
  • middleware/cache/manager.go (4 hunks)
  • middleware/csrf/csrf.go (2 hunks)
  • middleware/csrf/session_manager.go (2 hunks)
  • middleware/keyauth/keyauth.go (2 hunks)
  • middleware/logger/default_logger.go (3 hunks)
  • middleware/logger/tags.go (3 hunks)
  • middleware/requestid/requestid.go (2 hunks)
  • middleware/session/data.go (2 hunks)
  • middleware/session/middleware.go (3 hunks)
  • middleware/session/session.go (3 hunks)
  • middleware/session/store.go (3 hunks)
  • req.go (2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2024-10-02T23:02:12.306Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/session.go:46-61
Timestamp: 2024-10-02T23:02:12.306Z
Learning: In this codebase, the `sessionPool` only contains `Session` instances, so type assertions without additional checks are acceptable.

Applied to files:

  • middleware/session/session.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#3051
File: middleware/session/session.go:215-216
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The `session.Save()` method in the `middleware/session` package returns the `Session` back to `sync.Pool`.

Applied to files:

  • middleware/session/session.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/session.go:272-293
Timestamp: 2024-10-08T19:06:06.583Z
Learning: In the `middleware/session/session.go` file, the `saveSession()` method returns either `nil` or an error, so additional error wrapping in the `Save()` method is unnecessary.

Applied to files:

  • middleware/session/session.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/csrf/session_manager.go:30-43
Timestamp: 2024-10-08T19:06:06.583Z
Learning: In the session middleware, `session.FromContext(c)` returns `*session.Middleware`, whereas `m.session.Get(c)` returns `*session.Store`, so they are not directly interchangeable.

Applied to files:

  • middleware/session/session.go
  • middleware/csrf/session_manager.go
  • middleware/session/middleware.go
  • middleware/session/store.go
📚 Learning: 2025-07-19T18:04:19.891Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3598
File: docs/middleware/csrf.md:364-366
Timestamp: 2025-07-19T18:04:19.891Z
Learning: Both `*Session` and `*Middleware` in the session package have `Destroy()` methods with the signature `func Destroy() error` that take no arguments. The method is called directly on the session middleware instance without any parameters.

Applied to files:

  • middleware/session/session.go
📚 Learning: 2024-11-10T23:44:13.704Z
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/adaptor/adaptor.go:111-111
Timestamp: 2024-11-10T23:44:13.704Z
Learning: In the `middleware/adaptor/adaptor.go` file of the Fiber framework, when updating context handling, replacing `c.Context()` with `c.RequestCtx()` is appropriate to access the `fasthttp.RequestCtx`.

Applied to files:

  • middleware/requestid/requestid.go
  • middleware/csrf/csrf.go
  • middleware/session/middleware.go
📚 Learning: 2024-11-08T04:10:42.990Z
Learnt from: gaby
PR: gofiber/fiber#3193
File: middleware/cache/cache_test.go:897-897
Timestamp: 2024-11-08T04:10:42.990Z
Learning: In the Fiber framework, `Context()` is being renamed to `RequestCtx()`, and `UserContext()` to `Context()` to improve clarity and align with Go's context conventions.

Applied to files:

  • middleware/requestid/requestid.go
🧬 Code graph analysis (9)
middleware/session/session.go (2)
req.go (1)
  • Locals (524-540)
middleware/session/middleware.go (1)
  • Middleware (14-20)
middleware/keyauth/keyauth.go (1)
req.go (1)
  • Locals (524-540)
middleware/csrf/session_manager.go (1)
middleware/csrf/token.go (1)
  • Token (9-13)
middleware/requestid/requestid.go (1)
req.go (1)
  • Locals (524-540)
middleware/csrf/csrf.go (2)
req.go (1)
  • Locals (524-540)
ctx_interface_gen.go (1)
  • Ctx (17-414)
middleware/basicauth/basicauth.go (1)
req.go (1)
  • Locals (524-540)
middleware/session/middleware.go (1)
req.go (1)
  • Locals (524-540)
middleware/logger/tags.go (1)
req.go (1)
  • Locals (524-540)
middleware/session/store.go (2)
req.go (1)
  • Locals (524-540)
middleware/session/middleware.go (1)
  • Middleware (14-20)
🪛 GitHub Check: lint
helpers.go

[failure] 505-505:
directive //nolint:errcheck // only contains headerParams is unused for linter "errcheck" (nolintlint)

middleware/cache/heap.go

[failure] 45-45:
directive //nolint:errcheck // Forced type assertion is unused for linter "errcheck" (nolintlint)


[failure] 82-82:
directive //nolint:errcheck // Forced type assertion required to implement the heap.Interface interface is unused for linter "errcheck" (nolintlint)

middleware/cache/manager.go

[failure] 58-58:
directive //nolint:errcheck // We store nothing else in the pool is unused for linter "errcheck" (nolintlint)


[failure] 96-96:
directive //nolint:errcheck // We store nothing else in the pool is unused for linter "errcheck" (nolintlint)


[failure] 111-111:
directive //nolint:errcheck // TODO: Handle error here is unused for linter "errcheck" (nolintlint)

🪛 GitHub Actions: golangci-lint
helpers.go

[error] 505-505: golangci-lint: directive //nolint:errcheck // only contains headerParams is unused for linter 'errcheck' (nolintlint).

⏰ 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: unit (1.25.x, windows-latest)
  • GitHub Check: unit (1.25.x, macos-13)
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: repeated
  • GitHub Check: Compare
🔇 Additional comments (3)
req.go (1)

527-540: LGTM: safer generic Locals with zero-value fallback

Switching to reflect.TypeAssert removes panics from bad casts and standardizes zero-value semantics. Matches the usage pattern across the PR.

middleware/cache/manager.go (1)

3-11: Please verify module setup and CI Go version

I couldn’t locate a go.mod in the repository or any CI workflow files, so it’s unclear where the module’s Go directive lives and which Go version your pipeline is actually using. Please double-check:

  • Where your go.mod file is located (root or submodule) and confirm its go directive is set to 1.25.
  • Which CI configuration file your project uses (e.g. .github/workflows/*, .gitlab-ci.yml, .circleci/config.yml, etc.) and ensure that the Go version there matches 1.25.
middleware/csrf/session_manager.go (1)

3-10: Go 1.25 reflection use: verify availability across builds

Importing reflect to use TypeAssert implies Go 1.25. Make sure the module/tooling is upgraded; otherwise, CI will fail on older runners.

You can reuse the verification script from the other comment (it scans the whole repo).

Comment thread helpers.go Outdated
Comment thread middleware/cache/heap.go Outdated
Comment thread middleware/cache/heap.go Outdated
Comment thread middleware/cache/manager.go
Comment thread middleware/cache/manager.go
Comment thread middleware/cache/manager.go
Comment thread middleware/logger/tags.go
@gaby gaby added the v3 label Aug 21, 2025
@gaby gaby added this to v3 Aug 21, 2025
@gaby gaby added this to the v3 milestone Aug 21, 2025
@ReneWerner87
Copy link
Copy Markdown
Member Author

not sure, maybe later
its not much faster

@github-project-automation github-project-automation Bot moved this to Done in v3 Aug 22, 2025
@ReneWerner87 ReneWerner87 deleted the codex/2025-08-21-11-09-58 branch January 27, 2026 12:22
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.

3 participants