Compare commits

...

5 Commits

Author SHA1 Message Date
burybell
cd932b1d4e
Merge fa1c0ce2abbd9e5432a440688ffe076d6832d3c6 into d3ffc9985281dcf4d3bef604cce4e662b1a327a6 2026-03-18 15:48:11 +08:00
Abhiyan Khanal
d3ffc99852
test(engine): add regression tests for HandleContext with NoRoute (#4571)
Add two regression tests for GitHub issue #1848 to verify that
Context.handlers are properly isolated in HandleContext when used
from a NoRoute handler with group and engine middleware.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-03-16 20:49:59 +08:00
Bo-Yi Wu
fa1c0ce2ab
Merge branch 'master' into master 2026-02-28 10:07:54 +08:00
Bo-Yi Wu
d81699791a
Merge branch 'master' into master 2026-02-27 23:36:11 +08:00
linhujun
b0e3cdc077 fix(context): improve client disconnection handling in Stream method 2025-04-15 11:18:39 +08:00
2 changed files with 73 additions and 1 deletions

View File

@ -1327,7 +1327,7 @@ func (c *Context) SSEvent(name string, message any) {
// indicates "Is client disconnected in middle of stream"
func (c *Context) Stream(step func(w io.Writer) bool) bool {
w := c.Writer
clientGone := w.CloseNotify()
clientGone := c.Request.Context().Done()
for {
select {
case <-clientGone:

View File

@ -743,6 +743,78 @@ func TestEngineHandleContextPreventsMiddlewareReEntry(t *testing.T) {
assert.Equal(t, int64(1), handlerCounterV2)
}
func TestEngineHandleContextNoRouteWithGroupMiddleware(t *testing.T) {
// Scenario from issue #1848:
// - Engine with no global middleware (gin.New())
// - A group with middleware
// - A route in that group
// - NoRoute handler that redirects via HandleContext
// The group middleware should run exactly once per HandleContext call,
// not accumulate across redirects.
var middlewareCount, handlerCount int64
r := New()
grp := r.Group("", func(c *Context) {
atomic.AddInt64(&middlewareCount, 1)
c.Next()
})
grp.GET("/target", func(c *Context) {
atomic.AddInt64(&handlerCount, 1)
c.String(http.StatusOK, "ok")
})
r.NoRoute(func(c *Context) {
c.Request.URL.Path = "/target"
r.HandleContext(c)
})
// Access a non-existent route to trigger NoRoute -> HandleContext
w := PerformRequest(r, "GET", "/nonexistent")
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "ok", w.Body.String())
// Middleware and handler should each run exactly once
assert.Equal(t, int64(1), atomic.LoadInt64(&middlewareCount))
assert.Equal(t, int64(1), atomic.LoadInt64(&handlerCount))
}
func TestEngineHandleContextNoRouteWithEngineMiddleware(t *testing.T) {
// When engine middleware exists and NoRoute redirects via HandleContext,
// verify the handlers run the expected number of times.
var engineMwCount, groupMwCount, handlerCount int64
r := New()
r.Use(func(c *Context) {
atomic.AddInt64(&engineMwCount, 1)
c.Next()
})
grp := r.Group("", func(c *Context) {
atomic.AddInt64(&groupMwCount, 1)
c.Next()
})
grp.GET("/target", func(c *Context) {
atomic.AddInt64(&handlerCount, 1)
c.String(http.StatusOK, "ok")
})
r.NoRoute(func(c *Context) {
c.Request.URL.Path = "/target"
r.HandleContext(c)
})
w := PerformRequest(r, "GET", "/nonexistent")
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "ok", w.Body.String())
// Handler and group middleware should each run once (from HandleContext)
assert.Equal(t, int64(1), atomic.LoadInt64(&handlerCount))
assert.Equal(t, int64(1), atomic.LoadInt64(&groupMwCount))
// Engine middleware runs twice: once for the NoRoute chain, once for the HandleContext chain
// This is expected behavior since HandleContext re-enters the full handler chain
assert.Equal(t, int64(2), atomic.LoadInt64(&engineMwCount))
}
func TestEngineHandleContextUseEscapedPathPercentEncoded(t *testing.T) {
r := New()
r.UseEscapedPath = true