Commits

ianb  committed c9b636c

Added tests for file-related conditional requests. Added support for If-None-Match to DataApp. Added parsing support for multi-value headers.

  • Participants
  • Parent commits 7628019

Comments (0)

Files changed (4)

File docs/news.txt

     res.mustcontain('this must be there',
                     no=['error', 'unexpected'])
 
+* Fixed ``fileapp.FileApp`` to pay attention to the ``If-None-Match``
+  header, which does ETag matching; before only ``If-Modified-Since``
+  was supported, even though an ``ETag`` header was being sent; in
+  particular Firefox would then only send ``If-None-Match`` and so
+  conditional requests never worked.
+
 0.9.3
 -----
 

File paste/fileapp.py

 
     def __call__(self, environ, start_response):
         headers = self.headers[:]
-        ETAG.update(headers, self.last_modified)
+        current_etag = str(self.last_modified)
+        ETAG.update(headers, current_etag)
         if self.expires is not None:
             EXPIRES.update(headers, delta=self.expires)
 
         except HTTPBadRequest, exce:
             return exce.wsgi_application(environ, start_response)
 
+        try:
+            client_etags = IF_NONE_MATCH.parse(environ)
+            if client_etags:
+                for etag in client_etags:
+                    if etag == current_etag or etag == '*':
+                        # horribly inefficient, n^2 performance, yuck!
+                        for head in list_headers(entity=True):
+                            head.delete(headers)
+                        start_response('304 Not Modified', headers)
+                        return ['']
+        except HTTPBadRequest, exce:
+            return exce.wsgi_application(environ, start_response)
+
         (lower,upper) = (0, self.content_length - 1)
         range = RANGE.parse(environ)
         if range and 'bytes' == range[0] and 1 == len(range[1]):

File paste/httpheaders.py

             return ''
         return ", ".join([str(v).strip() for v in results])
 
+    def parse(self, *args, **kwargs):
+        value = self.__call__(*args, **kwargs)
+        values = value.split(',')
+        return [
+            v.strip() for v in values
+            if v.strip()]
+
 class _MultiEntryHeader(HTTPHeader):
     """
     a multi-value ``HTTPHeader`` where items cannot be combined with a comma

File tests/test_fileapp.py

     finally:
         import os
         os.unlink(tempfile)
+
+def test_file_cache():
+    from paste import fileapp
+    filename = os.path.join(os.path.dirname(__file__),
+                            'urlparser_data', 'secured.txt')
+    app = TestApp(fileapp.FileApp(filename))
+    res = app.get('/')
+    etag = res.header('ETag')
+    last_mod = res.header('Last-Modified')
+    res = app.get('/', headers={'If-Modified-Since': last_mod},
+                  status=304)
+    res = app.get('/', headers={'If-None-Match': etag},
+                  status=304)
+    res = app.get('/', headers={'If-None-Match': 'asdf'},
+                  status=200)
+    res = app.get('/', headers={'If-Modified-Since': 'Sat, 1 Jan 2005 12:00:00 GMT'},
+                  status=200)
+    res = app.get('/', headers={'If-Modified-Since': last_mod + '; length=100'},
+                  status=304)
+    res = app.get('/', headers={'If-Modified-Since': 'invalid date'},
+                  status=400)