Changeset 1445
- Timestamp:
- 11/22/06 13:44:12
- Files:
-
- trunk/cherrypy/lib/cptools.py (modified) (5 diffs)
- trunk/cherrypy/test/test_etags.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/lib/cptools.py
r1431 r1445 1 1 """Functions for builtin CherryPy tools.""" 2 2 3 import md5 3 4 import re 4 5 … … 14 15 If autotags is True, an ETag response-header value will be provided 15 16 from an MD5 hash of the response body (unless some other code has 16 already provided an ETag header). If False, the ETag will not be 17 automatic, and if no other code has provided an ETag value, then no 18 checks will be performed against If-Match or If-None-Match headers. 17 already provided an ETag header). If False (the default), the ETag 18 will not be automatic. 19 20 WARNING: the autotags feature is not designed for URL's which allow 21 methods other than GET. For example, if a POST to the same URL returns 22 no content, the automatic ETag will be incorrect, breaking a fundamental 23 use for entity tags in a possibly destructive fashion. Likewise, if you 24 raise 304 Not Modified, the response body will be empty, the ETag hash 25 will be incorrect, and your application will break. 26 See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24 19 27 """ 20 28 response = cherrypy.response … … 24 32 return 25 33 34 status, reason, msg = _http.valid_status(response.status) 35 26 36 etag = response.headers.get('ETag') 27 37 38 # Automatic ETag generation. See warning in docstring. 28 39 if (not etag) and autotags: 29 import md5 30 etag = '"%s"' % md5.new(response.collapse_body()).hexdigest() 31 response.headers['ETag'] = etag 32 33 if etag: 34 response.ETag = etag 35 36 status, reason, msg = _http.valid_status(response.status) 37 40 if status == 200: 41 etag = response.collapse_body() 42 etag = '"%s"' % md5.new(etag).hexdigest() 43 response.headers['ETag'] = etag 44 45 response.ETag = etag 46 47 # "If the request would, without the If-Match header field, result in 48 # anything other than a 2xx or 412 status, then the If-Match header 49 # MUST be ignored." 50 if status >= 200 and status <= 299: 38 51 request = cherrypy.request 39 52 … … 41 54 conditions = [str(x) for x in conditions] 42 55 if conditions and not (conditions == ["*"] or etag in conditions): 43 if status >= 200 and status < 299:44 raise cherrypy.HTTPError(412)56 raise cherrypy.HTTPError(412, "If-Match failed: ETag %r did " 57 "not match %r" % (etag, conditions)) 45 58 46 59 conditions = request.headers.elements('If-None-Match') or [] 47 60 conditions = [str(x) for x in conditions] 48 61 if conditions == ["*"] or etag in conditions: 49 if status >= 200 and status < 299:50 if request.method in ("GET", "HEAD"):51 raise cherrypy.HTTPRedirect([], 304)52 else:53 raise cherrypy.HTTPError(412)62 if request.method in ("GET", "HEAD"): 63 raise cherrypy.HTTPRedirect([], 304) 64 else: 65 raise cherrypy.HTTPError(412, "If-None-Match failed: ETag %r " 66 "matched %r" % (etag, conditions)) 54 67 55 68 def validate_since(): … … 68 81 since = request.headers.get('If-Unmodified-Since') 69 82 if since and since != lastmod: 70 if (status >= 200 and status < 299) or status == 412:83 if (status >= 200 and status <= 299) or status == 412: 71 84 raise cherrypy.HTTPError(412) 72 85 73 86 since = request.headers.get('If-Modified-Since') 74 87 if since and since == lastmod: 75 if (status >= 200 and status < 299) or status == 304:88 if (status >= 200 and status <= 299) or status == 304: 76 89 if request.method in ("GET", "HEAD"): 77 90 raise cherrypy.HTTPRedirect([], 304) trunk/cherrypy/test/test_etags.py
r1311 r1445 11 11 resource.exposed = True 12 12 13 def fail(self): 14 raise cherrypy.HTTPError(412) 13 def fail(self, code): 14 code = int(code) 15 if 300 <= code <= 399: 16 raise cherrypy.HTTPRedirect([], code) 17 else: 18 raise cherrypy.HTTPError(code) 15 19 fail.exposed = True 16 20 … … 29 33 self.assertHeader('Content-Type', 'text/html') 30 34 self.assertBody('Oh wah ta goo Siam.') 31 self.assertHeader('ETag') 32 for k, v in self.headers: 33 if k.lower() == 'etag': 34 etag = v 35 break 35 etag = self.assertHeader('ETag') 36 36 37 37 # Test If-Match (both valid and invalid) … … 39 39 self.assertStatus("200 OK") 40 40 self.getPage("/resource", headers=[('If-Match', "*")]) 41 self.assertStatus("200 OK") 42 self.getPage("/resource", headers=[('If-Match', "*")], method="POST") 41 43 self.assertStatus("200 OK") 42 44 self.getPage("/resource", headers=[('If-Match', "a bogus tag")]) … … 53 55 self.assertStatus("200 OK") 54 56 55 # Test raising 412in page handler56 self.getPage("/fail ", headers=[('If-Match', etag)])57 # Test raising errors in page handler 58 self.getPage("/fail/412", headers=[('If-Match', etag)]) 57 59 self.assertStatus(412) 60 self.getPage("/fail/304", headers=[('If-Match', etag)]) 61 self.assertStatus(304) 62 self.getPage("/fail/412", headers=[('If-None-Match', "*")]) 63 self.assertStatus(412) 64 self.getPage("/fail/304", headers=[('If-None-Match', "*")]) 65 self.assertStatus(304) 58 66 59 67

