kataras / neffos

A modern, fast and scalable websocket framework with elegant API written in Go
http://bit.ly/neffos-wiki
MIT License
571 stars 47 forks source link

[INFO] How to integrate with Echo framework #9

Open xXLokerXx opened 5 years ago

xXLokerXx commented 5 years ago

Hi @kataras, I started to use neffos nearly weak ago and i really like it, where i work we decide to move into real time aplication, and i searched for a good socket package, i already used iris and i really like how it works, i was working with websockets of iris and when i updated iris i see some errors (i was in trubles before because updates of other libraries we use), then i read the documentation and i've changed all.

I also integrate the sockets with echo framework and i wold like to leave here how i made it for anyone who need it and also i think it will be good for this greate library if they have examples of implementatios with other to.

Then here is the code:

package main

import (
    "github.com/labstack/echo"
        "github.com/kataras/neffos"
    "github.com/kataras/neffos/gorilla"
    "github.com/labstack/echo"
)

type serverConn struct {
    *neffos.NSConn
}

func main() {
        router := echo.New()

        controller := new(serverConn)
        events := neffos.NewStruct(controller).
        SetNamespace("default").
        SetEventMatcher(neffos.EventTrimPrefixMatcher("On"))

        websocketServer := neffos.New(
        gorilla.DefaultUpgrader, /* DefaultGobwasUpgrader can be used too. */
        events)

       // Wrap the web socket handler with echo function to be compatible, both use http library to work
       router.Any("/echo", echo.WrapHandler(websocketServer), deleteOrigin)

        if err := router.Start(":8080"); err != nil {
        log.Fatalf("error in ListenAndServe: %s", err)
    }
}

// deleteOrigin allows others origins than local access to sockets
// if we avoid this handler then we can't connect from servers in other ports or routes
// NOTE: This isn't the best way to do it, but i can't found another way to do it.
func deleteOrigin(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        c.Request().Header.Del("Origin")
        return next(c)
    }
}

func (c *serverConn) OnNamespaceConnected(msg neffos.Message) error {
    log.Printf("[%s] connected to namespace [%s]",
        c.Conn, msg.Namespace)
    return nil
}

func (c *serverConn) OnNamespaceDisconnect(msg neffos.Message) error {
    log.Printf("[%s] disconnected from namespace [%s]", c.Conn, msg.Namespace)
    return nil
}

func (c *serverConn) OnChat(msg neffos.Message) error {
    log.Printf("[%s] sent: %s", c.Conn, string(msg.Body))
    c.Conn.Server().Broadcast(c.Conn, msg)
    return nil
}

This is only for the server file, if anyone want to test it use with the files in the route: _examples/basic/browser

One las thing: I don't know how to acces to the context of the request.

kataras commented 5 years ago

Looks great @xXLokerXx, thanks, this library is designed for every Gopher.

About your question on

One las thing: I don't know how to acces to the context of the request.

There are many ways but the easiest is to wrap the neffos.Socket with a custom socket structure and create a custom echo Handler which will put the the original Echo Context to that socket wrapper.

You can see how Iris does that at: https://github.com/kataras/iris/blob/4e1c4ac355ad0f0b176ff223ae70e3f32a71bd4a/websocket/websocket.go#L151-L192

An alternative way would be to use the Request().WithContext and provide a key and a value which will be the echo Context but I really don't recommend that one.

xXLokerXx commented 4 years ago

I just did a change: from this: router.GET("/echo", echo.WrapHandler(websocketServer), deleteOrigin) to this: router.Any("/echo", echo.WrapHandler(websocketServer), deleteOrigin)

xXLokerXx commented 4 years ago

@kataras I have another problem with sockets, the socket is disconnect and reconnect a lot of times and i don't know why

kataras commented 4 years ago

Hello @xXLokerXx, is this the neffos.js javascript client library? If so please check the reconnect option and try to remove it or increase its value. Neffos is trying to re-connect on networks errors (in browser and nodejs client). Is this a backend (main neffos websocket server) issue? Do you have a code I can take a look of?

Thanks,

xXLokerXx commented 4 years ago

@kataras yes i've working with neffos.js and nefos (for go), i've got the next error:

goroutine 1272113 [running]:
runtime.throw(0xe3003b, 0x15)
        /usr/local/go/src/runtime/panic.go:774 +0x72 fp=0xc0001a38e8 sp=0xc0001a38b8 pc=0x42f8d2
runtime.mapdelete_faststr(0xd1a2a0, 0xc0001d8120, 0xc0008a02ef, 0x2)
        /usr/local/go/src/runtime/map_faststr.go:306 +0x386 fp=0xc0001a3950 sp=0xc0001a38e8 pc=0x414f36
github.com/server/socket.(*serverConn).OnNamespaceDisconnect(0xc000010148, 0x0, 0x0, 0xc000c401c0, 0x7, 0x0, 0x0, 0xe30ffd, 0x16, 0x0, ...)
        /go/src/github.com/server/socket/socket.go:63 +0xe5 fp=0xc0001a39a8 sp=0xc0001a3950 pc=0x96cc95
runtime.call256(0xc0008253b0, 0xc0009d41f8, 0xc000930000, 0xb8000000c8)
        /usr/local/go/src/runtime/asm_amd64.s:542 +0x55 fp=0xc0001a3ab8 sp=0xc0001a39a8 pc=0x45a3f5
reflect.callMethod(0xc007754bc0, 0xc0001a3b98, 0xc0001a3b80)
        /usr/local/go/src/reflect/value.go:714 +0x1f0 fp=0xc0001a3b68 sp=0xc0001a3ab8 pc=0x48bf00
reflect.methodValueCall(0x0, 0x0, 0xc000c401c0, 0x7, 0x0, 0x0, 0xe30ffd, 0x16, 0x0, 0x0, ...)
        /usr/local/go/src/reflect/asm_amd64.s:35 +0x42 fp=0xc0001a3b98 sp=0xc0001a3b68 pc=0x4957b2
github.com/kataras/neffos.makeEventFromMethod.func1(0xc0007a80c0, 0x0, 0x0, 0xc000c401c0, 0x7, 0x0, 0x0, 0xe30ffd, 0x16, 0x0, ...)
        /go/src/github.com/kataras/neffos/reflect.go:208 +0xbf fp=0xc0001a3c68 sp=0xc0001a3b98 pc=0x95217f
github.com/kataras/neffos.Events.fireEvent(0xc0001d8180, 0xc0007a80c0, 0x0, 0x0, 0xc000c401c0, 0x7, 0x0, 0x0, 0xe30ffd, 0x16, ...)
        /go/src/github.com/kataras/neffos/conn_handler.go:41 +0x16a fp=0xc0001a3d40 sp=0xc0001a3c68 pc=0x9469da
github.com/kataras/neffos.(*Conn).Close(0xc001394400)
        /go/src/github.com/kataras/neffos/conn.go:1023 +0x1a8 fp=0xc0001a3f50 sp=0xc0001a3d40 pc=0x946658
github.com/kataras/neffos.(*Conn).startReader(0xc001394400)
        /go/src/github.com/kataras/neffos/conn.go:335 +0x169 fp=0xc0001a3fd8 sp=0xc0001a3f50 pc=0x942419
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1357 +0x1 fp=0xc0001a3fe0 sp=0xc0001a3fd8 pc=0x45bf41
created by github.com/kataras/neffos.(*Server).Upgrade
        /go/src/github.com/kataras/neffos/server.go:340 +0x6c2

and i use the next code in backend:

package socket

import (
    "fmt"
    "log"
    "net/http"

    "github.com/kataras/neffos"
    "github.com/kataras/neffos/gobwas"
    "github.com/labstack/echo"
)

type serverConn struct {
    *neffos.NSConn
}

var (
    Clients         = make(map[string]*serverConn)
    websocketServer *neffos.Server
)

func init() {
    controller := new(serverConn)
    events := neffos.NewStruct(controller).
        SetNamespace("default").
        // This will convert the "OnChat" method to a "Chat" event instead.
        SetEventMatcher(neffos.EventTrimPrefixMatcher("On"))

    websocketServer = neffos.New(
        gobwas.DefaultUpgrader,
        // gorilla.DefaultUpgrader, /* DefaultGobwasUpgrader can be used too. */
        // gorilla.Upgrader(gorillaWs.Upgrader{CheckOrigin: func(*http.Request) bool { return true }}),
        events)
}

func SocketHandler(router *echo.Echo) {
    websocketServer.IDGenerator = func(w http.ResponseWriter, r *http.Request) string {
        if userID := r.Header.Get("idUser"); userID != "" {
            return userID
        }

        return neffos.DefaultIDGenerator(w, r)
    }
    router.Any("/echo", echo.WrapHandler(websocketServer))
}

func (c *serverConn) OnNamespaceConnected(msg neffos.Message) error {
    // ctx := websocket.GetContext(c.Conn)

    fmt.Printf("%v\n", c.Conn.ID())

    Clients[c.Conn.ID()] = c

    log.Printf("[%s] connected to namespace [%s] with IP [%s]",
        c.Conn, msg.Namespace)
    return nil
}

func (c *serverConn) OnNamespaceDisconnect(msg neffos.Message) error {
    log.Printf("[%s] disconnected from namespace [%s]", c.Conn, msg.Namespace)
    delete(Clients, c.Conn.ID())
    return nil
}

func (c *serverConn) OnRoomJoined(msg neffos.Message) error {
    text := fmt.Sprintf("[%s] joined to room [%s].", c, msg.Room)
    log.Printf("%s", text)

    // notify others.
    c.Conn.Server().Broadcast(c, neffos.Message{
        Namespace: msg.Namespace,
        Room:      msg.Room,
        Event:     "Notify",
        Body:      []byte(text),
    })

    return nil
}

func (c *serverConn) OnChat(msg neffos.Message) error {
    log.Printf("[%s] sent: %s", c.Conn, string(msg.Body))
    c.Conn.Server().Broadcast(c.Conn, msg)
    return nil
}

func (c *serverConn) OnNotification(msg neffos.Message) error {
    log.Printf("[%s] sent: '%s' to Notification", c.Conn, string(msg.Body))
    c.Conn.Server().Broadcast(c.Conn, msg)
    return nil
}