13

I wanted to create one streaming API using gin-gonic server in golang.

func StreamData(c *gin.Context) {
    chanStream := make(chan int, 10)
    go func() {for i := 0; i < 5; i++ {
        chanStream <- i
        time.Sleep(time.Second * 1)
    }}()
    c.Stream(func(w io.Writer) bool {
        c.SSEvent("message", <-chanStream)
        return true
    })
}

router.GET("/stream", controller.StreamData)

But when I am trying to hit the endpoint, it just stucks and no response comes. Has someone used the stream function so that he/she can point the mistake which I might be doing. Thanks!

kunalag
  • 131
  • 1
  • 1
  • 3
  • Figured out the issue. After SSEvent, returning true is making the stream function to keep on running, after communication through channel is completed, we need to return false after that stream will get closed. – kunalag Jun 29 '17 at 13:35
  • But now the issue is, till the time stream function is running, the caller doesnt get any stream data, it gets only after the c.Stream function completes. – kunalag Jun 29 '17 at 13:36

1 Answers1

20

You should return false if the stream ended. And close the chan.

package main

import (
    "io"
    "time"

    "github.com/gin-gonic/contrib/static"
    "github.com/gin-gonic/gin"
    "github.com/mattn/go-colorable"
)

func main() {
    gin.DefaultWriter = colorable.NewColorableStderr()
    r := gin.Default()
    r.GET("/stream", func(c *gin.Context) {
        chanStream := make(chan int, 10)
        go func() {
            defer close(chanStream)
            for i := 0; i < 5; i++ {
                chanStream <- i
                time.Sleep(time.Second * 1)
            }
        }()
        c.Stream(func(w io.Writer) bool {
            if msg, ok := <-chanStream; ok {
                c.SSEvent("message", msg)
                return true
            }
            return false
        })
    })
    r.Use(static.Serve("/", static.LocalFile("./public", true)))
    r.Run()
}

Additional

Client code should be:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<script>
var stream = new EventSource("/stream");
stream.addEventListener("message", function(e){
    console.log(e.data);
});
</script>    
</body>
</html>
mattn
  • 7,571
  • 30
  • 54
  • Thanks a lot. Already figured that out.(comments below the question) But now I am facing a different issue, till the time stream function is running, the caller doesn't get any stream data, it gets only after the c.Stream function completes. Will be great, if you can help me with that. – kunalag Jun 29 '17 at 15:01
  • Do you mean that the data read from chan may be appear after timeout of response? – mattn Jun 29 '17 at 15:18
  • Right now with the current implementation, the system who hits the "/stream" receives data after 5 seconds. with all 5 events at one shot. What I want is calling system should start receiving the events after every second, response should look like a streaming API to the receiver. I hope I am clear with the problem statement this time. – kunalag Jun 29 '17 at 15:23
  • really? I make sure my implementation drain the each responses per seconds. – mattn Jun 29 '17 at 15:26
  • Your code works perfectly! Thanks. Some issue at my end. I ll check – kunalag Jun 29 '17 at 15:30
  • 2
    Found the issue....was using gzip compression in the gin router. It was not allowing it to stream the events. Commented it, works fine now. – kunalag Jun 29 '17 at 15:38
  • May I know, how to write a client to call stream API in Golang? – Ganeshdip Jul 10 '19 at 06:32