From 497abfe83fe25ff052aff184cc4afd9a2f5bb775 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Mon, 24 Mar 2025 01:22:20 -0600 Subject: [PATCH] ???? --- api/sse.go | 58 +++++++++++++++++++----------- pages/templates/tools.ssedemo.html | 28 +++++++++++++-- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/api/sse.go b/api/sse.go index dbb2d20..1cd49ff 100644 --- a/api/sse.go +++ b/api/sse.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "net/http" "time" "atri.dad/lib" @@ -26,51 +27,66 @@ func SSE(c echo.Context) error { // Use the request context, which is cancelled when the client disconnects ctx := c.Request().Context() - c.Response().Header().Set(echo.HeaderContentType, "text/event-stream") - c.Response().Header().Set(echo.HeaderConnection, "keep-alive") - c.Response().Header().Set(echo.HeaderCacheControl, "no-cache") + // Set headers for SSE + c.Response().Header().Set("Content-Type", "text/event-stream") + c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + c.Response().Header().Set("Connection", "keep-alive") + c.Response().Header().Set("Pragma", "no-cache") + c.Response().Header().Set("Expires", "0") + c.Response().Header().Set("X-Accel-Buffering", "no") - // Get origin from request - origin := c.Request().Header.Get(echo.HeaderOrigin) - // Only allow specific origins + // Set CORS headers + origin := c.Request().Header.Get("Origin") if origin == "https://atri.dad" || origin == "http://localhost:3000" { - c.Response().Header().Set(echo.HeaderAccessControlAllowOrigin, origin) - c.Response().Header().Set(echo.HeaderAccessControlAllowCredentials, "true") + c.Response().Header().Set("Access-Control-Allow-Origin", origin) + c.Response().Header().Set("Access-Control-Allow-Credentials", "true") + } else { + // Allow any origin as fallback for testing + c.Response().Header().Set("Access-Control-Allow-Origin", "*") } - // Create a channel to receive messages from the lib.SSEServer + // Set response status + c.Response().WriteHeader(http.StatusOK) + c.Response().Flush() + + // Create a channel to receive messages clientChan := make(chan string) - // Add the client to the lib.SSEServer + // Add the client to the SSEServer lib.SSEServer.AddClient(channel, clientChan) - defer func() { - // Remove the client from the lib.SSEServer when the connection is closed - lib.SSEServer.RemoveClient(channel, clientChan) - }() + // Clean up when the connection is closed + defer lib.SSEServer.RemoveClient(channel, clientChan) - // Create a ticker that fires every 15 seconds - ticker := time.NewTicker(30 * time.Second) + // Write the initial message + initialMsg := ": connected\n\n" + if _, err := c.Response().Write([]byte(initialMsg)); err != nil { + return err + } + c.Response().Flush() + + // Create a ticker for keep-alive messages + ticker := time.NewTicker(15 * time.Second) defer ticker.Stop() + // Event loop for { select { case <-ctx.Done(): - // If the client has disconnected, stop the loop + // Client disconnected return nil case <-ticker.C: - // Every 30 seconds, send a comment to keep the connection alive - if _, err := c.Response().Write([]byte(": keep-alive\n\n")); err != nil { + // Send keep-alive comment + if _, err := c.Response().Write([]byte(": ping\n\n")); err != nil { return err } c.Response().Flush() case msg := <-clientChan: - // Handle incoming messages from the lib.SSEServer + // Format the SSE message data := fmt.Sprintf("data: %s\n\n", msg) if _, err := c.Response().Write([]byte(data)); err != nil { return err } - c.Response().Flush() } } diff --git a/pages/templates/tools.ssedemo.html b/pages/templates/tools.ssedemo.html index 5473bfc..12db32f 100644 --- a/pages/templates/tools.ssedemo.html +++ b/pages/templates/tools.ssedemo.html @@ -7,7 +7,7 @@ Atridad Lahiji // Tools // SSE Demo {{end}} {{define "description"}} -A demo of my SSE implimentation. +A demo of my SSE implementation. {{end}} {{define "head"}} @@ -17,9 +17,9 @@ A demo of my SSE implimentation.

Server Sent Events Demo

This page demonstrates the use of the HTMX SSE - Extention to receive Server Sent Events on the "default" channel.

+ Extension to receive Server Sent Events on the "default" channel.

Any events received on the "default" channel will appear below:

-
+
Waiting for SSE Message...
@@ -37,4 +37,26 @@ A demo of my SSE implimentation. {{define "foot"}} + {{end}}