Skip to content

Unsafe type assertions in Hijack() and CloseNotify() cause panic with http.TimeoutHandler #4638

@jassus213

Description

@jassus213

Description

Problem

Similar to #4460 (fixed for Flush() in #4479), Hijack() and CloseNotify() still use direct type assertions that panic when the underlying writer doesn't implement the interface:

// panics with http.TimeoutHandler (timeoutWriter is not http.Hijacker)
return w.ResponseWriter.(http.Hijacker).Hijack()

// panics with http.TimeoutHandler (timeoutWriter is not http.CloseNotifier)
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()

Environment

  • Go version: go1.25.0 linux/amd64 - Gin version: v1.12.0
  • OS: Linux 6.17.0 (Ubuntu)

Expected behavior

Same graceful degradation already implemented in Flush() — return error / nil instead of panicking.

Gin Version

d3ffc99

Can you reproduce the bug?

Yes

Source Code

  package main                                                                                                                                                                                
                                                                   
  import (                                                                                                                                                                                    
      "net/http"                                                                   
      "net/http/httptest"                                                                                                                                                                     
      "time"
      "testing"                                                                                                                                                                               
                                                                                   
      "github.com/gin-gonic/gin"         
  )                                                         
                                                          
  func TestTimeoutHandlerHijackPanic(t *testing.T) {               
      r := gin.New()                  
      r.GET("/ws", func(c *gin.Context) {
          // Any handler that attempts to hijack the connection (e.g. WebSocket upgrade)                                                                                                      
          _, _, err := c.Writer.Hijack()                    
          if err != nil {                                                                                                                                                                     
              c.String(http.StatusInternalServerError, err.Error())                
          }                                                        
      })                                                    
                                                                                                                                                                                              
      // Wrapping with http.TimeoutHandler is common for adding global request timeouts.                                                                                                      
      // timeoutWriter (internal to net/http) does NOT implement http.Hijacker.                                                                                                               
      handler := http.TimeoutHandler(r, 5*time.Second, "timeout")                                                                                                                             
                                                                                                                                                                                              
      req := httptest.NewRequest(http.MethodGet, "/ws", nil)
      w := httptest.NewRecorder()                                                                                                                                                             
                                                                                                                                                                                              
      // Panics: interface conversion: *http.timeoutWriter is not http.Hijacker
      handler.ServeHTTP(w, req)                                                                                                                                                               
  }

Go Version

go1.25.0 linux/amd64

Operating System

Linux 6.17.0-20-generic

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugFound something you weren't expecting? Report it here!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions