lib/http.py
changeset 7 d6a8258bd90e
child 8 0ce1f471e9d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/http.py	Fri Feb 06 21:31:02 2009 +0200
@@ -0,0 +1,141 @@
+"""
+    WSGI HTTP utility code
+"""
+
+# for utility functions
+import cgi
+
+# for header handling
+import wsgiref.headers
+
+class Request (object) :
+    """
+        HTTP Request with associated metadata
+    """
+
+    def __init__ (self, env) :
+        """
+            Parse env data
+        """
+
+        # store env
+        self.env = env
+
+        # get the querystring
+        self.arg_str = env.get('QUERY_STRING', '')
+
+        # parse query args
+        self.arg_dict = cgi.parse_qs(self.arg_str, True)
+
+class Response (object) :
+    """
+        HTTP Response with headers and data
+    """
+
+    def __init__ (self, data, content_type='text/html', status='200 OK', charset='utf8') :
+        """
+            Create the response. The Content-type header is built from the given values. The given \a data must be
+            either a str (which is sent plain), an unicode object (which is encoded with the relevant charset), or
+            None, whereupon an empty response body is sent. The content_type argument can also be forced to None to
+            not send a Content-type header (e.g. for redirects)
+        """
+
+        # store info
+        self.status = status
+        self.data = data
+        self.charset = charset
+
+        # headers
+        self.headers = wsgiref.headers.Headers([])
+        
+        # add Content-type header?
+        if content_type :
+            self.add_header('Content-type', content_type, charset=charset)
+
+    def add_header (self, name, value, **params) :
+        """
+            Add response header with the given name/value, plus option params
+
+            XXX: uses the wsgiref.headers code, not sure how that behaves re multiple headers with the same name, etc
+        """
+        
+        self.headers.add_header(name, value, **params)
+    
+    def get_status (self) :
+        """
+            Returns response status string (XXX Foo)
+        """
+
+        return self.status
+    
+    def get_headers (self) :
+        """
+            Returns the list of header (name, value) pairs
+        """
+
+        return self.headers.items()
+
+    def get_data (self) :
+        """
+            Returns the response data - as an encoded string
+        """
+
+        if self.data :
+            return self.data.encode(self.charset)
+
+        else :
+            return ''
+
+class ErrorResponse (Response) :
+    """
+        A response with an error code / message
+    """
+
+    def __init__ (self, status, message) :
+        """
+            Build a plain error message response with the given status/message
+        """
+
+        data = """\
+<html><head><title>%(title)s</title></head><body>
+<h1>%(title)s</h1>
+<p>%(message)s</p>
+</body></html>
+""" % dict(
+            title       = status, 
+            message     = message
+        )
+            
+        super(ErrorResponse, self).__init__(data, status=status)
+
+class ResponseError (Exception) :
+    """
+        An exception that results in a specfic 4xx ErrorResponse message to the client
+    """
+
+    def __init__ (self, message, status='400 Bad Request') :
+        self.status = status
+        self.message = message
+
+        super(ResponseError, self).__init__(message)
+
+    def get_response (self) :
+        return ErrorResponse(self.status, self.message)
+
+class Redirect (Response) :
+    """
+        Redirect response
+    """
+
+    def __init__ (self, url) :
+        """
+            Redirect to given *absolute* URL
+        """
+        
+        # no content-type or data
+        super(Redirect, self).__init__(None, content_type=None, status='302 Found')
+
+        # add Location: header
+        self.add_header("Location", url)
+
+