Index: albatross/httpdapp.py
===================================================================
--- albatross/httpdapp.py	(revision 109)
+++ albatross/httpdapp.py	(working copy)
@@ -8,51 +8,54 @@
 
 import BaseHTTPServer
 import cgi
+import mimetypes
 from cStringIO import StringIO
 from urlparse import urlsplit
+import os.path
+import shutil
 
+from albatross.cgiapp import RequestFields
 
-class Request:
 
+class Request(RequestFields):
+
     """BaseHTTPServer request adaptor"""
 
     def __init__(self, req):
 
         self._req = req
         self._status = 200;
-        self._fields = []
         self.headers_out = {}
         self.data = StringIO()
 
-        if self._req.command == 'GET':
-            self._fields = self.parse_get_fields()
-        elif self._req.command == 'POST':
-            self._fields = self.parse_post_fields()
-        else:
-            pass
+        fields = self.make_fields()
+        RequestFields.__init__(self, fields)
 
-    def parse_get_fields(self):
-        """Extract fields from query string"""
-        qs = urlsplit(self._req.requestline.split()[1])[3]
-        if not qs:
-            return {}
-        return cgi.parse_qs(qs)
+    def make_fields(self):
+        '''
+        Make a cgi.FieldStorage from the request.
 
-    def parse_post_fields(self):
-        """Extract fields from posted data"""
-        fields = {}
-        content_type = self._req.headers.get('content-type')
-        if content_type:
-            key, pdict = cgi.parse_header(content_type)
-            if key == 'application/x-www-form-urlencoded':
-                cl = int(self._req.headers['content-length'])
-                fields = cgi.parse_qs(self._req.rfile.read(cl), 1)
-            elif key == 'multipart/form-data':
-                fields = cgi.parse_multipart(self.content, pdict)
-            else:
-                pass
-        return fields
+        This feels like a complete hack but it makes good use of the standard
+        cgi module ... and it works! Basically, we're pretending to be a
+        CGI by providing an "environment" dictionary with everything the
+        cgi.FieldStorage needs (I simply read the code to know what's used).
+        '''
+        
+        environ = {}
+        environ['REQUEST_METHOD'] = self._req.command
+        environ['QUERY_STRING'] = urlsplit(self._req.requestline.split()[1])[3]
 
+        self._req.headers.setdefault(
+            'content-type',
+            'application/x-www-form-urlencoded')
+
+        field_storage = cgi.FieldStorage(
+            fp=self._req.rfile,
+            headers=self._req.headers,
+            environ=environ)
+
+        return field_storage
+        
     def get_header(self, name):
         return self._req.headers.get(name)
         
@@ -62,25 +65,6 @@
     def end_headers(self):
         pass
 
-    def has_field(self, name):
-        return self._fields.has_key(name)
-
-    def field_value(self, name):
-        
-        # Get the field
-        field = self._fields[name]
-        
-        # Field is always a list. If there is only one item in it then return
-        # the item and let Albatross take care of input fields with the list
-        # attribute
-        if len(field) == 1:
-            return field[0]
-        else:
-            return field
-
-    def field_names(self):
-        return self._fields.keys()
-
     def get_uri(self):
         return self._req.path
 
@@ -116,12 +100,19 @@
     """
 
     def do_GET(self):
+        # Try the static resources first
+        for uri_path, fs_path in self.server.static_resources.items():
+            if self.path.startswith(uri_path):
+                path = self.path.split(uri_path)[1]
+                self.send_file('%s%s' % (fs_path, path))
+                return
         self.process_request()
 
     def do_POST(self):
         self.process_request()
 
     def process_request(self):
+        '''Process a request to the Albatross app'''
         self.log_request()
         req = Request(self)
         self.server.app.run(req)
@@ -132,11 +123,48 @@
         if req.status() == 200:
             self.wfile.write(req.data.getvalue())
 
+    def send_file(self, path):
+        '''Send a file directly from the filesystem'''
+        
+        self.log_request()
+        
+        f = None
 
+        try:
+            try:
+                f = file(path, 'rb')
+                self.send_response(200)
+                self.send_header('Content-Type', mimetypes.guess_type(path)[0])
+                self.send_header('Content-Length', os.path.getsize(path))
+                self.end_headers()
+                shutil.copyfileobj(f, self.wfile)
+            except IOError, e:
+                self.send_response(404, self.responses[404])
+                self.end_headers()
+        finally:
+            if f:
+                f.close()
+
+
 class HTTPServer(BaseHTTPServer.HTTPServer):
 
-    """Simple, standalone HTTP server for Albatross applications."""
+    """
+    Simple, standalone HTTP server for Albatross applications.
+    
+    HTTPServer is a simple web server dedicated to processing requests and
+    mapping that request through to Albatross. It can also serve static
+    resources such as images, stylesheets etc directly from the filesystem.
+    """
 
-    def __init__(self, app, port):
-        BaseHTTPServer.HTTPServer.__init__(self, ('', port), RequestHandler)
+    def __init__(self, app, port, static_resources={}):
+        '''
+        Create an HTTP server for the Albatross application, app,
+        and listen for requests on the specified port.
+
+        static_resources is an optional dictionary that maps uri paths to
+        a corresponding filesystem path. Static resources are served directly
+        from the filesystem without involving the Albatross application.
+        '''
+        BaseHTTPServer.HTTPServer.__init__( self, ('', port), RequestHandler)
         self.app = app
+        self.static_resources = static_resources
