comparison async_web_server.py @ 37:c6b41464c021 default tip

factored async web server into separate file
author Atul Varma <varmaa@toolness.com>
date Thu, 24 Dec 2009 18:34:51 -0800
parents
children
comparison
equal deleted inserted replaced
36:352f4cc55d12 37:c6b41464c021
1 import os
2 import httplib
3 import cStringIO
4 import mimetools
5 import mimetypes
6 import logging
7
8 import cosocket
9
10 KEEP_ALIVE_MAX_REQUESTS = 99
11 KEEP_ALIVE_TIMEOUT = int(cosocket.DEFAULT_TIMEOUT)
12 KEEP_ALIVE_ENABLED = True
13
14 BLOCK_SIZE = 8192
15
16 def until_http_response_sent(msg = '', mimetype = 'text/plain',
17 length = None, code = 200,
18 additional_headers = None):
19 headers = {'Content-Type': mimetype}
20 if KEEP_ALIVE_ENABLED:
21 headers.update({'Keep-Alive': 'timeout=%d, max=%d' %
22 (KEEP_ALIVE_TIMEOUT,
23 KEEP_ALIVE_MAX_REQUESTS),
24 'Connection': 'Keep-Alive'})
25 if additional_headers:
26 headers.update(additional_headers)
27 if length is None:
28 length = len(msg)
29 headers['Content-Length'] = str(length)
30
31 header_lines = ['HTTP/1.1 %d %s' % (code,
32 httplib.responses[code])]
33 header_lines.extend(['%s: %s' % (key, value)
34 for key, value in headers.items()])
35 header_lines.extend(['', msg])
36 content = '\r\n'.join(header_lines)
37 yield cosocket.until_sent(content)
38
39 def until_http_file_sent(filename, block_size = BLOCK_SIZE):
40 ext = '.' + filename.split('.')[-1]
41
42 if ext in mimetypes.types_map:
43 mimetype = mimetypes.types_map[ext]
44 else:
45 mimetype = 'text/plain'
46
47 length = os.stat(filename).st_size
48 num_blocks = length / block_size
49 if length % block_size:
50 num_blocks += 1
51 infile = open(filename, 'r')
52
53 yield until_http_response_sent(mimetype = mimetype,
54 length = length)
55
56 for i in range(num_blocks):
57 # TODO: This could be bad since we're reading the file
58 # synchronously.
59 block = infile.read(block_size)
60 yield cosocket.until_sent(block)
61
62 class AsyncWebServer(object):
63 def __init__(self, addr, app):
64 self._num_connections = 0
65 self._app = app
66 cosocket.AsyncChatCoroutine(self._server_coroutine(addr))
67
68 def _server_coroutine(self, bind_addr):
69 yield cosocket.until_listening(bind_addr)
70 while 1:
71 conn, addr = yield cosocket.until_connection_accepted()
72 cosocket.AsyncChatCoroutine(self._connection_coroutine(addr),
73 conn)
74
75 def _connection_coroutine(self, addr):
76 self._num_connections += 1
77 try:
78 if KEEP_ALIVE_ENABLED:
79 for i in range(KEEP_ALIVE_MAX_REQUESTS):
80 yield self._until_one_request_processed(addr)
81 else:
82 yield self._until_one_request_processed(addr)
83 finally:
84 logging.info('Closing connection to %s' % repr(addr))
85 self._num_connections -= 1
86
87 def _until_one_request_processed(self, addr):
88 request = yield cosocket.until_received(terminator = '\r\n\r\n')
89 request = request.splitlines()
90 request_line = request[0]
91 logging.info("Request from %s: %s" % (addr, request_line))
92 stringfile = cStringIO.StringIO('\n'.join(request[1:]))
93 headers = mimetools.Message(stringfile)
94 req_parts = request_line.split()
95
96 yield self._app.until_request_processed(method = req_parts[0],
97 path = req_parts[1],
98 headers = headers,
99 addr = addr)