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

root/branches/cp3-wsgi-remix/lib/encoding.py

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

Lots of mixedCase to lower_with_underscores.

  • Property svn:eol-style set to native
Line 
1 import struct
2 import time
3
4 import cherrypy
5
6
7 def decode(encoding=None, default_encoding='utf-8'):
8     """Decode cherrypy.request.params."""
9     if not encoding:
10         ct = cherrypy.request.headers.elements("Content-Type")
11         if ct:
12             ct = ct[0]
13             encoding = ct.params.get("charset", None)
14             if (not encoding) and ct.value.lower().startswith("text/"):
15                 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
16                 # When no explicit charset parameter is provided by the
17                 # sender, media subtypes of the "text" type are defined
18                 # to have a default charset value of "ISO-8859-1" when
19                 # received via HTTP.
20                 encoding = "ISO-8859-1"
21        
22         if not encoding:
23             encoding = default_encoding
24    
25     try:
26         decode_params(encoding)
27     except UnicodeDecodeError:
28         # IE and Firefox don't supply a charset when submitting form
29         # params with a CT of application/x-www-form-urlencoded.
30         # So after all our guessing, it could *still* be wrong.
31         # Start over with ISO-8859-1, since that seems to be preferred.
32         decode_params("ISO-8859-1")
33
34 def decode_params(encoding):
35     decoded_params = {}
36     for key, value in cherrypy.request.params.items():
37         if hasattr(value, 'file'):
38             # This is a file being uploaded: skip it
39             decoded_params[key] = value
40         elif isinstance(value, list):
41             # value is a list: decode each element
42             decoded_params[key] = [v.decode(encoding) for v in value]
43         else:
44             # value is a regular string: decode it
45             decoded_params[key] = value.decode(encoding)
46    
47     # Decode all or nothing, so we can try again on error.
48     cherrypy.request.params = decoded_params
49
50
51 # Encoding
52
53 def encode(encoding=None, errors='strict'):
54     ct = cherrypy.response.headers.elements("Content-Type")
55     if ct:
56         ct = ct[0]
57         if ct.value.lower().startswith("text/"):
58             # Set "charset=..." param on response Content-Type header
59             ct.params['charset'] = find_acceptable_charset(encoding, errors=errors)
60             cherrypy.response.headers["Content-Type"] = str(ct)
61
62 def encode_stream(encoding, errors='strict'):
63     """Encode a streaming response body.
64     
65     Use a generator wrapper, and just pray it works as the stream is
66     being written out.
67     """
68     def encoder(body):
69         for line in body:
70             yield line.encode(encoding, errors)
71     cherrypy.response.body = encoder(cherrypy.response.body)
72     return True
73
74 def encode_string(encoding, errors='strict'):
75     """Encode a buffered response body."""
76     try:
77         body = []
78         for chunk in cherrypy.response.body:
79             body.append(chunk.encode(encoding, errors))
80         cherrypy.response.body = body
81     except UnicodeError:
82         return False
83     else:
84         return True
85
86 def find_acceptable_charset(encoding=None, default_encoding='utf-8', errors='strict'):
87     response = cherrypy.response
88    
89     attempted_charsets = []
90    
91     stream = cherrypy.config.get("stream_response", False)
92     if stream:
93         encoder = encode_stream
94     else:
95         response.collapse_body()
96         encoder = encode_string
97    
98     failmsg = "The response could not be encoded with %s"
99    
100     if encoding is not None:
101         # If specified, force this encoding to be used, or fail.
102         if encoder(encoding, errors):
103             return encoding
104         else:
105             raise cherrypy.HTTPError(500, failmsg % encoding)
106    
107     # Parse the Accept_Charset request header, and try to provide one
108     # of the requested charsets (in order of user preference).
109     encs = cherrypy.request.headers.elements('Accept-Charset')
110     if not encs:
111         # Any character-set is acceptable.
112         charsets = []
113         if encoder(default_encoding, errors):
114             return default_encoding
115         else:
116             raise cherrypy.HTTPError(500, failmsg % default_encoding)
117     else:
118         charsets = [enc.value.lower() for enc in encs]
119         if "*" not in charsets:
120             # If no "*" is present in an Accept-Charset field, then all
121             # character sets not explicitly mentioned get a quality
122             # value of 0, except for ISO-8859-1, which gets a quality
123             # value of 1 if not explicitly mentioned.
124             iso = 'iso-8859-1'
125             if iso not in charsets:
126                 attempted_charsets.append(iso)
127                 if encoder(iso, errors):
128                     return iso
129        
130         for element in encs:
131             if element.qvalue > 0:
132                 if element.value == "*":
133                     # Matches any charset. Try our default.
134                     if default_encoding not in attempted_charsets:
135                         attempted_charsets.append(default_encoding)
136                         if encoder(default_encoding, errors):
137                             return default_encoding
138                 else:
139                     encoding = element.value
140                     if encoding not in attempted_charsets:
141                         attempted_charsets.append(encoding)
142                         if encoder(encoding, errors):
143                             return encoding
144    
145     # No suitable encoding found.
146     ac = cherrypy.request.headers.get('Accept-Charset')
147     if ac is None:
148         msg = "Your client did not send an Accept-Charset header."
149     else:
150         msg = "Your client sent this Accept-Charset header: %s." % ac
151     msg += " We tried these charsets: %s." % ", ".join(attempted_charsets)
152     raise cherrypy.HTTPError(406, msg)
153
154
155 # GZIP
156
157 def compress(body, compress_level):
158     """Compress 'body' at the given compress_level."""
159     import zlib
160    
161     yield '\037\213'      # magic header
162     yield '\010'         # compression method
163     yield '\0'
164     yield struct.pack("<L", long(time.time()))
165     yield '\002'
166     yield '\377'
167    
168     crc = zlib.crc32("")
169     size = 0
170     zobj = zlib.compressobj(compress_level,
171                             zlib.DEFLATED, -zlib.MAX_WBITS,
172                             zlib.DEF_MEM_LEVEL, 0)
173     for line in body:
174         size += len(line)
175         crc = zlib.crc32(line, crc)
176         yield zobj.compress(line)
177     yield zobj.flush()
178     yield struct.pack("<l", crc)
179     yield struct.pack("<L", size & 0xFFFFFFFFL)
180
181 def gzip(compress_level=9, mime_types=['text/html', 'text/plain']):
182     response = cherrypy.response
183     if not response.body:
184         # Response body is empty (might be a 304 for instance)
185         return
186    
187     def zipit():
188         # Return a generator that compresses the page
189         varies = response.headers.get("Vary", "")
190         varies = [x.strip() for x in varies.split(",") if x.strip()]
191         if "Accept-Encoding" not in varies:
192             varies.append("Accept-Encoding")
193         response.headers['Vary'] = ", ".join(varies)
194        
195         response.headers['Content-Encoding'] = 'gzip'
196         response.body = compress(response.body, compress_level)
197    
198     acceptable = cherrypy.request.headers.elements('Accept-Encoding')
199     if not acceptable:
200         # If no Accept-Encoding field is present in a request,
201         # the server MAY assume that the client will accept any
202         # content coding. In this case, if "identity" is one of
203         # the available content-codings, then the server SHOULD use
204         # the "identity" content-coding, unless it has additional
205         # information that a different content-coding is meaningful
206         # to the client.
207         return
208    
209     ct = response.headers.get('Content-Type').split(';')[0]
210     for coding in acceptable:
211         if coding.value == 'identity' and coding.qvalue != 0:
212             return
213         if coding.value in ('gzip', 'x-gzip'):
214             if coding.qvalue == 0:
215                 return
216             if ct in mime_types:
217                 zipit()
218             return
219     cherrypy.HTTPError(406, "identity, gzip").set_response()
220
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets