SSE
1 Example of use
2 HTTP responses
sse-http-response?
3 SSE variables
sse?
make-sse
sse-serve
sse-send-event
sse-dispatcher
7.9

SSE

Oscar Alberto Quijano Xacur

 (require SSE) package: SSE

1 Example of use

Let us start by defining a function that serves a website with a text area that is filled with messages coming from server sent events on port 8122.

#lang racket
 
(require web-server/http/xexpr
         web-server/web-server
         web-server/servlet-dispatch
         SSE)
 
;; Page that connects to the server-sent events
(define (start-page req)
    (response/xexpr
        `(html
          (head
          (meta ([http-equiv "content-type"] [content "text/html; charset=utf-8"] ))
          (meta ([name "viewport"] [content "width=device-width"] ))
          (title "Events test"))
 
          (body
          (h1 "Welcome to the Events test")
          (textarea ([readonly ""]))
          (script
          "
          var evtSource = new EventSource(\"//localhost:8122\");
          var textArea = document.getElementsByTagName(\"textarea\")[0];
 
          evtSource.onmessage = function(e){
              textArea.value =  e.data + \"\\n\" + textArea.value ;
              console.log(e.data);
          }
          ")))))

Now we define the server sent events, serve them on port 8122 and run the servlet for the function above on port 8080.

(define a-sse (make-sse))
(sse-serve a-sse #:port 8122)
(serve #:dispatch (dispatch/servlet start-page) #:port 8080)

Note that we are not using the function serve/servlet for serving the website. serve does not block the racket REPL. Now open a browser and visit http://localhost:8080. You are going to see a welcome message and an empty textarea. In order to add some text send a data only event as follows:

(sse-send-event a-sse #:data "Hello World!")

At this point the text "Hello World!" appears in the text area.

Now open a new tab or window of your browser and go again to localhost:8080. Note that the message "Hello World!" does not appear in this window. This is because we used #:id with value #f (it is the default value). Let us now send one with #:id equal to #t.

(sse-send-event a-sse #:data "Hello World! (again)" #:id #t)

At this point the two windows that you have open will show the message "Hello World! (again)". This would be the case even with #:id equal to #f. What makes the difference is that if you open now a new tab and connect to localhost:8080, that window will also show this message.

2 HTTP responses

Server Sent Events (SSE) connections start with an http request for connection. The SSE standard allows 4 possible responses. The library has a way to represent each of them as shown in the following table.

HTTP Status Code

 

Library Representation

200 Accept

 

'accept

204 No Content

 

'no-content

301 Moved Permanently

 

'(moved-permanently "http://new-location.com")

307 Temporary Redirect

 

'(temporary-redirect "http://new-location.com")

For the two hundred codes, the representation is simply a symbol. For the three hundred codes, it is a list with a symbol and string with the url of the redirection. A predicate to check if something is a proper representation of one of the http responses above.

procedure

(sse-http-response? x)  boolean?

  x : any/c

3 SSE variables

To see if the value of a variable is a sse, the following predicate can be used.

procedure

(sse? x)  boolean?

  x : any/c

Racket servlets use functions that take an HTTP request and return an HTTP response. Similarly, the function make-sse takes a parameter called the initial request servlet, which takes a request and returns one of the four http responses allowed for SSE. If the response is 'accept, a connection for the event stream is established.

procedure

(make-sse [initial-request-servlet])  sse?

  initial-request-servlet : (-> request? sse-http-response?)
   = (lambda (req) 'accept)

Once a sse variable has been created it can be served on a specific port with the sse-serve function and messages can be sent using sse-send-event.

procedure

(sse-serve a-sse #:port port)  void?

  a-sse : sse?
  port : listen-port-number?

procedure

(sse-send-event a-sse    
  [#:data data    
  #:event event    
  #:id id    
  #:retry retry])  void?
  a-sse : sse?
  data : (or/c string? null?) = empty
  event : (or/c string? null?) = empty
  id : boolean? = #f
  retry : (or/c positive-integer? null?) = empty
If id is #t the message is send and it is added to the message-hash of a-sse. The actual id value sent is always a positive integer starting from one. It is automatically managed by the library. The messages added to the message-hash are sent to new connections. When the header Last-Event-ID is sent in a request only the messages with an id greater than that value are sent.

procedure

(sse-dispatcher x)  dispatcher/c

  x : sse?
Returns the dispatcher associated with a sse.