Mercurial > cosocket
annotate openwebchat.py @ 33:8146a59c8045
Added basic 'chat' functionality.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Mon, 27 Apr 2009 14:44:40 -0700 |
parents | 1acccfa6a4f6 |
children | 0e853a889c27 |
rev | line source |
---|---|
26 | 1 import os |
2 import math | |
28 | 3 import re |
4 import httplib | |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
5 import cStringIO |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
6 import mimetools |
26 | 7 |
8 from cosocket import * | |
9 import channels | |
10 | |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
11 class Conversation(list): |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
12 pass |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
13 |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
14 class Conversations(dict): |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
15 pass |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
16 |
26 | 17 class OpenWebChatServer(object): |
28 | 18 CONVERSATION_URL = re.compile('\/conversations\/([A-Za-z0-9_]+)') |
19 | |
33
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
20 BOUNDARY = "'''" |
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
21 |
26 | 22 BLOCK_SIZE = 8192 |
23 | |
24 MIME_TYPES = {'html' : 'text/html', | |
25 'js' : 'text/javascript', | |
26 'css' : 'text/css'} | |
27 | |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
28 def __init__(self, addr, conversations): |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
29 self._convs = conversations |
26 | 30 self._server = CoroutineSocketServer(addr, |
31 self._server_coroutine) | |
32 | |
33 def run(self): | |
34 self._server.run() | |
35 | |
28 | 36 def _until_http_response_sent(self, msg = '', mimetype = 'text/plain', |
37 length = None, code = 200): | |
31 | 38 headers = {'Keep-Alive': 'timeout=99, max=99', |
39 'Connection': 'Keep-Alive', | |
40 'Content-Type': mimetype} | |
30 | 41 if not mimetype.startswith('multipart'): |
42 if length is None: | |
43 length = len(msg) | |
31 | 44 headers['Content-Length'] = str(length) |
45 | |
46 header_lines = ['HTTP/1.1 %d %s' % (code, | |
47 httplib.responses[code])] | |
48 header_lines.extend(['%s: %s' % (key, value) | |
49 for key, value in headers.items()]) | |
50 header_lines.extend(['', msg]) | |
51 content = '\r\n'.join(header_lines) | |
28 | 52 yield until_sent(content) |
26 | 53 |
54 def _until_file_sent(self, filename): | |
55 mimetype = self.MIME_TYPES[filename.split('.')[-1]] | |
56 | |
57 length = os.stat(filename).st_size | |
27
7e74ceedaa26
More readable way of determining number of blocks needed, though I think I'm missing something...
Atul Varma <varmaa@toolness.com>
parents:
26
diff
changeset
|
58 num_blocks = length / self.BLOCK_SIZE |
7e74ceedaa26
More readable way of determining number of blocks needed, though I think I'm missing something...
Atul Varma <varmaa@toolness.com>
parents:
26
diff
changeset
|
59 if length % self.BLOCK_SIZE: |
7e74ceedaa26
More readable way of determining number of blocks needed, though I think I'm missing something...
Atul Varma <varmaa@toolness.com>
parents:
26
diff
changeset
|
60 num_blocks += 1 |
26 | 61 infile = open(filename, 'r') |
62 | |
28 | 63 yield self._until_http_response_sent(mimetype = mimetype, |
64 length = length) | |
26 | 65 |
66 for i in range(num_blocks): | |
67 # TODO: This could be bad since we're reading the file | |
68 # synchronously. | |
69 block = infile.read(self.BLOCK_SIZE) | |
70 yield until_sent(block) | |
71 | |
72 def _server_coroutine(self, addr): | |
73 while 1: | |
74 yield self._until_one_request_processed(addr) | |
75 | |
30 | 76 def _until_multipart_header_sent(self, boundary): |
77 yield self._until_http_response_sent( | |
78 '--%s\r\n' % boundary, | |
79 mimetype = ('multipart/x-mixed-replace; ' | |
80 'boundary="%s"' % boundary)) | |
81 | |
82 def _until_multipart_part_sent(self, boundary, msg): | |
83 yield until_sent('\r\n'.join( | |
84 ('Content-Length: %d' % len(msg), | |
85 'Content-Type: text/plain', | |
86 '', | |
87 msg, | |
88 '', | |
89 '--%s' % boundary, | |
90 ''))) | |
91 | |
26 | 92 def _until_one_request_processed(self, addr): |
93 request = yield until_received(terminator = '\r\n\r\n') | |
94 request = request.splitlines() | |
95 request_line = request[0] | |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
96 stringfile = cStringIO.StringIO('\n'.join(request[1:])) |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
97 headers = mimetools.Message(stringfile) |
26 | 98 req_parts = request_line.split() |
99 if req_parts[1] == '/listen': | |
33
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
100 yield self._until_multipart_header_sent(self.BOUNDARY) |
26 | 101 while 1: |
33
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
102 msg = yield channels.until_message_received('global') |
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
103 yield self._until_multipart_part_sent(self.BOUNDARY, msg) |
26 | 104 elif req_parts[1] == '/send': |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
105 length = int(headers.getheader('Content-Length', 0)) |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
106 msg = yield until_received(bytes = length) |
33
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
107 yield channels.until_message_sent('global', msg) |
28 | 108 yield self._until_http_response_sent('sent.') |
26 | 109 elif req_parts[1] in ['/', '/jquery.js', '/openwebchat.js', |
33
8146a59c8045
Added basic 'chat' functionality.
Atul Varma <varmaa@toolness.com>
parents:
32
diff
changeset
|
110 '/json2.js', '/openwebchat.css']: |
26 | 111 if req_parts[1] == '/': |
112 filename = 'openwebchat.html' | |
113 else: | |
114 filename = req_parts[1][1:] | |
115 | |
116 yield self._until_file_sent(filename) | |
28 | 117 else: |
118 match = self.CONVERSATION_URL.match(req_parts[1]) | |
119 if match: | |
120 yield self._until_http_response_sent('not implemented', | |
121 code = 501) | |
122 else: | |
123 yield self._until_http_response_sent('not found', | |
124 code = 404) | |
26 | 125 |
126 if __name__ == '__main__': | |
32
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
127 server = OpenWebChatServer(('127.0.0.1', 8071), |
1acccfa6a4f6
Added support for header parsing.
Atul Varma <varmaa@toolness.com>
parents:
31
diff
changeset
|
128 Conversations()) |
26 | 129 server.run() |