Your example uses twisted only as a wsgi container. As well as any other thread-based wsgi container it allows you to use time.sleep(1)
.
It is the case where allowing twisted to handle /my_event_source
directly might be beneficial. Here's an example from Using server sent events implemented in Python using twisted:
def cycle(echo):
# Every second, sent a "ping" event.
timestr = datetime.utcnow().isoformat()+"Z"
echo("event: ping\n")
echo('data: ' + json.dumps(dict(time=timestr)))
echo("\n\n")
# Send a simple message at random intervals.
if random.random() < 0.1:
echo("data: This is a message at time {}\n\n".format(timestr))
class SSEResource(resource.Resource):
def render_GET(self, request):
request.setHeader("Content-Type", "text/event-stream")
lc = task.LoopingCall(cycle, request.write)
lc.start(1) # repeat every second
request.notifyFinish().addBoth(lambda _: lc.stop())
return server.NOT_DONE_YET
where the client static/index.html
is from the same source:
<!doctype html>
<title>Using server-sent events</title>
<ol id="eventlist">nothing sent yet.</ol>
<script>
if (!!window.EventSource) {
var eventList = document.getElementById("eventlist");
var source = new EventSource('/my_event_source');
source.onmessage = function(e) {
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
eventList.appendChild(newElement);
}
source.addEventListener("ping", function(e) {
var newElement = document.createElement("li");
var obj = JSON.parse(e.data);
newElement.innerHTML = "ping at " + obj.time;
eventList.appendChild(newElement);
}, false);
source.onerror = function(e) {
alert("EventSource failed.");
source.close();
};
}
</script>
You could combine it with your wsgi application:
app = Flask(__name__)
@app.route('/')
def index():
return redirect(url_for('static', filename='index.html'))
if __name__ == "__main__":
root = resource.Resource()
root.putChild('', wsgi.WSGIResource(reactor, reactor.getThreadPool(), app))
root.putChild('static', static.File("./static"))
root.putChild('my_event_source', SSEResource())
reactor.listenTCP(8001, server.Site(root))
reactor.run()
WSGIResource
expects to handle all urls so the routing code needs to be rewritten to support multiple flask handlers.