Mercurial > bzapi
view 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 |
line wrap: on
line source
import os import httplib import cStringIO import mimetools import mimetypes import logging import cosocket KEEP_ALIVE_MAX_REQUESTS = 99 KEEP_ALIVE_TIMEOUT = int(cosocket.DEFAULT_TIMEOUT) KEEP_ALIVE_ENABLED = True BLOCK_SIZE = 8192 def until_http_response_sent(msg = '', mimetype = 'text/plain', length = None, code = 200, additional_headers = None): headers = {'Content-Type': mimetype} if KEEP_ALIVE_ENABLED: headers.update({'Keep-Alive': 'timeout=%d, max=%d' % (KEEP_ALIVE_TIMEOUT, KEEP_ALIVE_MAX_REQUESTS), 'Connection': 'Keep-Alive'}) if additional_headers: headers.update(additional_headers) 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 cosocket.until_sent(content) def until_http_file_sent(filename, block_size = BLOCK_SIZE): ext = '.' + filename.split('.')[-1] if ext in mimetypes.types_map: mimetype = mimetypes.types_map[ext] else: mimetype = 'text/plain' length = os.stat(filename).st_size num_blocks = length / block_size if length % block_size: num_blocks += 1 infile = open(filename, 'r') yield 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(block_size) yield cosocket.until_sent(block) class AsyncWebServer(object): def __init__(self, addr, app): self._num_connections = 0 self._app = app cosocket.AsyncChatCoroutine(self._server_coroutine(addr)) def _server_coroutine(self, bind_addr): yield cosocket.until_listening(bind_addr) while 1: conn, addr = yield cosocket.until_connection_accepted() cosocket.AsyncChatCoroutine(self._connection_coroutine(addr), conn) def _connection_coroutine(self, addr): self._num_connections += 1 try: if KEEP_ALIVE_ENABLED: for i in range(KEEP_ALIVE_MAX_REQUESTS): yield self._until_one_request_processed(addr) else: yield self._until_one_request_processed(addr) finally: logging.info('Closing connection to %s' % repr(addr)) self._num_connections -= 1 def _until_one_request_processed(self, addr): request = yield cosocket.until_received(terminator = '\r\n\r\n') request = request.splitlines() request_line = request[0] logging.info("Request from %s: %s" % (addr, request_line)) stringfile = cStringIO.StringIO('\n'.join(request[1:])) headers = mimetools.Message(stringfile) req_parts = request_line.split() yield self._app.until_request_processed(method = req_parts[0], path = req_parts[1], headers = headers, addr = addr)