Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/filters/encodingfilter.py

Revision 1519 (checked in by fumanchu, 2 years ago)

2.x backport of [1402]; fix for #577 (GzipFilter doesn't force an update of the Content-Length header)). Also fixes #617.

  • Property svn:eol-style set to native
Line 
1 import cherrypy
2 from basefilter import BaseFilter
3
4
5 class EncodingFilter(BaseFilter):
6     """Filter that automatically encodes the response."""
7    
8     def before_finalize(self):
9         conf = cherrypy.config.get
10         if not conf('encoding_filter.on', False):
11             return
12        
13         ct = cherrypy.response.headers.elements("Content-Type")
14         if ct:
15             ct = ct[0]
16             if ct.value.lower().startswith("text/"):
17                 # Set "charset=..." param on response Content-Type header
18                 ct.params['charset'] = find_acceptable_charset()
19                 cherrypy.response.headers["Content-Type"] = str(ct)
20
21
22 def encode_stream(encoding, errors='strict'):
23     """Encode a streaming response body.
24     
25     Use a generator wrapper, and just pray it works as the stream is
26     being written out.
27     """
28     def encoder(body):
29         for chunk in body:
30             if isinstance(chunk, unicode):
31                 chunk = chunk.encode(encoding, errors)
32             yield chunk
33     cherrypy.response.body = encoder(cherrypy.response.body)
34     return True
35
36 def encode_string(encoding, errors='strict'):
37     """Encode a buffered response body."""
38     try:
39         body = []
40         for chunk in cherrypy.response.body:
41             if isinstance(chunk, unicode):
42                 chunk = chunk.encode(encoding, errors)
43             body.append(chunk)
44         cherrypy.response.body = body
45         # Delete Content-Length header so finalize() recalcs it.
46         cherrypy.response.headers.pop("Content-Length", None)
47     except (LookupError, UnicodeError):
48         return False
49     else:
50         return True
51
52 def find_acceptable_charset():
53     conf = cherrypy.config.get
54     response = cherrypy.response
55    
56     attempted_charsets = []
57    
58     stream = conf("stream_response", False)
59     if stream:
60         encode = encode_stream
61     else:
62         response.collapse_body()
63         encode = encode_string
64    
65     failmsg = "The response could not be encoded with %s"
66    
67     errors = conf('encoding_filter.errors', 'strict')
68     enc = conf('encoding_filter.encoding', None)
69     if enc is not None:
70         # If specified, force this encoding to be used, or fail.
71         if encode(enc, errors):
72             return enc
73         else:
74             raise cherrypy.HTTPError(500, failmsg % enc)
75    
76     # Parse the Accept_Charset request header, and try to provide one
77     # of the requested charsets (in order of user preference).
78     default_enc = conf('encoding_filter.default_encoding', 'utf-8')
79    
80     encs = cherrypy.request.headerMap.elements('Accept-Charset')
81     if not encs:
82         # Any character-set is acceptable.
83         charsets = []
84         if encode(default_enc, errors):
85             return default_enc
86         else:
87             raise cherrypy.HTTPError(500, failmsg % default_enc)
88     else:
89         charsets = [enc.value.lower() for enc in encs]
90         if "*" not in charsets:
91             # If no "*" is present in an Accept-Charset field, then all
92             # character sets not explicitly mentioned get a quality
93             # value of 0, except for ISO-8859-1, which gets a quality
94             # value of 1 if not explicitly mentioned.
95             iso = 'iso-8859-1'
96             if iso not in charsets:
97                 attempted_charsets.append(iso)
98                 if encode(iso, errors):
99                     return iso
100        
101         for element in encs:
102             if element.qvalue > 0:
103                 if element.value == "*":
104                     # Matches any charset. Try our default.
105                     if default_enc not in attempted_charsets:
106                         attempted_charsets.append(default_enc)
107                         if encode(default_enc, errors):
108                             return default_enc
109                 else:
110                     encoding = element.value
111                     if encoding not in attempted_charsets:
112                         attempted_charsets.append(encoding)
113                         if encode(encoding, errors):
114                             return encoding
115    
116     # No suitable encoding found.
117     ac = cherrypy.request.headers.get('Accept-Charset')
118     if ac is None:
119         msg = "Your client did not send an Accept-Charset header."
120     else:
121         msg = "Your client sent this Accept-Charset header: %s." % ac
122     msg += " We tried these charsets: %s." % ", ".join(attempted_charsets)
123     raise cherrypy.HTTPError(406, msg)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets