~ubuntu-branches/debian/jessie/cherrypy3/jessie

« back to all changes in this revision

Viewing changes to cherrypy/test/test_encoding.py

  • Committer: Package Import Robot
  • Author(s): Gustavo Noronha Silva, JCF Ploemen, Stéphane Graber, Gustavo Noronha
  • Date: 2012-01-06 10:13:27 UTC
  • mfrom: (1.1.4) (7.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20120106101327-smxnhguqs14ubl7e
Tags: 3.2.2-1
[ JCF Ploemen ]
* New upstream release (Closes: #571196).
* Bumped Standards-Version to 3.8.4 (no changes needed).
* Removing patch 02: no longer needed, incorporated upstream.
* Updating patch 00 to match release.
* Install cherryd man page via debian/manpages.
* debian/copyright:
  + Added notice for cherrypy/lib/httpauth.py.
  + Fixed years.
* debian/watch:
  + Don't hit on the -py3 release by blocking '-' from the version.
  + Mangle upstream version, inserting a tilde for beta/rc.

[ Stéphane Graber <stgraber@ubuntu.com> ]
 * Convert from python-support to dh_python2 (#654375)
  - debian/pyversions: Removed (no longer needed)
  - debian/rules
   + Replace call to dh_pysupport by dh_python2
   + Add --with=python2 to all dh calls
  - debian/control
   + Drop build-depends on python-support
   + Bump build-depends on python-all to >= 2.6.6-3~
   + Replace XS-Python-Version by X-Python-Version
   + Remove XB-Python-Version from binary package

[ Gustavo Noronha ]
* debian/control, debian/rules, debian/manpages:
 - use help2man to generate a manpage for cherryd at build time, since
  one is no longer shipped along with the source code
* debian/control:
- add python-nose to Build-Depends, since it's used during the
  documentation build for cross-reference generation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from cherrypy.test import test
2
 
test.prefer_parent_path()
3
1
 
 
2
import gzip
4
3
import sys
5
 
import gzip, StringIO
6
 
from httplib import IncompleteRead
 
4
 
7
5
import cherrypy
8
 
europoundUnicode = u'\x80\xa3'
9
 
europoundUtf8 = u'\x80\xa3'.encode('utf-8')
10
 
sing = u"\u6bdb\u6cfd\u4e1c: Sing, Little Birdie?"
 
6
from cherrypy._cpcompat import BytesIO, IncompleteRead, ntob, ntou
 
7
 
 
8
europoundUnicode = ntou('\x80\xa3')
 
9
sing = ntou("\u6bdb\u6cfd\u4e1c: Sing, Little Birdie?", 'escape')
11
10
sing8 = sing.encode('utf-8')
12
11
sing16 = sing.encode('utf-16')
13
12
 
14
13
 
15
 
def setup_server():
16
 
    class Root:
17
 
        def index(self, param):
18
 
            assert param == europoundUnicode
19
 
            yield europoundUnicode
20
 
        index.exposed = True
21
 
        
22
 
        def mao_zedong(self):
23
 
            return sing
24
 
        mao_zedong.exposed = True
25
 
        
26
 
        def utf8(self):
27
 
            return sing8
28
 
        utf8.exposed = True
29
 
        utf8._cp_config = {'tools.encode.encoding': 'utf-8'}
30
 
        
31
 
        def reqparams(self, *args, **kwargs):
32
 
            return repr(cherrypy.request.params)
33
 
        reqparams.exposed = True
34
 
    
35
 
    class GZIP:
36
 
        def index(self):
37
 
            yield "Hello, world"
38
 
        index.exposed = True
39
 
        
40
 
        def noshow(self):
41
 
            # Test for ticket #147, where yield showed no exceptions (content-
42
 
            # encoding was still gzip even though traceback wasn't zipped).
43
 
            raise IndexError()
44
 
            yield "Here be dragons"
45
 
        noshow.exposed = True
46
 
        
47
 
        def noshow_stream(self):
48
 
            # Test for ticket #147, where yield showed no exceptions (content-
49
 
            # encoding was still gzip even though traceback wasn't zipped).
50
 
            raise IndexError()
51
 
            yield "Here be dragons"
52
 
        noshow_stream.exposed = True
53
 
        noshow_stream._cp_config = {'response.stream': True}
54
 
    
55
 
    cherrypy.config.update({
56
 
            'environment': 'test_suite',
57
 
            'tools.encode.on': True,
58
 
            'tools.decode.on': True,
59
 
    })
60
 
    
61
 
    root = Root()
62
 
    root.gzip = GZIP()
63
 
    cherrypy.tree.mount(root, config={'/gzip': {'tools.gzip.on': True}})
64
 
 
65
 
 
66
 
 
67
14
from cherrypy.test import helper
68
15
 
69
16
 
70
17
class EncodingTests(helper.CPWebCase):
71
 
    
72
 
    def testDecoding(self):
 
18
 
 
19
    def setup_server():
 
20
        class Root:
 
21
            def index(self, param):
 
22
                assert param == europoundUnicode, "%r != %r" % (param, europoundUnicode)
 
23
                yield europoundUnicode
 
24
            index.exposed = True
 
25
            
 
26
            def mao_zedong(self):
 
27
                return sing
 
28
            mao_zedong.exposed = True
 
29
            
 
30
            def utf8(self):
 
31
                return sing8
 
32
            utf8.exposed = True
 
33
            utf8._cp_config = {'tools.encode.encoding': 'utf-8'}
 
34
            
 
35
            def cookies_and_headers(self):
 
36
                # if the headers have non-ascii characters and a cookie has
 
37
                #  any part which is unicode (even ascii), the response
 
38
                #  should not fail.
 
39
                cherrypy.response.cookie['candy'] = 'bar'
 
40
                cherrypy.response.cookie['candy']['domain'] = 'cherrypy.org'
 
41
                cherrypy.response.headers['Some-Header'] = 'My d\xc3\xb6g has fleas'
 
42
                return 'Any content'
 
43
            cookies_and_headers.exposed = True
 
44
 
 
45
            def reqparams(self, *args, **kwargs):
 
46
                return ntob(', ').join([": ".join((k, v)).encode('utf8')
 
47
                                  for k, v in cherrypy.request.params.items()])
 
48
            reqparams.exposed = True
 
49
            
 
50
            def nontext(self, *args, **kwargs):
 
51
                cherrypy.response.headers['Content-Type'] = 'application/binary'
 
52
                return '\x00\x01\x02\x03'
 
53
            nontext.exposed = True
 
54
            nontext._cp_config = {'tools.encode.text_only': False,
 
55
                                  'tools.encode.add_charset': True,
 
56
                                  }
 
57
        
 
58
        class GZIP:
 
59
            def index(self):
 
60
                yield "Hello, world"
 
61
            index.exposed = True
 
62
            
 
63
            def noshow(self):
 
64
                # Test for ticket #147, where yield showed no exceptions (content-
 
65
                # encoding was still gzip even though traceback wasn't zipped).
 
66
                raise IndexError()
 
67
                yield "Here be dragons"
 
68
            noshow.exposed = True
 
69
            # Turn encoding off so the gzip tool is the one doing the collapse.
 
70
            noshow._cp_config = {'tools.encode.on': False}
 
71
            
 
72
            def noshow_stream(self):
 
73
                # Test for ticket #147, where yield showed no exceptions (content-
 
74
                # encoding was still gzip even though traceback wasn't zipped).
 
75
                raise IndexError()
 
76
                yield "Here be dragons"
 
77
            noshow_stream.exposed = True
 
78
            noshow_stream._cp_config = {'response.stream': True}
 
79
        
 
80
        class Decode:
 
81
            def extra_charset(self, *args, **kwargs):
 
82
                return ', '.join([": ".join((k, v))
 
83
                                  for k, v in cherrypy.request.params.items()])
 
84
            extra_charset.exposed = True
 
85
            extra_charset._cp_config = {
 
86
                'tools.decode.on': True,
 
87
                'tools.decode.default_encoding': ['utf-16'],
 
88
                }
 
89
            
 
90
            def force_charset(self, *args, **kwargs):
 
91
                return ', '.join([": ".join((k, v))
 
92
                                  for k, v in cherrypy.request.params.items()])
 
93
            force_charset.exposed = True
 
94
            force_charset._cp_config = {
 
95
                'tools.decode.on': True,
 
96
                'tools.decode.encoding': 'utf-16',
 
97
                }
 
98
        
 
99
        root = Root()
 
100
        root.gzip = GZIP()
 
101
        root.decode = Decode()
 
102
        cherrypy.tree.mount(root, config={'/gzip': {'tools.gzip.on': True}})
 
103
    setup_server = staticmethod(setup_server)
 
104
 
 
105
    def test_query_string_decoding(self):
73
106
        europoundUtf8 = europoundUnicode.encode('utf-8')
74
 
        self.getPage('/?param=%s' % europoundUtf8)
 
107
        self.getPage(ntob('/?param=') + europoundUtf8)
75
108
        self.assertBody(europoundUtf8)
76
109
        
77
 
        # Make sure that encoded utf8 gets parsed correctly
 
110
        # Encoded utf8 query strings MUST be parsed correctly.
 
111
        # Here, q is the POUND SIGN U+00A3 encoded in utf8 and then %HEX
78
112
        self.getPage("/reqparams?q=%C2%A3")
79
 
        self.assertBody(r"{'q': u'\xa3'}")
 
113
        # The return value will be encoded as utf8.
 
114
        self.assertBody(ntob("q: \xc2\xa3"))
 
115
        
 
116
        # Query strings that are incorrectly encoded MUST raise 404.
 
117
        # Here, q is the POUND SIGN U+00A3 encoded in latin1 and then %HEX
 
118
        self.getPage("/reqparams?q=%A3")
 
119
        self.assertStatus(404)
 
120
        self.assertErrorPage(404, 
 
121
            "The given query string could not be processed. Query "
 
122
            "strings for this resource must be encoded with 'utf8'.")
 
123
    
 
124
    def test_urlencoded_decoding(self):
 
125
        # Test the decoding of an application/x-www-form-urlencoded entity.
 
126
        europoundUtf8 = europoundUnicode.encode('utf-8')
 
127
        body=ntob("param=") + europoundUtf8
 
128
        self.getPage('/', method='POST',
 
129
                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
 
130
                              ("Content-Length", str(len(body))),
 
131
                              ],
 
132
                     body=body),
 
133
        self.assertBody(europoundUtf8)
 
134
        
 
135
        # Encoded utf8 entities MUST be parsed and decoded correctly.
 
136
        # Here, q is the POUND SIGN U+00A3 encoded in utf8
 
137
        body = ntob("q=\xc2\xa3")
 
138
        self.getPage('/reqparams', method='POST',
 
139
                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
 
140
                              ("Content-Length", str(len(body))),
 
141
                              ],
 
142
                     body=body),
 
143
        self.assertBody(ntob("q: \xc2\xa3"))
 
144
        
 
145
        # ...and in utf16, which is not in the default attempt_charsets list:
 
146
        body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
 
147
        self.getPage('/reqparams', method='POST',
 
148
                     headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-16"),
 
149
                              ("Content-Length", str(len(body))),
 
150
                              ],
 
151
                     body=body),
 
152
        self.assertBody(ntob("q: \xc2\xa3"))
 
153
        
 
154
        # Entities that are incorrectly encoded MUST raise 400.
 
155
        # Here, q is the POUND SIGN U+00A3 encoded in utf16, but
 
156
        # the Content-Type incorrectly labels it utf-8.
 
157
        body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
 
158
        self.getPage('/reqparams', method='POST',
 
159
                     headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"),
 
160
                              ("Content-Length", str(len(body))),
 
161
                              ],
 
162
                     body=body),
 
163
        self.assertStatus(400)
 
164
        self.assertErrorPage(400, 
 
165
            "The request entity could not be decoded. The following charsets "
 
166
            "were attempted: ['utf-8']")
 
167
    
 
168
    def test_decode_tool(self):
 
169
        # An extra charset should be tried first, and succeed if it matches.
 
170
        # Here, we add utf-16 as a charset and pass a utf-16 body.
 
171
        body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
 
172
        self.getPage('/decode/extra_charset', method='POST',
 
173
                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
 
174
                              ("Content-Length", str(len(body))),
 
175
                              ],
 
176
                     body=body),
 
177
        self.assertBody(ntob("q: \xc2\xa3"))
 
178
        
 
179
        # An extra charset should be tried first, and continue to other default
 
180
        # charsets if it doesn't match.
 
181
        # Here, we add utf-16 as a charset but still pass a utf-8 body.
 
182
        body = ntob("q=\xc2\xa3")
 
183
        self.getPage('/decode/extra_charset', method='POST',
 
184
                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
 
185
                              ("Content-Length", str(len(body))),
 
186
                              ],
 
187
                     body=body),
 
188
        self.assertBody(ntob("q: \xc2\xa3"))
 
189
        
 
190
        # An extra charset should error if force is True and it doesn't match.
 
191
        # Here, we force utf-16 as a charset but still pass a utf-8 body.
 
192
        body = ntob("q=\xc2\xa3")
 
193
        self.getPage('/decode/force_charset', method='POST',
 
194
                     headers=[("Content-Type", "application/x-www-form-urlencoded"),
 
195
                              ("Content-Length", str(len(body))),
 
196
                              ],
 
197
                     body=body),
 
198
        self.assertErrorPage(400, 
 
199
            "The request entity could not be decoded. The following charsets "
 
200
            "were attempted: ['utf-16']")
 
201
    
 
202
    def test_multipart_decoding(self):
 
203
        # Test the decoding of a multipart entity when the charset (utf16) is
 
204
        # explicitly given.
 
205
        body=ntob('\r\n'.join(['--X',
 
206
                               'Content-Type: text/plain;charset=utf-16',
 
207
                               'Content-Disposition: form-data; name="text"',
 
208
                               '',
 
209
                               '\xff\xfea\x00b\x00\x1c c\x00',
 
210
                               '--X',
 
211
                               'Content-Type: text/plain;charset=utf-16',
 
212
                               'Content-Disposition: form-data; name="submit"',
 
213
                               '',
 
214
                               '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
 
215
                               '--X--']))
 
216
        self.getPage('/reqparams', method='POST',
 
217
                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
 
218
                              ("Content-Length", str(len(body))),
 
219
                              ],
 
220
                     body=body),
 
221
        self.assertBody(ntob("text: ab\xe2\x80\x9cc, submit: Create"))
 
222
    
 
223
    def test_multipart_decoding_no_charset(self):
 
224
        # Test the decoding of a multipart entity when the charset (utf8) is
 
225
        # NOT explicitly given, but is in the list of charsets to attempt.
 
226
        body=ntob('\r\n'.join(['--X',
 
227
                               'Content-Disposition: form-data; name="text"',
 
228
                               '',
 
229
                               '\xe2\x80\x9c',
 
230
                               '--X',
 
231
                               'Content-Disposition: form-data; name="submit"',
 
232
                               '',
 
233
                               'Create',
 
234
                               '--X--']))
 
235
        self.getPage('/reqparams', method='POST',
 
236
                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
 
237
                              ("Content-Length", str(len(body))),
 
238
                              ],
 
239
                     body=body),
 
240
        self.assertBody(ntob("text: \xe2\x80\x9c, submit: Create"))
 
241
    
 
242
    def test_multipart_decoding_no_successful_charset(self):
 
243
        # Test the decoding of a multipart entity when the charset (utf16) is
 
244
        # NOT explicitly given, and is NOT in the list of charsets to attempt.
 
245
        body=ntob('\r\n'.join(['--X',
 
246
                               'Content-Disposition: form-data; name="text"',
 
247
                               '',
 
248
                               '\xff\xfea\x00b\x00\x1c c\x00',
 
249
                               '--X',
 
250
                               'Content-Disposition: form-data; name="submit"',
 
251
                               '',
 
252
                               '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
 
253
                               '--X--']))
 
254
        self.getPage('/reqparams', method='POST',
 
255
                     headers=[("Content-Type", "multipart/form-data;boundary=X"),
 
256
                              ("Content-Length", str(len(body))),
 
257
                              ],
 
258
                     body=body),
 
259
        self.assertStatus(400)
 
260
        self.assertErrorPage(400, 
 
261
            "The request entity could not be decoded. The following charsets "
 
262
            "were attempted: ['us-ascii', 'utf-8']")
 
263
    
 
264
    def test_nontext(self):
 
265
        self.getPage('/nontext')
 
266
        self.assertHeader('Content-Type', 'application/binary;charset=utf-8')
 
267
        self.assertBody('\x00\x01\x02\x03')
80
268
    
81
269
    def testEncoding(self):
82
270
        # Default encoding should be utf-8
111
299
        self.assertStatus("406 Not Acceptable")
112
300
        self.assertInBody("Your client sent this Accept-Charset header: "
113
301
                          "us-ascii, ISO-8859-1, x-mac-ce. We tried these "
114
 
                          "charsets: x-mac-ce, us-ascii, ISO-8859-1.")
 
302
                          "charsets: ISO-8859-1, us-ascii, x-mac-ce.")
115
303
        
116
304
        # Test the 'encoding' arg to encode.
117
305
        self.getPage('/utf8')
120
308
        self.assertStatus("406 Not Acceptable")
121
309
    
122
310
    def testGzip(self):
123
 
        zbuf = StringIO.StringIO()
 
311
        zbuf = BytesIO()
124
312
        zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
125
 
        zfile.write("Hello, world")
 
313
        zfile.write(ntob("Hello, world"))
126
314
        zfile.close()
127
315
        
128
316
        self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip")])
132
320
        
133
321
        # Test when gzip is denied.
134
322
        self.getPage('/gzip/', headers=[("Accept-Encoding", "identity")])
135
 
        self.assertNoHeader("Vary")
 
323
        self.assertHeader("Vary", "Accept-Encoding")
 
324
        self.assertNoHeader("Content-Encoding")
136
325
        self.assertBody("Hello, world")
137
326
        
138
327
        self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip;q=0")])
139
 
        self.assertNoHeader("Vary")
 
328
        self.assertHeader("Vary", "Accept-Encoding")
 
329
        self.assertNoHeader("Content-Encoding")
140
330
        self.assertBody("Hello, world")
141
331
        
142
332
        self.getPage('/gzip/', headers=[("Accept-Encoding", "*;q=0")])
143
333
        self.assertStatus(406)
144
 
        self.assertNoHeader("Vary")
 
334
        self.assertNoHeader("Content-Encoding")
145
335
        self.assertErrorPage(406, "identity, gzip")
146
336
        
147
337
        # Test for ticket #147
167
357
                              '/gzip/noshow_stream',
168
358
                              headers=[("Accept-Encoding", "gzip")])
169
359
 
170
 
if __name__ == "__main__":
171
 
    setup_server()
172
 
    helper.testmain()
 
360
    def test_UnicodeHeaders(self):
 
361
        self.getPage('/cookies_and_headers')
 
362
        self.assertBody('Any content')
 
363