Recently upgraded the excellent alexedwards/scs/v2 within a Go webapp from 2.5.0 to 2.7.0 to allow for Go 1.20+ support for http.NewResponseController(). Subsequently to allow for extending handler specific server write timeouts for large file uploads. This is good. An unfortunate consequence of this necessary upgrade is the loss of websocket functionality, currently using Gorilla websockets v1.5.1 (latest). I get the common error produced on attempted connection upgrade... websocket: response does not implement http.Hijacker I've researched several leads for this, a few here on SO but I can't resolve anything yet which is specific to this case. I've tried a few attempts at implementing http.Hijacker alongside my other middleware code and within my websocket endpoint handler (commented out, 'misguided' example attached for illustration)... I just don't think I understand this well enough...
type WebsocketConnection struct {
*websocket.Conn
}
type WebsocketPayload struct {
Action string `json:"action"`
Message string `json:"message"`
AdminName string `json:"admin_name"`
MessageType string `json:"message_type"`
AdminID int `json:"admin_id"`
Connection WebsocketConnection `json:"-"`
}
type WebsocketJSONResponse struct {
Action string `json:"action"`
Message string `json:"message"`
AdminName string `json:"admin_name"`
AdminID int `json:"admin_id"`
CurrentLogins []string `json:"current_logins"`
}
var upgradeConnection = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
var webClients = make(map[WebsocketConnection]string)
var websocketChannel = make(chan WebsocketPayload)
func (app *application) WebsocketEndPoint(w http.ResponseWriter, r *http.Request) {
// rc := http.NewResponseController(w)
// netConn, _, err := rc.Hijack()
// if err != nil {
// app.errorLog.Println(err)
// return
// }
// defer netConn.Close()
app.debugLog.Printf("w's type is %T\n", w)
ws, err := upgradeConnection.Upgrade(w, r, nil)
if err != nil {
app.errorLog.Println(err) <= ERROR HERE
return
}
app.infoLog.Printf("Web client connected from %s", r.RemoteAddr)
var response WebsocketJSONResponse
response.Message = "Connected to server..."
err = ws.WriteJSON(response)
if err != nil {
app.errorLog.Println(err)
return
}
conn := WebsocketConnection{Conn: ws}
webClients[conn] = ""
go app.ListenForWebsocket(&conn)
}
"w" in this case is *scs.sessionResponseWriter. How do I implement http.Hijacker in the response without wrecking the connection and get the upgrade made ? What is the correct approach please ?
Version 2.5.0 of the alexedwards/scs/v2 package supports the Hijacker interface. Version 2.7.0 does not support the Hijacker interface, but it does support support response controllers. The gorilla websocket package does not use response controllers.
Option 1
Use 2.5.0 of the alexedwards/scs/v2 package.
Option 2
Work around the gorilla package's defect by unwrapping the response writer in your application code:
func (app *application) WebsocketEndPoint(w http.ResponseWriter, r *http.Request) {
wupgrade := w
if u, ok := w.(interface{ Unwrap() http.ResponseWriter }); ok {
wupgrade = u.Unwrap()
}
app.debugLog.Printf("w's type is %T\n", w)
ws, err := upgradeConnection.Upgrade(wupgrade, r, nil)
if err != nil {
app.errorLog.Println(err)
return
}
app.infoLog.Printf("Web client connected from %s", r.RemoteAddr)
var response WebsocketJSONResponse
response.Message = "Connected to server..."
err = ws.WriteJSON(response)
if err != nil {
app.errorLog.Println(err)
return
}
conn := WebsocketConnection{Conn: ws}
webClients[conn] = ""
go app.ListenForWebsocket(&conn)
}