parent
f45d41af88
commit
82a9f1dad2
@ -0,0 +1,114 @@ |
|||||||
|
""" |
||||||
|
Basic web server / framework. |
||||||
|
""" |
||||||
|
|
||||||
|
from SocketServer import ThreadingMixIn |
||||||
|
import BaseHTTPServer |
||||||
|
from BaseHTTPServer import BaseHTTPRequestHandler |
||||||
|
import urlparse |
||||||
|
import cgi |
||||||
|
|
||||||
|
|
||||||
|
class HTTPError(Exception): |
||||||
|
""" |
||||||
|
HTTPError may be thrown by routes to indicate HTTP errors such as 404, 301, |
||||||
|
etc. They are caught by the 'framework' and sent to the client's browser. |
||||||
|
""" |
||||||
|
def __init__(self, status_code, msg, headers=None): |
||||||
|
if headers is None: |
||||||
|
headers = {} |
||||||
|
self.status_code = status_code |
||||||
|
self.msg = msg |
||||||
|
self.headers = headers |
||||||
|
Exception.__init__(self, status_code, msg, headers) |
||||||
|
|
||||||
|
|
||||||
|
class ThreadedHTTPServer(ThreadingMixIn, BaseHTTPServer.HTTPServer): |
||||||
|
""" |
||||||
|
Base class for multithreaded HTTP servers. |
||||||
|
""" |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class RequestHandler(BaseHTTPRequestHandler): |
||||||
|
""" |
||||||
|
Basic web server request handler. Handles GET and POST requests. You should |
||||||
|
inherit from this class and implement h_ methods for handling requests. |
||||||
|
If no path is set, it dispatches to the 'index' or 'default' method. |
||||||
|
""" |
||||||
|
def log_message(self, fmt, *args): |
||||||
|
"""Overrides BaseHTTPRequestHandler which logs to the console. We log |
||||||
|
to our log file instead""" |
||||||
|
fmt = "{} {}" |
||||||
|
self.scriptform.log.info(fmt.format(self.address_string(), args)) |
||||||
|
|
||||||
|
def do_GET(self): |
||||||
|
""" |
||||||
|
Handle a GET request. |
||||||
|
""" |
||||||
|
self._call(*self._parse(self.path)) |
||||||
|
|
||||||
|
def do_POST(self): |
||||||
|
""" |
||||||
|
Handle a POST request. |
||||||
|
""" |
||||||
|
form_values = cgi.FieldStorage( |
||||||
|
fp=self.rfile, |
||||||
|
headers=self.headers, |
||||||
|
environ={'REQUEST_METHOD': 'POST'}) |
||||||
|
self._call(self.path.strip('/'), params={'form_values': form_values}) |
||||||
|
|
||||||
|
def _parse(self, reqinfo): |
||||||
|
""" |
||||||
|
Parse information from a request. |
||||||
|
""" |
||||||
|
url_comp = urlparse.urlsplit(reqinfo) |
||||||
|
path = url_comp.path |
||||||
|
query_vars = urlparse.parse_qs(url_comp.query) |
||||||
|
# Only return the first value of each query var. E.g. for |
||||||
|
# "?foo=1&foo=2" return '1'. |
||||||
|
var_values = dict([(k, v[0]) for k, v in query_vars.items()]) |
||||||
|
return (path.strip('/'), var_values) |
||||||
|
|
||||||
|
def _call(self, path, params): |
||||||
|
""" |
||||||
|
Find a method to call on self.app_class based on `path` and call it. |
||||||
|
The method that's called is in the form 'h_<PATH>'. If no path was |
||||||
|
given, it will try to call the 'index' method. If no method could be |
||||||
|
found but a `default` method exists, it is called. Otherwise 404 is |
||||||
|
sent. |
||||||
|
|
||||||
|
Methods should take care of sending proper headers and content |
||||||
|
themselves using self.send_response(), self.send_header(), |
||||||
|
self.end_header() and by writing to self.wfile. |
||||||
|
""" |
||||||
|
method_name = 'h_{0}'.format(path) |
||||||
|
method_cb = None |
||||||
|
try: |
||||||
|
if hasattr(self, method_name) and \ |
||||||
|
callable(getattr(self, method_name)): |
||||||
|
method_cb = getattr(self, method_name) |
||||||
|
elif path == '' and hasattr(self, 'index'): |
||||||
|
method_cb = getattr(self, 'index') |
||||||
|
elif hasattr(self, 'default'): |
||||||
|
method_cb = getattr(self, 'default') |
||||||
|
else: |
||||||
|
raise HTTPError(404, "Not found") |
||||||
|
method_cb(**params) |
||||||
|
except HTTPError, err: |
||||||
|
# HTTP erors are generally thrown by the webapp on purpose. Send |
||||||
|
# error to the browser. |
||||||
|
if err.status_code not in (401, ): |
||||||
|
self.scriptform.log.exception(err) |
||||||
|
self.send_response(err.status_code) |
||||||
|
for header_k, header_v in err.headers.items(): |
||||||
|
self.send_header(header_k, header_v) |
||||||
|
self.end_headers() |
||||||
|
self.wfile.write("Error {}: {}".format(err.status_code, |
||||||
|
err.msg)) |
||||||
|
self.wfile.flush() |
||||||
|
return False |
||||||
|
except Exception, err: |
||||||
|
self.scriptform.log.exception(err) |
||||||
|
self.send_error(500, "Internal server error") |
||||||
|
raise |
Loading…
Reference in new issue