How to setup cherrypy server sent events

Viewed 1784

I am working on a simple web app which is done with angular and cherrypy (prototyping at the moment). I am uploading two files, and then calling a external python program within cherrypy to process them, using subprocess (popen). I am able to do this so far. What I want to achieve is the output from the external program (which I catch via popen) passed on to the client. My issue is that, I am trying to setup server sent events on cherrypy and having no success.

Here is my cherrypy method I am exposing (an example one from web):

@cherrypy.expose
def getUpdate(self):
    #Set the expected headers...
    cherrypy.response.headers["Content-Type"] = "text/event-stream;charset=utf-8"
    def content():
         yield "Hello,"
         yield "world"
    return content()

And here is the javascript client code (I have CORS enabled and working):

var sseEvent = new EventSource('http://localhost:8090/getUpdate');
sseEvent.onmessage = function (event) {
    console.log(event);
};
sseEvent.onopen = function (event) {
  //console.log("I have started...");
};

I have looked at this question and in this blog. Yet, the onmessage event on the EventSource object is not firing, on calling the function from the server side. My understanding is that, you can call the function from the server side and it will catch the event from the browser. I am wrong or the setup is wrong?

2 Answers

To complement this self answered question, I made this fully functional example which consist in two files.

This documentation was very helpful.

Happy fiddling to all !

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>title</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div>server-sent events</div>
    <h3>sse_handler.onmessage()</h3>
    <ul id="sse_message"></ul>

<script>

    // create a sse handler
    var sse_handler = new EventSource('http://localhost:8080/getUpdate');

    sse_handler.onmessage = function (event) {
        console.log("-- sse_handler.onmessage()", event);
        /* the onmessage method catch all generic message, those with no event field
        */
        var h_ul = document.getElementById('sse_message');
        var h_li = document.createElement("li");

        h_li.innerHTML = event.data;
        h_ul.appendChild(h_li);
    };

    sse_handler.onopen = function (event) {
        console.log("-- sse_handler.onopen()", event);
    };

</script>
</body>
</html>

and server.py

#!/usr/bin/env python3

import datetime

import cherrypy

from pathlib import Path

class TestServerSentEvent(object):
    @cherrypy.expose
    def index(self):
        return Path("index.html").read_text()

    @cherrypy.expose
    def getUpdate(self, * pos, ** nam):
        cherrypy.response.headers["Content-Type"] = "text/event-stream;charset=utf-8"
        return 'retry: 1200\ndata: {0}\n\n'.format(self.output())

    def output(self) :
        d = datetime.datetime.now()
        return f"TEST - {d}"

    getUpdate._cp_config = {'response.stream': True, 'tools.encode.encoding':'utf-8'}

if __name__ == '__main__':
    cherrypy.quickstart(TestServerSentEvent())
Related