From a3265bf7635412788216fc74acacc4cd24ced69f Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Fri, 11 Jul 2025 21:14:15 -0400 Subject: [PATCH] Initial commit --- README.md | 24 +++++++++++++++++++++++- go.mod | 3 +++ main.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 go.mod create mode 100644 main.go diff --git a/README.md b/README.md index 1cae2b6..12d99cd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ # learning_sse -Learning Server Side Events \ No newline at end of file +Learning Server Side Events. + +The magic is in setting the `Content Type` header to `text/event-stream`, writing the response in the from of `data: %s \n\n`, and flushing the content of the WriteResponse buffer manually. This is necessary because by default Go will implicitly flush the buffer only once the Handler has returned. + + +``` +func SSEHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + + tokens := strings.Split("Hey there Drew. I hope you enjoy The Alters!", " ") + for _, token := range tokens { + w.Write([]byte(fmt.Sprintf("data: %s \n\n", token))) + w.(http.Flusher).Flush() + time.Sleep(time.Millisecond * 420) + } +} +``` + +Use `go run main.go` and try curling the the sse endpoint. + +``` +curl http://0.0.0.0:8888/sse +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4c30946 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.runcible.io/androiddrew/learning_sse + +go 1.21.0 diff --git a/main.go b/main.go new file mode 100644 index 0000000..ba524a0 --- /dev/null +++ b/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "strings" + "time" +) + +func SSEHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + + tokens := strings.Split("Hey there Drew. I hope you enjoy The Alters!", " ") + for _, token := range tokens { + w.Write([]byte(fmt.Sprintf("data: %s \n\n", token))) + w.(http.Flusher).Flush() + time.Sleep(time.Millisecond * 420) + } +} + +func routes() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/sse", SSEHandler) + return mux +} + +func run(ctx context.Context, w io.Writer, args []string) error { + + addr := fmt.Sprintf("%s:%d", "0.0.0.0", 8888) + srv := &http.Server{ + Addr: addr, + Handler: routes(), + } + slog.Info("Listing on: http://0.0.0.0:8888") + return srv.ListenAndServe() +} + +func main() { + ctx := context.Background() + if err := run(ctx, os.Stdout, os.Args); err != nil { + slog.Error("an error occurred", "error", err) + } +}