From b76bc4f2d49fee537e206ca5646bff0b751e5cb8 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sat, 14 Sep 2024 20:39:38 +0300 Subject: [PATCH] switched to SSE transport, seems to work --- src/server.go | 55 ++++++++++++++++++++++++++++++++++++++++++----- webroot/script.js | 9 ++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/server.go b/src/server.go index 2c1ffef..fa7cdfd 100644 --- a/src/server.go +++ b/src/server.go @@ -29,6 +29,7 @@ type ChatMessage struct { var global_msg_id uint64 = 0 /* track global message ID */ var message_transport chan ChatMessage /* global message channel */ +var sse_transports []chan string /* SSE output channels */ var bro webview.WebView /* global browser instance handle */ /* escape HTML entities in JS parameters */ @@ -50,12 +51,20 @@ func appendChatMessages() { global_msg_id += 1 msg.Id = global_msg_id - /* directly call the append function on the JS side */ - fmt_str := "gooseAppendMessage('%d', '%s', '%s', '%s', '%s', '%s');" - js := fmt.Sprintf(fmt_str, msg.Id, esc(msg.Type), esc(msg.Timestamp), esc(msg.Username), esc(msg.Text), esc(msg.Icon)) - log.Printf("Evaluating JS: %s", js) - bro.Eval(js) + /* TODO any additional message logging should be done at this point! */ + + /* serialize the message and send it to the SSE channel */ + json_msg, err := json.Marshal(msg) + if err != nil { + log.Println("Error serializing the message:", err) + } + /* broadcast the message across the clients */ + for _, transport := range sse_transports { + if transport != nil { + transport <- string(json_msg) + } + } } } @@ -73,6 +82,40 @@ func msgAddHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("{\"status\": 1}")) /* respond with a status */ } +/* GET /sse handler */ +func sseHandler(w http.ResponseWriter, r *http.Request) { + /* prepare the headers */ + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + w.Header().Set("Access-Control-Allow-Origin", "*") + + /* create the channel */ + transport := make(chan string) + /* append it to the broadcast list */ + sse_transports = append(sse_transports, transport) + + /* close the channel after exiting the function */ + defer func() { + close(transport) + transport = nil + }() + + /* use HTTP response flusher */ + flusher, _ := w.(http.Flusher) + + /* message loop */ + for { + select { + case message := <-transport: + fmt.Fprintf(w, "data: %s\n\n", message) + flusher.Flush() + case <-r.Context().Done(): /* handle disconnects */ + return + } + } +} + /* entry point */ func main() { config_path := "config.json" @@ -100,6 +143,8 @@ func main() { go func() { /* start the server thread */ /* serve the POST /new */ http.HandleFunc("/new", msgAddHandler) + /* serve the GET /sse */ + http.HandleFunc("/sse", sseHandler) /* serve the webroot directory */ fs := http.FileServer(http.Dir("./webroot")) http.Handle("/", fs) diff --git a/webroot/script.js b/webroot/script.js index 0af1944..4d20cf9 100644 --- a/webroot/script.js +++ b/webroot/script.js @@ -57,3 +57,12 @@ function gooseAppendMessage(id, type, timestamp, username, text, icon) { /* autoscroll to the end */ window.scrollTo(0, document.body.scrollHeight) } + +/* entry point, set up an event source */ +window.addEventListener('DOMContentLoaded', function() { + var msgSource = new EventSource('/sse') + msgSource.onmessage = e => { + var msg = JSON.parse(e.data) + gooseAppendMessage(msg.id, msg.type, msg.timestamp, msg.username, msg.text, msg.icon) + } +}, false)