Skip to content

Instantly share code, notes, and snippets.

@werediver
Last active June 1, 2023 14:17
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save werediver/4358735 to your computer and use it in GitHub Desktop.
Save werediver/4358735 to your computer and use it in GitHub Desktop.
Simple demonstration of how to implement Server-sent events (SSE) in Python using Bottle micro web-framework. SSE require asynchronous request handling, but it's tricky with WSGI. One way to achieve that is to use gevent library as shown here.
"""
Simple demonstration of how to implement Server-sent events (SSE) in Python
using Bottle micro web-framework.
SSE require asynchronous request handling, but it's tricky with WSGI. One way
to achieve that is to use gevent library as shown here.
Usage: just start the script and open http://localhost:8080/ in your browser.
Based on:
- "Primer to Asynchronous Applications",
http://bottlepy.org/docs/dev/async.html
- "Using server-sent events",
https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events
"""
# Bottle requires gevent.monkey.patch_all() even if you don't like it.
from gevent import monkey; monkey.patch_all()
from gevent import sleep
from bottle import get, post, request, response
from bottle import GeventServer, run
import time
sse_test_page = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js "></script>
<script>
$(document).ready(function() {
var es = new EventSource("stream");
es.onmessage = function (e) {
$("#log").html($("#log").html()
+ "<p>Event: " + e.type + ", data: " + e.data + "</p>");
};
})
</script>
</head>
<body>
<div id="log" style="font-family: courier; font-size: 0.75em;"></div>
</body>
</html>
"""
@get('/')
def index():
return sse_test_page
@get('/stream')
def stream():
# "Using server-sent events"
# https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events
# "Stream updates with server-sent events"
# http://www.html5rocks.com/en/tutorials/eventsource/basics/
response.content_type = 'text/event-stream'
response.cache_control = 'no-cache'
# Set client-side auto-reconnect timeout, ms.
yield 'retry: 100\n\n'
n = 1
# Keep connection alive no more then... (s)
end = time.time() + 60
while time.time() < end:
yield 'data: %i\n\n' % n
n += 1
sleep(1)
if __name__ == '__main__':
run(server=GeventServer)
@qwdm
Copy link

qwdm commented Dec 20, 2014

import time

is needed

@antonavy
Copy link

Doesn't work for me, Nothing on server:8080 :(

@min-infcof
Copy link

There's an issue with this example: when SSE connection is closed by browser, gevent thread hangs on yield line.
When creating more new connections by let's say reloading SSE webpage in browser for few times, it will no longer serve a new page as there's a limited gevent thread pool.

Does anyone have found any solution for this, using bottle?

@oz123
Copy link

oz123 commented Aug 18, 2020

The HTML code has a tiny issue. e has no property event. This can be fixed by changing e.event in line 38 to e.type.

@werediver
Copy link
Author

I've applied your suggestion, @oz123. Thanks 🙂

Does it still work after this many years?.. 🤯

@oz123
Copy link

oz123 commented Aug 18, 2020

@werediver, why would it not work?
bottle has a stable API, and SSE hasn't changed.
While you are at it, you should import time at the top.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment