Mercurial > cosocket
view openwebchat.py @ 37:979b247cba5d
Serialization of conversation is now done in JSON format.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Mon, 27 Apr 2009 16:56:46 -0700 |
parents | d93c08dca64b |
children | d6467f3845ad |
line wrap: on
line source
import os import math import re import httplib import cStringIO import mimetools from cosocket import * import channels try: import json except ImportError: import simplejson as json class Conversation(list): def __init__(self, fileobj): list.__init__(self) self.__file = fileobj items = [] for line in self.__file.readlines(): items.append(json.loads(line)) self[:] = items def append(self, item): list.append(self, item) self.__file.write('%s\n' % json.dumps(item)) self.__file.flush() class OpenWebChatServer(object): CONVERSATION_URL = re.compile('\/conversations\/([A-Za-z0-9_]+)') BOUNDARY = "'''" BLOCK_SIZE = 8192 MIME_TYPES = {'html' : 'text/html', 'js' : 'text/javascript', 'css' : 'text/css'} def __init__(self, addr, conversation): self._conv = conversation self._server = CoroutineSocketServer(addr, self._server_coroutine) def run(self): self._server.run() def _until_http_response_sent(self, msg = '', mimetype = 'text/plain', length = None, code = 200): headers = {'Keep-Alive': 'timeout=99, max=99', 'Connection': 'Keep-Alive', 'Content-Type': mimetype} if not mimetype.startswith('multipart'): if length is None: length = len(msg) headers['Content-Length'] = str(length) header_lines = ['HTTP/1.1 %d %s' % (code, httplib.responses[code])] header_lines.extend(['%s: %s' % (key, value) for key, value in headers.items()]) header_lines.extend(['', msg]) content = '\r\n'.join(header_lines) yield until_sent(content) def _until_file_sent(self, filename): mimetype = self.MIME_TYPES[filename.split('.')[-1]] length = os.stat(filename).st_size num_blocks = length / self.BLOCK_SIZE if length % self.BLOCK_SIZE: num_blocks += 1 infile = open(filename, 'r') yield self._until_http_response_sent(mimetype = mimetype, length = length) for i in range(num_blocks): # TODO: This could be bad since we're reading the file # synchronously. block = infile.read(self.BLOCK_SIZE) yield until_sent(block) def _server_coroutine(self, addr): while 1: yield self._until_one_request_processed(addr) def _until_multipart_header_sent(self, boundary): yield self._until_http_response_sent( '--%s\r\n' % boundary, mimetype = ('multipart/x-mixed-replace; ' 'boundary="%s"' % boundary)) def _until_multipart_part_sent(self, boundary, msg): yield until_sent('\r\n'.join( ('Content-Length: %d' % len(msg), 'Content-Type: text/plain', '', msg, '', '--%s' % boundary, ''))) def _until_one_request_processed(self, addr): request = yield until_received(terminator = '\r\n\r\n') request = request.splitlines() request_line = request[0] stringfile = cStringIO.StringIO('\n'.join(request[1:])) headers = mimetools.Message(stringfile) req_parts = request_line.split() if req_parts[1] == '/listen': yield self._until_multipart_header_sent(self.BOUNDARY) i = 0 while 1: while i < len(self._conv): msg = json.dumps(self._conv[i]) i += 1 yield self._until_multipart_part_sent(self.BOUNDARY, msg) yield channels.until_message_received('new-message') elif req_parts[1] == '/send': length = int(headers.getheader('Content-Length', 0)) msg = yield until_received(bytes = length) self._conv.append(json.loads(msg)) yield channels.until_message_sent('new-message', None) yield self._until_http_response_sent('sent.') elif req_parts[1] in ['/', '/jquery.js', '/openwebchat.js', '/json2.js', '/openwebchat.css']: if req_parts[1] == '/': filename = 'openwebchat.html' else: filename = req_parts[1][1:] yield self._until_file_sent(filename) else: match = self.CONVERSATION_URL.match(req_parts[1]) if match: yield self._until_http_response_sent('not implemented', code = 501) else: yield self._until_http_response_sent('not found', code = 404) if __name__ == '__main__': CONVERSATION_FILE = 'conversation.dat' if not os.path.exists(CONVERSATION_FILE): open(CONVERSATION_FILE, 'w').close() server = OpenWebChatServer(('127.0.0.1', 8071), Conversation(open(CONVERSATION_FILE, 'r+w'))) server.run()