gosegmentation-faultmutexfibers

Global Variable Gives SIGSEGV


I am using Fiber to develop a backend. I have a map that is a global variable that holds the socket connections. When I use the global variable from the same package, no problem here, everything works fine. But, when I try to use the sockets from a route function, I am getting the error below.

I tried to use mutex.lock but no luck.

I checked the code, the socket is not nil in my sendToAll method but it becomes nil in the helper method( inside the lib: github.com/fasthttp/websocket.(*Conn).WriteMessage )

Any advice is welcome.

Thanks.


type ConnectedSocketsContainerType struct {
    M sync.Mutex
    ConnectedSockets map[string]*websocket.Conn
}

var ConnectedSocketsContainer = ConnectedSocketsContainerType{ M:sync.Mutex{} , ConnectedSockets: make(map[string]*websocket.Conn) }

In another package in GET request handler calls that method:

func send(socketID string,message string)  {
    sockethub.ConnectedSocketsContainer.M.Lock()
    sendToAll(message)
    sockethub.ConnectedSocketsContainer.M.Unlock()
}
func sendToAll(message string)  {
    for k := range sockethub.SocketsIDs {
        k.WriteMessage(1, []byte(message))
    }
}

The error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x14d27f8]

goroutine 6 [running]:
github.com/fasthttp/websocket.(*Conn).WriteMessage(0xc00006aa78, 0xc00045e115, {0xc0004540f0, 0x29, 0xc00006ab2f})
        /Users/emre/go/pkg/mod/github.com/fasthttp/websocket@v1.4.3-rc.10/conn.go:753 +0x38
goserver/controllers/api.sendToAll({0xc00045e115, 0x29})
        /Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:11 +0xac
goserver/controllers/api.send({0xc000456000, 0x15edfe1}, {0xc00045e115, 0x0})
        /Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:22 +0x65
goserver/controllers/api.SendMessageController(0xc000128a50)
        /Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:29 +0x71
github.com/gofiber/fiber/v2.(*App).next(0xc00019cb60, 0xc000456000)
        /Users/emre/go/pkg/mod/github.com/gofiber/fiber/v2@v2.23.0/router.go:127 +0x1d8
github.com/gofiber/fiber/v2.(*App).handler(0xc00019cb60, 0x10bb517)
        /Users/emre/go/pkg/mod/github.com/gofiber/fiber/v2@v2.23.0/router.go:155 +0xe5
github.com/valyala/fasthttp.(*Server).serveConn(0xc000126000, {0x16c4fa0, 0xc0000106e8})
        /Users/emre/go/pkg/mod/github.com/valyala/fasthttp@v1.31.0/server.go:2278 +0x122d
github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc00014c000, 0xc00022dba0)
        /Users/emre/go/pkg/mod/github.com/valyala/fasthttp@v1.31.0/workerpool.go:223 +0xa9
github.com/valyala/fasthttp.(*workerPool).getCh.func1()
        /Users/emre/go/pkg/mod/github.com/valyala/fasthttp@v1.31.0/workerpool.go:195 +0x38
created by github.com/valyala/fasthttp.(*workerPool).getCh
        /Users/emre/go/pkg/mod/github.com/valyala/fasthttp@v1.31.0/workerpool.go:194 +0x1b5
exit status 2

Full example for go server. Please see two comments that specify working and not working code blocks.

package main

import (
    "fmt"
    "sync"

    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/websocket/v2"
)

func main() {

    app := fiber.New()

    ListenSocket(app)
    app.Get("/socket/send", SendMessageController )
    app.Listen(":3000")

}
const websocketHeaderKey = "Sec-Websocket-Key"

var ConnectedIDSockets sync.Map 

func SendMessageController( c *fiber.Ctx ) error {
    ConnectedIDSockets.Range(func(key, value interface{}) bool {
        c := value.(*websocket.Conn)
        
        if c == nil {
            // that line is not printed, c is not nil.
            fmt.Println("c is nil.")
            return true
        }

        // we have crash at that line, even we read the err.
        err := c.WriteMessage(websocket.TextMessage, []byte("message"))

        // program does not runs to here since it crashed.
        println("err:", err)
        return true
    })
    return nil
}

func ListenSocket(app *fiber.App) {
    app.Use("/ws", func(c *fiber.Ctx) error {
        if websocket.IsWebSocketUpgrade(c) {
            c.Locals("allowed", true)
            c.Locals(websocketHeaderKey, string(c.Request().Header.Peek(websocketHeaderKey)))
            return c.Next()
        }
        return fiber.ErrUpgradeRequired
    })

    app.Get("/ws/:projectKEY", websocket.New(func(c *websocket.Conn) {

        socketID := c.Locals(websocketHeaderKey).(string)

        ConnectedIDSockets.Store(socketID, c)

        // that works.
        conn, _ := ConnectedIDSockets.Load(socketID)
        socketmap := conn.(*websocket.Conn)
        socketmap.WriteMessage(1, []byte(socketID))

    }))
}


Solution

  • This panic is confusing because there are actually two packages called websocket. One in github.com/gofiber/websocket/v2 and another one in github.com/fasthttp/websocket, and both have their own *websocket.Conn. However the websocket.Conn in github.com/gofiber/websocket actually embeds the websocket.Conn from github.com/fasthttp/websocket (I know, terrible design) making what's going on unclear.

    Your call to c.WriteMessage is actually going to c.Conn.WriteMessage, and c.Conn is what's nil. So in your nil check, you actually need to do if c == nil || c.Conn == nil { to check the embedded struct as well.