Mercurial > bzapi
view bzapi_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 | 352f4cc55d12 |
children |
line wrap: on
line source
import os import sys import re import cgi import logging try: import json except ImportError: import simplejson as json 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('([^\?]*)\?(.*)') REDIRECT_TEMPLATE = re.compile('\/([A-Za-z0-9_]+)$') URL_TEMPLATE = re.compile('\/([A-Za-z0-9_]+)/(.*)') MAX_MESSAGE_SIZE = 8192 ROBOTS_TXT = "User-agent: *\r\nDisallow: /" def __init__(self, media_dir, index_filename): self._media_dir = media_dir self._index_filename = index_filename def until_request_processed(self, method, path, headers, addr): match = self.URL_TEMPLATE.match(path) if path == '/': yield until_http_file_sent(self._index_filename) elif not match: match = self.REDIRECT_TEMPLATE.match(path) if match: newpath = path + '/' yield until_http_response_sent( newpath, code = 301, additional_headers = {'Location': newpath} ) elif path == '/robots.txt': yield until_http_response_sent(self.ROBOTS_TXT) else: yield until_http_response_sent('not found', code = 404) else: conv_name = match.group(1) page = match.group(2) if conv_name == 'status': # TODO: Return 404 if page is non-empty. lines = ('open connections : %d' % self._num_connections, 'open timers : %d' % len(time_map)) yield until_http_response_sent('\r\n'.join(lines)) elif conv_name == 'media': path = os.path.join(self._media_dir, *page.split('/')) path = os.path.normpath(path) if (path.startswith(self._media_dir) and os.path.exists(path) and os.path.isfile(path)): yield until_http_file_sent(path) else: yield until_http_response_sent('not found', code = 404) else: yield self._until_conv_request_processed(addr, headers, method, conv_name, page) def _parse_qs(self, querystring): querydict = {} cgi_querydict = cgi.parse_qs(querystring) for key, value in cgi_querydict.items(): querydict[key] = cgi_querydict[key][0] return querydict def _until_conv_request_processed(self, addr, headers, method, conv_name, page): match = self.QUERYSTRING_TEMPLATE.match(page) querydict = {} if match: querydict.update(self._parse_qs(match.group(2))) page = match.group(1) if page == 'listen': logging.info("Waiting for message on channel '%s' for %s" % (conv_name, addr)) msg = yield channels.until_message_received(conv_name) yield until_http_response_sent( json.dumps(msg), mimetype = 'application/json' ) elif page == 'send': length = int(headers.getheader('Content-Length', 0)) if length == 0 or length > self.MAX_MESSAGE_SIZE: yield until_http_response_sent('message too large', code = 413) else: 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.') else: yield until_http_response_sent('not found', code = 404) if __name__ == '__main__': args = dict(ip = '127.0.0.1', port = 8071, logfile = '', loglevel = 'info', media_dir = os.path.abspath('media')) args['index_filename'] = os.path.join(args['media_dir'], 'html', 'index.html') args['loglevel'] = getattr(logging, args['loglevel'].upper()) if args['logfile']: logging.basicConfig(filename = args['logfile'], level = args['loglevel']) else: logging.basicConfig(stream = sys.stdout, level = args['loglevel']) app = BugzillaApiApp(media_dir = args['media_dir'], index_filename = args['index_filename']) server = AsyncWebServer(addr = (args['ip'], args['port']), app = app) logging.info("Starting server with configuration: %s" % args) cosocket.loop()