Closed aldas closed 2 months ago
Relates to #1172
Use httputil.ReverseProxy to proxy SSE requests as it has support for streaming responses. See: https://github.com/golang/go/blob/b107d95b9a66bfe7150fd4f2915e9bb876a6999a/src/net/http/httputil/reverseproxy.go#L601
httputil.ReverseProxy
can be tested with
package main import ( "errors" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "log" "net/http" "net/url" ) func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) tmpURL, err := url.Parse("http://localhost:8081") if err != nil { log.Fatal(err) } e.Use(middleware.Proxy(middleware.NewRoundRobinBalancer([]*middleware.ProxyTarget{{URL: tmpURL}}))) if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } }
Go file for application
package main import ( "bytes" "errors" "fmt" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "log" "net/http" "time" ) func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.File("/", "./index.html") e.GET("/sse", func(c echo.Context) error { log.Printf("SSE client connected, ip: %v", c.RealIP()) w := c.Response() w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-c.Request().Context().Done(): log.Printf("SSE client disconnected, ip: %v", c.RealIP()) return nil case <-ticker.C: event := Event{ Data: []byte("ping: " + time.Now().Format(time.RFC3339Nano)), } if err := event.WriteTo(w); err != nil { return err } w.Flush() } } }) if err := e.Start(":8081"); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } } // Event structure is defined here: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format type Event struct { ID []byte Data []byte Event []byte Retry []byte Comment []byte } // WriteTo writes Event to given ResponseWriter func (ev *Event) WriteTo(w http.ResponseWriter) error { // Marshalling part is taken from: https://github.com/r3labs/sse/blob/c6d5381ee3ca63828b321c16baa008fd6c0b4564/http.go#L16 if len(ev.Data) == 0 && len(ev.Comment) == 0 { return nil } if len(ev.Data) > 0 { if _, err := fmt.Fprintf(w, "id: %s\n", ev.ID); err != nil { return err } sd := bytes.Split(ev.Data, []byte("\n")) for i := range sd { if _, err := fmt.Fprintf(w, "data: %s\n", sd[i]); err != nil { return err } } if len(ev.Event) > 0 { if _, err := fmt.Fprintf(w, "event: %s\n", ev.Event); err != nil { return err } } if len(ev.Retry) > 0 { if _, err := fmt.Fprintf(w, "retry: %s\n", ev.Retry); err != nil { return err } } } if len(ev.Comment) > 0 { if _, err := fmt.Fprintf(w, ": %s\n", ev.Comment); err != nil { return err } } if _, err := fmt.Fprint(w, "\n"); err != nil { return err } return nil }
in the same folder as app create index.html
<!DOCTYPE html> <html> <body> <h1>Getting server updates</h1> <div id="result"></div> <script> // Example taken from: https://www.w3schools.com/html/html5_serversentevents.asp if (typeof (EventSource) !== "undefined") { const source = new EventSource("/sse"); source.onmessage = function (event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } </script> </body> </html>
Relates to #1172
Use
httputil.ReverseProxy
to proxy SSE requests as it has support for streaming responses. See: https://github.com/golang/go/blob/b107d95b9a66bfe7150fd4f2915e9bb876a6999a/src/net/http/httputil/reverseproxy.go#L601can be tested with
Go file for application
in the same folder as app create index.html