Changeset 774
- Timestamp:
- 11/01/05 01:23:34
- Files:
-
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/lib/filter/gzipfilter.py (modified) (1 diff)
- trunk/cherrypy/test/test_core.py (modified) (3 diffs)
- trunk/cherrypy/test/test_gzip_filter.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/lib/cptools.py
r773 r774 186 186 raise cherrypy.HTTPError(416, message) 187 187 188 return result 189 190 191 class AcceptValue(object): 192 """A value (with parameters) from an Accept-* request header.""" 193 194 def __init__(self, value, params=None): 195 self.value = value 196 if params is None: 197 params = {} 198 self.params = params 199 200 def qvalue(self): 201 val = self.params.get("q", "1") 202 if isinstance(val, AcceptValue): 203 val = val.value 204 return float(val) 205 qvalue = property(qvalue, doc="The qvalue, or priority, of this value.") 206 207 def __str__(self): 208 p = [";%s=%s" % (k, v) for k, v in self.params.iteritems()] 209 return "%s%s" % (self.value, "".join(p)) 210 211 def __cmp__(self, other): 212 # If you sort a list of AcceptValue objects, they will be listed in 213 # priority order; that is, the most preferred value will be first. 214 diff = cmp(other.qvalue, self.qvalue) 215 if diff == 0: 216 diff = cmp(str(other), str(self)) 217 return diff 218 219 220 def getAccept(headername='Accept'): 221 """Return a list of AcceptValues from an Accept header, or None.""" 222 223 r = cherrypy.request.headerMap.get(headername) 224 if not r: 225 return None 226 227 result = [] 228 for capability in r.split(","): 229 # The first "q" parameter (if any) separates the initial 230 # parameter(s) (if any) from the accept-params. 231 atoms = re.split(r'; *q *=', capability, 1) 232 capvalue = atoms.pop(0).strip() 233 if atoms: 234 qvalue = atoms[0].strip() 235 if headername == 'Accept': 236 # The qvalue for an Accept header can have extensions. 237 atoms = [x.strip() for x in qvalue.split(";")] 238 qvalue = atoms.pop(0).strip() 239 ext = {} 240 for atom in atoms: 241 atom = atom.split("=", 1) 242 key = atom.pop(0).strip() 243 if atom: 244 val = atom[0].strip() 245 else: 246 val = "" 247 ext[key] = val 248 qvalue = AcceptValue(qvalue, ext) 249 params = {"q": qvalue} 250 else: 251 params = {} 252 253 if headername == 'Accept': 254 # The media-range may have parameters (before the qvalue). 255 atoms = [x.strip() for x in capvalue.split(";")] 256 capvalue = atoms.pop(0).strip() 257 for atom in atoms: 258 atom = atom.split("=", 1) 259 key = atom.pop(0).strip() 260 if atom: 261 val = atom[0].strip() 262 else: 263 val = "" 264 params[key] = val 265 266 result.append(AcceptValue(capvalue, params)) 267 268 result.sort() 188 269 return result 189 270 trunk/cherrypy/lib/filter/gzipfilter.py
r768 r774 17 17 return 18 18 19 if not cherrypy.response.body: 19 response = cherrypy.response 20 if not response.body: 20 21 # Response body is empty (might be a 304 for instance) 21 22 return 22 23 23 ct = cherrypy.response.headerMap.get('Content-Type').split(';')[0] 24 ae = cherrypy.request.headerMap.get('Accept-Encoding', '') 25 if (ct in cherrypy.config.get('gzipFilter.mimeTypeList', ['text/html']) 26 and ('gzip' in ae)): 27 cherrypy.response.headerMap['Content-Encoding'] = 'gzip' 24 def zipit(): 28 25 # Return a generator that compresses the page 26 response.headerMap['Content-Encoding'] = 'gzip' 29 27 level = cherrypy.config.get('gzipFilter.compresslevel', 9) 30 cherrypy.response.body = self.zip_body(cherrypy.response.body, level) 28 response.body = self.zip_body(response.body, level) 29 30 from cherrypy.lib import cptools 31 acceptable = cptools.getAccept('Accept-Encoding') 32 if acceptable is None: 33 # If no Accept-Encoding field is present in a request, 34 # the server MAY assume that the client will accept any 35 # content coding. In this case, if "identity" is one of 36 # the available content-codings, then the server SHOULD use 37 # the "identity" content-coding, unless it has additional 38 # information that a different content-coding is meaningful 39 # to the client. 40 return 41 42 ct = response.headerMap.get('Content-Type').split(';')[0] 43 ct = ct in cherrypy.config.get('gzipFilter.mimeTypeList', ['text/html']) 44 for coding in acceptable: 45 if coding.value == 'identity' and coding.qvalue != 0: 46 return 47 if coding.value in ('gzip', 'x-gzip'): 48 if coding.qvalue == 0: 49 return 50 if ct: 51 zipit() 52 return 53 cherrypy.HTTPError(406, "identity, gzip").set_response() 31 54 32 55 def write_gzip_header(self): trunk/cherrypy/test/test_core.py
r770 r774 175 175 path = os.path.join(os.getcwd(), os.path.dirname(__file__)) 176 176 return cptools.serveFile(os.path.join(path, "static/index.html")) 177 178 179 class Accept(Test): 180 181 def get_accept(self, headername): 182 return "\n".join([str(x) for x in cptools.getAccept(headername)]) 177 183 178 184 … … 572 578 "[Errno 2] No such file or directory: 'nonexistent.html'") 573 579 self.assertInBody(msg) 574 575 580 576 581 def testRanges(self): … … 619 624 self.assertHeader("Content-Range", "bytes */14") 620 625 626 def testAccept(self): 627 h = [('Accept', 'audio/*; q=0.2, audio/basic')] 628 self.getPage("/accept/get_accept?headername=Accept", h) 629 self.assertStatus("200 OK") 630 self.assertBody("audio/basic\n" 631 "audio/*;q=0.2") 632 633 h = [('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')] 634 self.getPage("/accept/get_accept?headername=Accept", h) 635 self.assertStatus("200 OK") 636 self.assertBody("text/x-c\n" 637 "text/html\n" 638 "text/x-dvi;q=0.8\n" 639 "text/plain;q=0.5") 640 641 # Test that more specific media ranges get priority. 642 # Note that the highest priority will be first in the list. 643 h = [('Accept', 'text/*, text/html, text/html;level=1, */*')] 644 self.getPage("/accept/get_accept?headername=Accept", h) 645 self.assertStatus("200 OK") 646 self.assertBody("text/html;level=1\n" 647 "text/html\n" 648 "text/*\n" 649 "*/*") 650 651 # Test Accept-Charset 652 h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')] 653 self.getPage("/accept/get_accept?headername=Accept-Charset", h) 654 self.assertStatus("200 OK") 655 self.assertBody("iso-8859-5\n" 656 "unicode-1-1;q=0.8") 657 658 # Test Accept-Encoding 659 h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')] 660 self.getPage("/accept/get_accept?headername=Accept-Encoding", h) 661 self.assertStatus("200 OK") 662 self.assertBody("gzip;q=1.0\n" 663 "identity;q=0.5\n" 664 "*;q=0") 665 666 # Test Accept-Language 667 h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')] 668 self.getPage("/accept/get_accept?headername=Accept-Language", h) 669 self.assertStatus("200 OK") 670 self.assertBody("da\n" 671 "en-gb;q=0.8\n" 672 "en;q=0.7") 673 621 674 def testHeaderCaseSensitivity(self): 622 675 # Tests that each header only appears once, regardless of case. trunk/cherrypy/test/test_gzip_filter.py
r768 r774 47 47 self.assertInBody(zbuf.getvalue()[:3]) 48 48 49 # Test when gzip is denied. 50 self.getPage('/', headers=[("Accept-Encoding", "identity")]) 51 self.assertBody("Hello, world") 52 self.getPage('/', headers=[("Accept-Encoding", "gzip;q=0")]) 53 self.assertBody("Hello, world") 54 self.getPage('/', headers=[("Accept-Encoding", "*;q=0")]) 55 self.assertStatus("406 Not Acceptable") 56 self.assertErrorPage(406, "identity, gzip") 57 49 58 # Test for ticket #147 50 59 helper.webtest.ignored_exceptions.append(IndexError)

