fasthttp / websocket

WebSocket implementation for fasthttp.
BSD 2-Clause "Simplified" License
540 stars 55 forks source link

Add context-aware upgrader #3

Closed romshark closed 6 years ago

romshark commented 6 years ago

Add a context-aware upgrader "UpgradeWithCtx" to allow passing arbitrary data (such as HTTP header values) from the HTTP handler to the WebSocket connection handler.

romshark commented 6 years ago

I faced a problem working on an experimental port of webwire-go to fasthttp. Webwire captures the user agent string of the client that's establishing the websocket connection but I didn't find a way to transfer this header from the fasthttp handler function to the fasthttp/websocket socket handler function because there's no way to identify what HTTP request a particular websocket came from. The only way is to make the upgrader pass an arbitrary context through.

Alternatively, we could've used interface{} instead of context.Context because the upgrader doesn't respect cancelation, but usually the context package is used for tunneling arbitrary data through 3rd-party APIs in Go.

Should we stick to the context package? should we use an empty interface? Or should we approach this completely differently?

savsgio commented 6 years ago

I undestood that you need the User-Agent header from ctx.RequestCtx, and pass it to the websocket's handler right? If so, you could try something like this:

func myFasthttpHandlerView(ctx *fasthttp.RequestCtx) {
        // Get the User-Agent before Upgrade connection and use it into the websocket's handler.
    userAgent := ctx.Request.Header.UserAgent()

    err := upgrader.Upgrade(ctx, func(ws *websocket.Conn) {
        defer ws.Close()
        for {
            err = ws.WriteMessage(mt, userAgent)
            if err != nil {
                log.Println("write:", err)
                break
            }
        }
    })
        ........
}

Anyway, Could you put an example, please? Because, I don't understand exactly what you need.

romshark commented 6 years ago

You're actually right, I wonder why I didn't think about using a lambda! facepalm

func (srv *server) handleHttp(ctx *fasthttp.RequestCtx) {
    userAgent := ctx.Request.Header.UserAgent()
    ua := make([]byte, len(userAgent))
    copy(ua, userAgent)

    err := upgrader.Upgrade(ctx, func(conn *websocket.Conn) {
        srv.handleConnection(ua, conn)
    })
}

This works perfectly fine (as long as you don't touch the fasthttp.RequestCtx context from inside the connection handler lambda)