terom@7: """ terom@7: WSGI HTTP utility code terom@7: """ terom@7: terom@7: # for utility functions terom@7: import cgi terom@7: terom@7: # for header handling terom@7: import wsgiref.headers terom@7: terom@7: class Request (object) : terom@7: """ terom@7: HTTP Request with associated metadata terom@7: """ terom@7: terom@7: def __init__ (self, env) : terom@7: """ terom@7: Parse env data terom@7: """ terom@7: terom@7: # store env terom@7: self.env = env terom@7: terom@7: # get the querystring terom@7: self.arg_str = env.get('QUERY_STRING', '') terom@7: terom@7: # parse query args terom@7: self.arg_dict = cgi.parse_qs(self.arg_str, True) terom@7: terom@7: class Response (object) : terom@7: """ terom@7: HTTP Response with headers and data terom@7: """ terom@7: terom@7: def __init__ (self, data, content_type='text/html', status='200 OK', charset='utf8') : terom@7: """ terom@7: Create the response. The Content-type header is built from the given values. The given \a data must be terom@7: either a str (which is sent plain), an unicode object (which is encoded with the relevant charset), or terom@7: None, whereupon an empty response body is sent. The content_type argument can also be forced to None to terom@7: not send a Content-type header (e.g. for redirects) terom@7: """ terom@7: terom@7: # store info terom@7: self.status = status terom@7: self.data = data terom@7: self.charset = charset terom@7: terom@7: # headers terom@7: self.headers = wsgiref.headers.Headers([]) terom@7: terom@7: # add Content-type header? terom@7: if content_type : terom@7: self.add_header('Content-type', content_type, charset=charset) terom@7: terom@7: def add_header (self, name, value, **params) : terom@7: """ terom@7: Add response header with the given name/value, plus option params terom@7: terom@7: XXX: uses the wsgiref.headers code, not sure how that behaves re multiple headers with the same name, etc terom@7: """ terom@7: terom@7: self.headers.add_header(name, value, **params) terom@7: terom@7: def get_status (self) : terom@7: """ terom@7: Returns response status string (XXX Foo) terom@7: """ terom@7: terom@7: return self.status terom@7: terom@7: def get_headers (self) : terom@7: """ terom@7: Returns the list of header (name, value) pairs terom@7: """ terom@7: terom@7: return self.headers.items() terom@7: terom@7: def get_data (self) : terom@7: """ terom@7: Returns the response data - as an encoded string terom@7: """ terom@7: terom@7: if self.data : terom@7: return self.data.encode(self.charset) terom@7: terom@7: else : terom@7: return '' terom@7: terom@7: class ErrorResponse (Response) : terom@7: """ terom@7: A response with an error code / message terom@7: """ terom@7: terom@7: def __init__ (self, status, message) : terom@7: """ terom@7: Build a plain error message response with the given status/message terom@7: """ terom@7: terom@7: data = """\ terom@7: %(title)s terom@7:

%(title)s

terom@7:

%(message)s

terom@7: terom@7: """ % dict( terom@7: title = status, terom@7: message = message terom@7: ) terom@7: terom@7: super(ErrorResponse, self).__init__(data, status=status) terom@7: terom@7: class ResponseError (Exception) : terom@7: """ terom@7: An exception that results in a specfic 4xx ErrorResponse message to the client terom@7: """ terom@7: terom@7: def __init__ (self, message, status='400 Bad Request') : terom@7: self.status = status terom@7: self.message = message terom@7: terom@7: super(ResponseError, self).__init__(message) terom@7: terom@7: def get_response (self) : terom@7: return ErrorResponse(self.status, self.message) terom@7: terom@7: class Redirect (Response) : terom@7: """ terom@7: Redirect response terom@7: """ terom@7: terom@7: def __init__ (self, url) : terom@7: """ terom@7: Redirect to given *absolute* URL terom@7: """ terom@7: terom@7: # no content-type or data terom@7: super(Redirect, self).__init__(None, content_type=None, status='302 Found') terom@7: terom@7: # add Location: header terom@7: self.add_header("Location", url) terom@7: terom@7: