I'm trying to implement Server Sent Events in Go. I'm using the Fiber framework, and found an example implementation that worked fine.
But it does not uses channels, so I cannot send events/messages whenever I want, it just keeps sending them at a specified interval.
Then I found another example which does use channels. But it does not uses Fiber, so I tried to mix the two examples. Right now, my code looks like this:
// ... imports
var sseChan chan string
var wg sync.WaitGroup
// main...
func sseHandler(c *fiber.Ctx) error {
c.Set("Content-Type", "text/event-stream")
c.Set("Cache-Control", "no-cache")
c.Set("Connection", "keep-alive")
c.Set("Transfer-Encoding", "chunked")
sseChan = make(chan string)
// wg.Add(1)
defer func() {
// wg.Done()
close(sseChan)
sseChan = nil
fmt.Println("defer func called")
}()
fmt.Println("client connected")
c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
for {
fmt.Println("inside streamwrite loop")
select {
case message := <-sseChan:
fmt.Println("got a msg on sseChan")
fmt.Fprintf(w, "data: %s\n\n", message)
err := w.Flush()
if err != nil {
// Refreshing page in web browser will establish a new
// SSE connection, but only (the last) one is alive, so
// dead connections must be closed here.
fmt.Printf("Error while flushing: %v. Closing http connection.\n", err)
break
}
case <-c.Context().Done():
fmt.Printf("Client closed connection")
return
}
}
}))
// wg.Wait()
fmt.Println("returnin")
return nil
}
func fireEvent(c *fiber.Ctx) error {
if sseChan != nil {
fmt.Println("sendin msg")
msg := time.Now().Format("15:04:05")
sseChan <- msg
}
return c.SendString("event fired")
}
When I try to establish a connection from browser (using EventSource
API), the backend throws this error:
client connected
returnin
defer func called
inside streamwrite loop
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x560 pc=0x6f157a]
goroutine 4 [running]:
github.com/valyala/fasthttp.(*RequestCtx).Done(...)
/home/user/go/pkg/mod/github.com/valyala/fasthttp@v1.45.0/server.go:2739
main.sseHandler.func2(0xc00013ebe0?)
/home/user/Projects/scaling-sse/api-go/main.go:71 +0x9a
github.com/valyala/fasthttp.NewStreamReader.func1()
I can see that the defer functions gets called before the loop in StreamWriter. That results in the sseChan
being nil
. So I added the wg
statements (commented in sseHandler
), which prevents the error but I don't get any output in my browser. Also, on closing the tab, the defer function doesn't gets called.