Mercurial > bzapi
changeset 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 | 352f4cc55d12 |
children | |
files | async_web_server.py bzapi_server.py |
diffstat | 2 files changed, 106 insertions(+), 98 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/async_web_server.py Thu Dec 24 18:34:51 2009 -0800 @@ -0,0 +1,99 @@ +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)
--- a/bzapi_server.py Thu Dec 24 18:07:39 2009 -0800 +++ b/bzapi_server.py Thu Dec 24 18:34:51 2009 -0800 @@ -1,110 +1,19 @@ import os import sys import re -import httplib -import cStringIO -import mimetools -import mimetypes import cgi import logging -from cosocket import * -import channels - try: import json except ImportError: import simplejson as json -KEEP_ALIVE_MAX_REQUESTS = 99 -KEEP_ALIVE_TIMEOUT = int(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 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 until_sent(block) - -class AsyncWebServer(object): - def __init__(self, addr, app): - self._num_connections = 0 - self._app = app - AsyncChatCoroutine(self._server_coroutine(addr)) - - def _server_coroutine(self, bind_addr): - yield until_listening(bind_addr) - while 1: - conn, addr = yield until_connection_accepted() - 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 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) +import channels +import cosocket +from async_web_server import until_http_response_sent +from async_web_server import until_http_file_sent +from async_web_server import AsyncWebServer class BugzillaApiApp(object): QUERYSTRING_TEMPLATE = re.compile('([^\?]*)\?(.*)') @@ -187,7 +96,7 @@ yield until_http_response_sent('message too large', code = 413) else: - msg = yield until_received(bytes = length) + msg = yield cosocket.until_received(bytes = length) json_msg = json.loads(msg) yield channels.until_message_sent(conv_name, json_msg) yield until_http_response_sent('sent.') @@ -218,4 +127,4 @@ app = app) logging.info("Starting server with configuration: %s" % args) - loop() + cosocket.loop()