Description

Version 1.11.0 does not work anymore with github.com/coder/websocket, because it rejects writting headers before Hijack().

In github.com/coder/websocket a fix has been made in 2019 to work with Gin, which calls WriteHeaderNow() before Hijack() in its Accept() function. But responseWriter.Hijack() first tests for w.Written(), which returns true as size is 0, and returns an error (errHijackAlreadyWritten).

So, for maximum compatibility, and unless there is a good reason to block this, I think you should change if w.Written() { to if w.size>0 {.

Gin Version

1.11.0

Can you reproduce the bug?

Yes

Source Code

package main

import (
    "context"
    "log"
    "time"

    "github.com/coder/websocket"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.Any("/ws", func(c *gin.Context) {
        ws, err := websocket.Accept(c.Writer, c.Request, nil)
        if err != nil {
            // This is were we receive errHijackAlreadyWritten
            log.Print("Websocket upgrade failed: ", err)
            return
        }
        defer ws.Close(websocket.StatusNormalClosure, "")
    })

    go r.Run()

    // Let the server start...
    time.Sleep(time.Second)

    // Try to open the websocket
    ws, _, err := websocket.Dial(context.Background(), "ws://localhost:8080/ws", nil)
    if err != nil {
        log.Fatalf("FAILED to dial websocket: %v", err)
    }
    defer ws.Close(websocket.StatusGoingAway, "leaving")
}

Go Version

1.25.1

Operating System

UbuntU 24.04

Comment From: appleboy

See the PR: https://github.com/gin-gonic/gin/pull/4373