I am experimenting with several new APIs:

c.Stream(stepFunction)
c.SSEvent(name, message)

I expect this will help developers to build simple and stable streaming correctly with Go and Gin.

I created a realtime-chat example: https://github.com/gin-gonic/gin/blob/develop/examples/realtime-chat/main.go

The implementation: https://github.com/gin-gonic/gin/blob/470b7e1010b3ceee8574fda670143f73710a04e5/context.go#L383-L412 https://github.com/gin-gonic/gin/blob/470b7e1010b3ceee8574fda670143f73710a04e5/render/ssevent.go

Feedback is welcome!

Comment From: manucorporat

c.Stream() is a general purpose abstraction for streaming, you can stream anything, text, json, a video! this is NOT websockets, it is a HTML request, the connection is never upgraded. It is NOT full-duplex.

c.SSEvent() is an implementation of Server-Sent Events! a very cool and simple tool for event-driven streaming. http://www.html5rocks.com/en/tutorials/eventsource/basics/

Comment From: bradrydzewski

I've been playing with this the past few days and it is great. The server-sent-event implementation was really simple to get working. I would love to see the Event.Id and Event.Retry parameters exposed through gin. I ended just using the sse library directly in conjunction with the Stream, which worked fine as well:

    var id int
    c.Stream(func(w io.Writer) bool {
        id++
        sse.Encode(w, sse.Event{
            Id: strconv.Itoa(id),
            Event: "message",
            Data:  ...,
        })
        return true
    })

Comment From: manucorporat

I am redesigning the c.Render() method. c.Render() is the "parent" of c.JSON, c.XML, c.HTML... Right now it was "slow" to use that function, since it used variadic arguments (compiler inlining can not be applied and an additional heap allocation is required). Also it used interface{} a lot, the compiler can not verify the types.

I have proposed a new API. - No variadic - No heap allocation - Static typed (you can detect bugs at compiler time)

The new API is what you probably are interested in:

you can still use c.SSEvent(code, eventName, data)

or use sse.Event directly:

c.Render(200, sse.Event{
    Name: "my event",
    Id: "1255",
    Data: data,
})

^^^this should be what you are looking for!!

Comment From: manucorporat

Another examples: c.JSON is a shortcut for

c.Render(200, render.JSON{data})

The Gin API is changing a lot right now, but it will be frozen in v1.

https://github.com/gin-gonic/gin/commit/947b53d4a26f24d9842fd1539e8a65fee590a8a8 https://github.com/manucorporat/sse/commit/320b4a6ccac258cfc2479ef91a27505d98c96949

Comment From: djamelfel

@manucorporat they are a non sense for me, sse.Event cannot be used as type render.Render in argument to c.Render

Comment From: manucorporat

@djamelfel are you sure? yes, it can be.

Just tried:

    r.GET("/ping", func(c *gin.Context) {
        c.Render(-1, sse.Event{
            Event: "hola",
        })
    })
go get -u github.com/gin-gonic/gin
go get -u github.com/manucorporat/sse

Comment From: djamelfel

You're right, it's because i used an old version of gin.

Comment From: metal3d

Same problem as @dalu excepting that the problem for me is only behind nginx. I cannot "flush" to output.

Comment From: metal3d

@dalu sorry for the latency, Actually I found the exact same solution myself weeks ago :)

Thanks a lot.

Comment From: whybeyoung

@dalu sorry for the latency, Actually I found the exact same solution myself weeks ago :)

Thanks a lot.

@metal3d what's the solution? can you describe it ?