Alexandre Bourget

geek joy

Archives for June 2010

HTML5's EventSource in Pylons (read Comet or AJAX polling)

June 16, 2010 at 05:40 PM

Have you heard of EventSource ? It's very cool.

Here is some Pylons code to implement some sweet HTML5 server-pushing-events to web clients.

The example is based off this post by Rick Waldron. It uses the HTML5 features in Chromium version 6. Follow on his post for install instructions.

So in a random controller, dump this code:

import time
class EventsourceController(BaseController):

    def page(self):
        """Serve a fully fledged HTML page"""
        return '<script src="%s"></script>Loaded...' % url.current(action='js')

    def js(self):
        """Serve the JavaScript referenced by the previous action"""
        response.content_type = 'text/javascript'
        return """
document.addEventListener('DOMContentLoaded', function () {
  
  var eventSrc  = new EventSource('%s');
  
  eventSrc.addEventListener('open', function (event) {
    console.log("OPEN");
    console.log(event.type);
  });
  
  eventSrc.addEventListener('message', function (event) {
    console.log("RECEIVED");
    console.log(event.type);
    console.log(event.data);
  
  });
  console.log("Loaded...");

}, false);
""" % url.current(action='event_sender')

    def event_sender(self):
        """Implement a full-blown Event collector and dispatcher"""
        response.headers['content-type'] = 'text/event-stream'
        # Don't use this! It will append a charset=utf-8 and it won't work.
        #response.content_type = 'text/event-stream'
        response.status_int = 200
        def go():
            for x in range(10):
                msg = "data: %s %s\n\n" % (x, time.time())
                time.sleep(2)
                yield msg
        return go()

With a generator, you can have any code wait for some external events (other users' input, UPS failures, mouse movements on the server) and send some events to your web client.

Make sure you take the ErrorHandler middleware out in config/middleware.py, because it buffers the output: it will keep the data from being flushed to the web user until all the content is finished processing. Another option would be to turn debugging off in your .ini file.

def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
    ...
    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)

    if asbool(full_stack):
        # Handle Python exceptions
        #app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
    ...

Point your browser to your http://.../{controller}/page with Chromium 6, and watch the events being printed to the control (Ctrl+Shift+J).

Now put some Gevent and Greenlet goodness in there, and you'll have some real-time app server in no time!

Read and Post Comments