Skip to content

Instantly share code, notes, and snippets.

@tehasdf
Last active December 13, 2015 19:19
Show Gist options
  • Save tehasdf/4962109 to your computer and use it in GitHub Desktop.
Save tehasdf/4962109 to your computer and use it in GitHub Desktop.
from twisted.web.resource import Resource
from twisted.web import server
from flask import Flask, render_template_string
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
import json
INDEX_TEMPLATE = """
<title>Hello world!</title>
<script>
var es = new EventSource("/es");
var messageDump;
es.onerror = function(evt){
mes("Closed!");
};
es.onopen = function(evt){
mes("Open!");
}
es.onmessage = function(evt){
var text = JSON.parse(evt.data).data;
mes(text);
}
function mes(text){
var li = document.createElement("li");
li.innerText = text;
messageDump.insertBefore(li, messageDump.firstChild);
};
document.addEventListener('DOMContentLoaded', function(){
messageDump = document.getElementById("messages");
}, false);
</script>
<ul id=messages>
</ul>
"""
# the twisted ES part, it just keeps the incoming requests as self.clients
class EventSource(Resource):
clients = []
def send_event(self, request, event, data):
message = json.dumps({"event": event, "data": data})
request.write('data: %s\n\n' % message)
def send_to_all(self, event, data):
for client in self.clients:
self.send_event(client, event, data)
def render_GET(self, request):
request.setResponseCode(200)
request.setHeader('Content-Type', 'text/event-stream')
request.notifyFinish().addBoth(self.close_es_request, request=request)
self.clients.append(request)
self.send_event(request, "hello", "Hello! There are %d clients connected" % len(self.clients))
return server.NOT_DONE_YET
def send_old_events(self, request, last_id, count=10):
for num, (current_id, event, data) in enumerate(self._sent):
if num > count or current_id <= last_id:
break
self.send_event(request, event, data)
def close_es_request(self, error=None, request=None):
self.clients.remove(request)
# let's just instantiate one global ES resource
es_resource = EventSource()
# send a heartbeat to all ES clients once in a while just to see if it works
heartbeat = LoopingCall(es_resource.send_to_all, event="hearbeat", data="Heartbeat!")
heartbeat.start(5)
# flask app
app = Flask(__name__)
@app.route('/')
def index():
return render_template_string(INDEX_TEMPLATE)
# serving stuff, boilerplate mostly
class Root(Resource):
def getChild(self, path, request):
request.postpath.insert(0, request.prepath.pop(0))
return flaskAppResource
flaskAppResource = WSGIResource(reactor, reactor.getThreadPool(), app.wsgi_app)
root = Root() # /<anything> -> flask app, with the exception of...
root.putChild("es", es_resource) # /es -> EventSource()
site = server.Site(root)
reactor.listenTCP(5000, site)
reactor.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment