Skip to content

Conversation

@Fluder-Paradyne
Copy link

Hey, I’ll be honest — I used AI to create this PR.
I’m still trying to understand what’s happening, but this PR might help with root causing or solving #45, and it might also fix my issue #81.

…inish/timeout; restore fast-path BufferPool; tests pass with -race
@Fluder-Paradyne
Copy link
Author

it is passing all the tests, except Bearer PR Check for using unsafe.
pr in my forked repo : Fluder-Paradyne#6

…anicking

Reason: re-panicking in non-debug mode depended on Gin's recovery defer still being in scope. Under certain timings (goroutine scheduling, recovery frame unwinding), the re-panic escaped recovery and intermittently failed tests (e.g., TestTimeoutPanic with high -count). We now always handle the panic inside the timeout middleware by writing a 500 with 'panic caught: <value>' and only include the stack trace when gin.IsDebugging(). This removes timing-dependent behavior, avoids double-recovery, and keeps the rest of the middleware semantics intact (chain continues on copied context, main goroutine flushes/aborts). Result: deterministic behavior and passing tests under -race and high iteration counts.
@codecov-commenter
Copy link

codecov-commenter commented Nov 30, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.62%. Comparing base (1d781d2) to head (262c645).
⚠️ Report is 51 commits behind head on master.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #82      +/-   ##
==========================================
- Coverage   95.27%   90.62%   -4.66%     
==========================================
  Files           4        4              
  Lines         127      160      +33     
==========================================
+ Hits          121      145      +24     
- Misses          4       12       +8     
- Partials        2        3       +1     
Flag Coverage Δ
go-1.18 ?
go-1.21 ?
go-1.24 90.62% <100.00%> (?)
go-1.25 90.62% <100.00%> (?)
race-false 90.62% <100.00%> (?)
race-true 90.62% <100.00%> (?)
ubuntu-latest 90.62% <100.00%> (-4.66%) ⬇️

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.

@appleboy appleboy requested a review from Copilot November 30, 2025 04:15
Copy link

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@appleboy appleboy added the bug Something isn't working label Nov 30, 2025
@appleboy appleboy requested a review from Copilot December 1, 2025 04:22
Copy link

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 2 out of 2 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +146 to +167
func cloneHandlerState(dst, src *gin.Context) {
vdst := reflect.ValueOf(dst).Elem()
vsrc := reflect.ValueOf(src).Elem()

// Copy handlers slice
srcHandlersField := vsrc.FieldByName("handlers")
dstHandlersField := vdst.FieldByName("handlers")
if srcHandlersField.IsValid() && dstHandlersField.IsValid() {
srcHandlers := reflect.NewAt(srcHandlersField.Type(), unsafe.Pointer(srcHandlersField.UnsafeAddr())).Elem()
dstHandlers := reflect.NewAt(dstHandlersField.Type(), unsafe.Pointer(dstHandlersField.UnsafeAddr())).Elem()
dstHandlers.Set(srcHandlers)
}

// Copy index
srcIndexField := vsrc.FieldByName("index")
dstIndexField := vdst.FieldByName("index")
if srcIndexField.IsValid() && dstIndexField.IsValid() {
srcIndex := reflect.NewAt(srcIndexField.Type(), unsafe.Pointer(srcIndexField.UnsafeAddr())).Elem()
dstIndex := reflect.NewAt(dstIndexField.Type(), unsafe.Pointer(dstIndexField.UnsafeAddr())).Elem()
dstIndex.Set(srcIndex)
}
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Using reflection and unsafe pointer manipulation to access private fields of gin.Context is extremely fragile and will break if Gin's internal structure changes. This approach couples the middleware tightly to Gin's implementation details. Consider checking if Gin's Copy() method already handles handler state, or propose an enhancement to Gin's API to support this use case officially.

Copilot uses AI. Check for mistakes.
}
// In non-debug mode, re-throw the original panic value to be handled by the upper middleware.
panic(pi.Value)
return
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Changing panic behavior from re-panic to swallowing the panic is a breaking change that alters how errors propagate through the middleware chain. Previously, non-debug mode allowed upstream middleware to handle panics; now all panics are caught and converted to 500 responses. This could break existing error handling strategies in applications using this middleware.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +25
p.mu.Lock()
if p.last != nil {
b := p.last
p.last = nil
p.mu.Unlock()
return b
}
p.mu.Unlock()
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The fast-path optimization introduces mutex contention on every Get() call, which may negate the benefit of avoiding sync.Pool. Under high concurrency, this mutex could become a bottleneck. Consider using atomic operations or sync.Pool's inherent per-P caching instead.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to 36
// Put adds a buffer back to the pool without resetting it. The caller is
// responsible for calling Reset() when appropriate.
func (p *BufferPool) Put(buf *bytes.Buffer) {
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The documentation states 'The caller is responsible for calling Reset() when appropriate,' but this introduces potential bugs if callers forget to reset buffers. The previous behavior or current usage pattern should be clarified, and ideally Reset() should be called within Put() to prevent dirty buffers from being reused.

Suggested change
// Put adds a buffer back to the pool without resetting it. The caller is
// responsible for calling Reset() when appropriate.
func (p *BufferPool) Put(buf *bytes.Buffer) {
// Put adds a buffer back to the pool, automatically resetting it to prevent
// dirty buffers from being reused.
func (p *BufferPool) Put(buf *bytes.Buffer) {
buf.Reset()

Copilot uses AI. Check for mistakes.
bufPool.Put(buffer)

// Prevent Gin from executing subsequent handlers; we've already run them via cCopy
c.Abort()
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The addition of c.Abort() after successful handler completion is a significant behavioral change that should be covered by tests to verify that subsequent middleware is not executed after timeout handlers complete.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants