"""Simple example comet server for educational purposes.
Requires Spawning and Eventlet
http://pypi.python.org/pypi/eventlet/
http://pypi.python.org/pypi/Spawning/
Run the comet server with:
spawn spawningcomet.wsgi_application --threads=0
Then, to send an event to any browsers waiting, run this in another terminal:
python spawningcomet.py hello world
"""
import struct
import sys
import uuid
from eventlet import api
from eventlet import coros
SEND_EVENT_INTERFACE = ''
SEND_EVENT_PORT = 4200
HTML_TEMPLATE = """
Dynamic content will appear below
"""
class Comet(object):
def __init__(self):
api.spawn(
api.tcp_server,
api.tcp_listener((SEND_EVENT_INTERFACE, SEND_EVENT_PORT)),
self.read_events_forever)
self.current_event = {'event': coros.event(), 'next': None}
self.first_event_id = str(uuid.uuid1())
self.events = {self.first_event_id: self.current_event}
def read_events_forever(self, (sock, addr)):
reader = sock.makefile('r')
try:
while True:
## Read the next event value out of the socket
valuelen = reader.read(4)
if not valuelen:
break
valuelen, = struct.unpack('!L', valuelen)
value = reader.read(valuelen)
## Make a new event and link the current event to it
old_event = self.current_event
old_event['next'] = str(uuid.uuid1())
self.current_event = {
'event': coros.event(), 'next': None}
self.events[old_event['next']] = self.current_event
## Send the event value to any waiting http requests
old_event['event'].send(value)
finally:
reader.close()
sock.close()
def __call__(self, env, start_response):
if env['REQUEST_METHOD'] != 'GET':
start_response('405 Method Not Allowed', [('Content-type', 'text/plain')])
return ['Method Not Allowed\n']
if not env['PATH_INFO'] or env['PATH_INFO'] == '/':
start_response('200 OK', [('Content-type', 'text/html')])
return HTML_TEMPLATE % (self.first_event_id, )
event = self.events.get(env['PATH_INFO'][1:], None)
if event is None:
start_response('404 Not Found', [('Content-type', 'text/plain')])
return ['Not Found\n']
value = event['event'].wait()
start_response('200 OK', [
('Content-type', 'text/plain'),
('X-Next-Event', event['next'])])
return [value, '\n']
def send_event(where, value):
sock = api.connect_tcp(where)
writer = sock.makefile('w')
writer.write('%s%s' % (struct.pack('!L', len(value)), value))
if __name__ == '__main__':
if len(sys.argv) > 1:
value = ' '.join(sys.argv[1:])
else:
value = sys.stdin.read()
send_event((SEND_EVENT_INTERFACE, SEND_EVENT_PORT), value)
else:
wsgi_application = Comet()