Here is a demo I wrote that demonstrates how to use the Comet method of http streaming. Of course this was before it was named Comet.
from cgi import escape
from random import uniform
from Queue import Queue,Empty
from sets import Set
from socket import error
from threading import Thread
from urllib import unquote_plus
from wsgiref.simple_server import make_server
class Connection(Queue):
"""Handles the persistant connection between the client and server"""
#This set could get messed up by multi-threading.
objects=Set() #set of live connections
def __init__(self,obj_up_hook=None):
self.name=""
self.obj_up_hook=obj_up_hook
Queue.__init__(self)
def __str__(self):
return self.name
def __repr__(self):
return "Connection object: " + str(self)
def online(self):
self.objects.add(self)
print '"' + str(self) + '" has joined'
if self.obj_up_hook:
self.obj_up_hook(self)
def offline(self):
self.objects.discard(self)
print '"' + str(self) + '" has left'
if self.obj_up_hook:
self.obj_up_hook(self)
def send_to_all(msg):
"""Sends a message to all online objects"""
for x in Connection.objects:
x.put(msg)
send_to_all = staticmethod(send_to_all)
def run(self,write,keep_alive=" "):
"""Waits for messages and outputs them until window is closed"""
self.online()
while 1:
try:
#Wait for a new message.
m=self.get(True,uniform(10,15))
except Empty:
#The waiting timed out.
m=keep_alive
try:
write(m)
except error:
#most likely the client closed the window
self.offline()
return
class ChatApp():
"""Handles a Request"""
def __init__(self, environ, start_response):
self.environ=environ
self.start_response=start_response
def index(self):
"""login page"""
return """<script>
function submit(e){
if(!e)e=window.event;
if(e.keyCode==13){
url="/main/"+input.value;
location.href=url
}
}
</script>
<body>
<table width=100% height=60%>
<td width=100% height=100%><center>Enter your name:<br><input id=input style="width:50%" onkeypress="submit(event)">"""
def main(self,user):
"""main page"""
return '''<script>
function submit(e){
if(!e)e=window.event;
if(e.keyCode==13){
url="/ajax/'''+str(user)+'''?"+input.value;
input.value="";
if(window.ActiveXObject){ajax=new ActiveXObject("Microsoft.XMLHTTP")};
if(window.XMLHttpRequest){ajax=new XMLHttpRequest()};
ajax.open("GET",url,true);
ajax.send(null);
}
}
</script>
<body topmargin=0 bottommargin=0 leftmargin=0 rightmargin=0>
<table width=100% height=100% cellspacing=0 cellpadding=0>
<td width=80% height=100%>
<iframe id=thebox style="border-right:0;border-left:0;border-top:0;border-bottom:0" width=100% height=100% src="/top/'''+str(user)+'''"></iframe>
<td width=20% height=100%>
<iframe name=thelist style="border-right:0;border-left:0;border-top:0;border-bottom:0" width=100% height=100% src="/list/'''+str(user)+'''"></iframe>
<tr><td><input id=input style="width:100%" value="Type your message here" onkeypress="submit(event)">'''
def refresh_online_list(self,connection):
Connection.send_to_all('<script>u()</script>')
def top(self,write,usern):
"""actual chat window"""
#create another thread to serve new requests
Server()
write("""<body><script>
function u(){parent.frames["thelist"].location.reload();}
function s(str){document.write(str);window.scrollBy(0,100);}
</script>""")
u=Connection(self.refresh_online_list)
u.name=usern
u.run(write)
def onlinelist(self,user):
"""online list"""
string = "<b>" + str(len(Connection.objects)) + " Online:</b><br>"
for u in Connection.objects:
string += str(u)+"<br>n"
return string
def ajax(self,user,message):
"""page that accepts messages"""
#this escape function escapes all html and quotes
print "recieved message: " + message
print "sending message: " + self.esc(message)
Connection.send_to_all("<script>s('" + "<b>" + self.esc(str(user)) +
":</b> " + self.esc(message) + "<br>" + "')</script>")
print "done"
def esc(string):
#for html:
string = escape(string,True)
#for javascript (order is important)
string = string.replace("\","\\")
string = string.replace("'","\'")
return string
esc = staticmethod(esc)
def __iter__(self):
print "recieved request"
write = self.start_response('200 OK', [('Content-type', 'text/html')])
patharray = self.environ['PATH_INFO'].split('/')
if patharray==["",""]:
yield self.index()
return
if patharray[1]=="favicon.ico":
return
command=patharray[1]
user=patharray[2]
if command=="main":
yield self.main(user)
elif command=="top":
self.top(write,user)
yield ""
elif command=="list":
yield self.onlinelist(user)
elif command=="ajax":
self.ajax(user,unquote_plus(self.environ['QUERY_STRING']))
yield ""
else:
yield "unknown command: "+str(command)
class Server(Thread):
"""A thread that serves requests"""
def __init__(self):
Thread.__init__(self)
self.setDaemon(1)
self.start()
def run(self):
self.httpd.serve_forever()
def start():
httpd = make_server('0.0.0.0', 9081, ChatApp)
Server.httpd=httpd
print "Serving HTTP on port 9081..."
s=Server()
s.join()#don't exit
if __name__ == '__main__':
start()