1

My server code in golang

package main

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

    "github.com/rs/cors"
)

func main() {
    mux := http.NewServeMux();

    mux.HandleFunc("/sse" , handleSse)

    c  := cors.New(cors.Options{
            AllowedOrigins:   []string{"*"},
            AllowedMethods:   []string{http.MethodGet, http.MethodPost,
                        http.MethodDelete , http.MethodPut},
            AllowCredentials: true,
        })

    handler := c.Handler(mux)

    log.Fatal(http.ListenAndServe(":6969" , handler))
}

func handleSse(w http.ResponseWriter , r * http.Request){

    w.Header().Set("Content-type","text/event-stream")
    w.Header().Set("Cache-Control","no-cache")
    w.Header().Set("Connection","keep-alive")

    f , ok := w.(http.Flusher);
    if !ok{
    http.Error( w , "SSE not supported" ,
        http.StatusBadRequest)
    return;
    }

    fmt.Fprintf(w,"data:%v\n\n","sample data");
    f.Flush();
}

client side code

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>SSE</title>
  </head>
  <body>
    SSE running
    <script>
    const event = new EventSource("http://localhost:6969/sse");
    event.onmessage = () =>{
        console.log("this dude is slow");
    };
    </script>
  </body>
</html>

The problem I have that in network tab new text stream or response comes after 5.4 sec later. I want server to send response every 2 second I have tried infinite for loop in server code which is shown in some tutorial and it doesn't works

EDIT : The for loop handler function


func handleSse(w http.ResponseWriter , r * http.Request){

    w.Header().Set("Content-type","text/event-stream")
    w.Header().Set("Cache-Control","no-cache")
    w.Header().Set("Connection","keep-alive")
    w.WriteHeader(http.StatusOK)

    f , ok := w.(http.Flusher);
    if !ok{
    http.Error( w , "SSE not supported , IE6 bruh" ,
        http.StatusBadRequest)
    return;
    }

    for i := 0 ; i < 10 ; i++{
        fmt.Fprintln(w,"retry : 1000"); //This line also doesnot help
        fmt.Fprintf(w,"data :%v\n\n","Sorry");
        f.Flush();
    //time.Sleep(1 * time.Second) //This line increase delay to 25 secs
    }
}
2
  • "I have tried infinite for loop in server code" - show us that. Your current code will send one message and then close the connection (so the delay between messages is the reconnection time; you can prove this by adding fmt.Fprintln(w, "retry: 1000") above the line that sends data). With SSE you want to keep the connection open - see this answer for a good example.
    – Brits
    Commented Jul 7 at 21:32
  • I have tried providing reconnection time but thats does not help. The delay is still 5 second I also edited the question and add loop code. Commented Jul 8 at 10:03

1 Answer 1

0

The spacing is important - fmt.Fprintf(w,"data :%v\n\n","Sorry"); will not work; it needs to be fmt.Fprintf(w, "data:%v\n\n", "Sorry").

Following is a working example; it will send 10 messages and then drop the connection. Because it also sets retry: 10000 (retry in 10000ms i.e. 10s) the browser will reconnect after 10 seconds (and receive another 10 mesages a second apart). Note that I added a timestamp to the messages (makes the output clearer because browsers tend to combine identical output lines).

package main

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

    "github.com/rs/cors"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
        w.Write([]byte(`<!DOCTYPE html>
<html lang="en">
  <head>
    <title>SSE</title>
  </head>
  <body>
    SSE running
    <script>
    const event = new EventSource("http://localhost:6969/sse");
    event.onmessage = () =>{
        console.log("this dude is slow");
    };
    </script>
  </body>
</html>
`))
    })

    mux.HandleFunc("/sse", handleSse)

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"*"},
        AllowedMethods: []string{http.MethodGet, http.MethodPost,
            http.MethodDelete, http.MethodPut},
        AllowCredentials: true,
    })

    handler := c.Handler(mux)

    log.Fatal(http.ListenAndServe(":6969", handler))
}

func handleSse(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("Content-type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    w.WriteHeader(http.StatusOK)

    f, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "SSE not supported , IE6 bruh",
            http.StatusBadRequest)
        return
    }

    for i := 0; i < 10; i++ {
        fmt.Fprintln(w, "retry: 10000") // This means that after the connection drops the browser will wait 10 seconds before reconnecting
        fmt.Fprintf(w, "data:%v\n\n", "Sorry")
        f.Flush()
        time.Sleep(1 * time.Second) // This means a message will be sent every second (until 10 have been sent when the connection will drop)
    }
}

In a real system, the handler (handleSse in this case) may well only exit if it receives an error when writing, otherwise it will stay alive as long as the program is running. Such a handler will receive data from somewhere, perhaps via a channel, and send it to the browser.

Note that my original suggestion to add fmt.Fprintln(w, "retry: 1000") was an attempt to make it clearer what was happening. With your initial code the browser was connecting, receiving a single message (after which the server dropped the connection), and then reconnecting after 5 seconds (to receive another single message...).

Not the answer you're looking for? Browse other questions tagged or ask your own question.