Forum OpenACS Development: Re: Implementing Server-Sent Event backend in OpenACS

Collapse
Posted by Gustaf Neumann on

Dear Brian,

I've looked into it and tried to come with a fairly simple example. The question you have not addressed is, where the events are coming from that you want to distribute. In the example below, the events are triggered from a scheduled procedure. There is certainly much more which can be done: Define a small API, and/or integrate with the message-redirector, etc. etc.

Hope you find this simple example useful!
-g

Subscriber. This script is called by the EventSource() call from the HTML page (see below). Place it e.g. into /www/timer-sse.tcl

 #
 # A sample SSE subscriber
 #
 # Detach the channel from the connection thread and make it usable in the background.
 # The channel is added to the nsv-array of the SSE subscribers
 #

 set channel [ns_connchan detach]
 ns_connchan write $channel [append _ \
                                "HTTP/1.1 200 OK\r\n" \
                                "Cache-Control: no-cache\r\n" \
                                "Content-type: text/event-stream\r\n" \
                                "\r\n"]

 #
 # Send some initial data (not needed)
 #
 ns_connchan write $channel "data: Hi There!\n\n"
 ns_connchan write $channel "data: Start time is: [clock format [clock seconds]]\n\n"

 #
 # Let background task know about us
 
 nsv_set sse_subscribers $channel 1

Background task checking for subscribers and delivering messages to them.

Place this e.g. into some *-init.tcl file such this is registered during startup:

    # initialize nsv array
    nsv_set sse_subscribers {} {}

    #
    # Background task: check every 5 seconds for subscribers and tell them the time.
    #
    ns_schedule_proc -thread 5s {
        foreach subscriber [nsv_array names sse_subscribers] {
            if {$subscriber eq ""} continue
            try {
                ns_connchan write $subscriber "data: A voice from the background says: it is now [clock format [clock seconds]]!\n\n"
            } on ok {result} {
                # Everyhing was fine.
            } on error {errorMsg} {
                ns_log notice "SSE SEND failed: '$errorMsg'"
                ns_connchan close $subscriber
                nsv_unset sse_subscribers $subscriber
            }
        }
    }

Sample page for testing (remove the space before "script>"):

<!DOCTYPE html>
<html>
<meta content="utf-8" http-equiv="encoding">
<title>Timer SSE demo</title>
<body>

<h1>Getting server updates</h1>
<div id="result"></div>

< script>
if(typeof(EventSource) !== "undefined") {
  var source = new EventSource("/timer-sse.tcl");
  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>
Collapse
Posted by Brian Fenton on
Gustaf, THANK YOU!

That's is incredibly useful, and your help has been really above and beyond my expectation. I'm very grateful.

regards
Brian

Collapse
Posted by Gustaf Neumann on
Brian,

how went your experiments with Server-Sent Events? Could you use this code? Should we consider some API?

-g

Collapse
Posted by Brian Fenton on
Hi Gustaf

yes, it's been going very well - your code was a huge help, and needed almost no changes for my application. I'm making slow progress writing a front end using Vue.js but it's a great learning experience.

Many thanks again
Brian

Collapse
Posted by Malte Sussdorff on
Thanks Gustaf for this. We will experiment with this later this year, replacing our polling for "notifications" into SSE as described here.