1# -*- coding: utf-8 -*-
2import StringIO
3
4import webapp2
5
6import test_base
7
8
9def _norm_req(s):
10    return '\r\n'.join(s.strip().replace('\r','').split('\n'))
11
12_test_req = """
13POST /webob/ HTTP/1.0
14Accept: */*
15Cache-Control: max-age=0
16Content-Type: multipart/form-data; boundary=----------------------------deb95b63e42a
17Host: pythonpaste.org
18User-Agent: UserAgent/1.0 (identifier-version) library/7.0 otherlibrary/0.8
19
20------------------------------deb95b63e42a
21Content-Disposition: form-data; name="foo"
22
23foo
24------------------------------deb95b63e42a
25Content-Disposition: form-data; name="bar"; filename="bar.txt"
26Content-type: application/octet-stream
27
28these are the contents of the file 'bar.txt'
29
30------------------------------deb95b63e42a--
31"""
32
33_test_req2 = """
34POST / HTTP/1.0
35Content-Length: 0
36
37"""
38
39_test_req = _norm_req(_test_req)
40_test_req2 = _norm_req(_test_req2) + '\r\n'
41
42
43class TestRequest(test_base.BaseTestCase):
44    def test_charset(self):
45        req = webapp2.Request.blank('/', environ={
46            'CONTENT_TYPE': 'text/html; charset=ISO-8859-4',
47        })
48        self.assertEqual(req.content_type, 'text/html')
49        self.assertEqual(req.charset, 'iso-8859-4')
50
51        req = webapp2.Request.blank('/', environ={
52            'CONTENT_TYPE': 'application/json; charset="ISO-8859-1"',
53        })
54        self.assertEqual(req.content_type, 'application/json')
55        self.assertEqual(req.charset, 'iso-8859-1')
56
57        req = webapp2.Request.blank('/', environ={
58            'CONTENT_TYPE': 'application/json',
59        })
60        self.assertEqual(req.content_type, 'application/json')
61        self.assertEqual(req.charset.lower(), 'utf-8')
62
63        match = webapp2._charset_re.search('text/html')
64        if match:
65            charset = match.group(1).lower().strip().strip('"').strip()
66        else:
67            charset = 'utf-8'
68        self.assertEqual(charset, 'utf-8')
69
70        match = webapp2._charset_re.search('text/html; charset=ISO-8859-4')
71        if match:
72            charset = match.group(1).lower().strip().strip('"').strip()
73        else:
74            charset = 'utf-8'
75        self.assertEqual(charset, 'iso-8859-4')
76
77        match = webapp2._charset_re.search('text/html; charset="ISO-8859-4"')
78        if match:
79            charset = match.group(1).lower().strip().strip('"').strip()
80        else:
81            charset = 'utf-8'
82        self.assertEqual(charset, 'iso-8859-4')
83
84        match = webapp2._charset_re.search('text/html; charset=  "  ISO-8859-4  "  ')
85        if match:
86            charset = match.group(1).lower().strip().strip('"').strip()
87        else:
88            charset = 'utf-8'
89        self.assertEqual(charset, 'iso-8859-4')
90
91    def test_unicode(self):
92        req = webapp2.Request.blank('/?1=2', POST='3=4')
93
94        res = req.GET.get('1')
95        self.assertEqual(res, '2')
96        self.assertTrue(isinstance(res, unicode))
97
98        res = req.str_GET.get('1')
99        self.assertEqual(res, '2')
100        self.assertTrue(isinstance(res, str))
101
102        res = req.POST.get('3')
103        self.assertEqual(res, '4')
104        self.assertTrue(isinstance(res, unicode))
105
106        res = req.str_POST.get('3')
107        self.assertEqual(res, '4')
108        self.assertTrue(isinstance(res, str))
109
110    def test_cookie_unicode(self):
111        import urllib
112        import base64
113
114        # With base64 ---------------------------------------------------------
115
116        value = base64.b64encode(u'á'.encode('utf-8'))
117        rsp = webapp2.Response()
118        rsp.set_cookie('foo', value)
119
120        cookie = rsp.headers.get('Set-Cookie')
121        req = webapp2.Request.blank('/', headers=[('Cookie', cookie)])
122
123        self.assertEqual(req.cookies.get('foo'), value)
124        self.assertEqual(base64.b64decode(req.cookies.get('foo')).decode('utf-8'), u'á')
125
126        # Without quote -------------------------------------------------------
127
128        # Most recent WebOb versions take care of quoting.
129        # (not the version available on App Engine though)
130
131        value = u'föö=bär; föo, bär, bäz=dïng;'
132        rsp = webapp2.Response()
133        rsp.set_cookie('foo', value)
134
135        cookie = rsp.headers.get('Set-Cookie')
136        req = webapp2.Request.blank('/', headers=[('Cookie', cookie)])
137
138        self.assertEqual(req.cookies.get('foo'), value)
139
140        # With quote, hard way ------------------------------------------------
141
142        # Here is our test value.
143        x = u'föö'
144        # We must store cookies quoted. To quote unicode, we need to encode it.
145        y = urllib.quote(x.encode('utf8'))
146        # The encoded, quoted string looks ugly.
147        self.assertEqual(y, 'f%C3%B6%C3%B6')
148        # But it is easy to get it back to our initial value.
149        z = urllib.unquote(y).decode('utf8')
150        # And it is indeed the same value.
151        self.assertEqual(z, x)
152
153        # Set a cookie using the encoded/quoted value.
154        rsp = webapp2.Response()
155        rsp.set_cookie('foo', y)
156        cookie = rsp.headers.get('Set-Cookie')
157        self.assertEqual(cookie, 'foo=f%C3%B6%C3%B6; Path=/')
158
159        # Get the cookie back.
160        req = webapp2.Request.blank('/', headers=[('Cookie', cookie)])
161        self.assertEqual(req.cookies.get('foo'), y)
162        # Here is our original value, again. Problem: the value is decoded
163        # before we had a chance to unquote it.
164        w = urllib.unquote(req.cookies.get('foo').encode('utf8')).decode('utf8')
165        # And it is indeed the same value.
166        self.assertEqual(w, x)
167
168        # With quote, easy way ------------------------------------------------
169
170        value = u'föö=bär; föo, bär, bäz=dïng;'
171        quoted_value = urllib.quote(value.encode('utf8'))
172        rsp = webapp2.Response()
173        rsp.set_cookie('foo', quoted_value)
174
175        cookie = rsp.headers.get('Set-Cookie')
176        req = webapp2.Request.blank('/', headers=[('Cookie', cookie)])
177
178        cookie_value = req.str_cookies.get('foo')
179        unquoted_cookie_value = urllib.unquote(cookie_value).decode('utf-8')
180        self.assertEqual(cookie_value, quoted_value)
181        self.assertEqual(unquoted_cookie_value, value)
182
183    def test_get(self):
184        req = webapp2.Request.blank('/?1=2&1=3&3=4', POST='5=6&7=8')
185
186        res = req.get('1')
187        self.assertEqual(res, '2')
188
189        res = req.get('1', allow_multiple=True)
190        self.assertEqual(res, ['2', '3'])
191
192        res = req.get('8')
193        self.assertEqual(res, '')
194
195        res = req.get('8', allow_multiple=True)
196        self.assertEqual(res, [])
197
198        res = req.get('8', default_value='9')
199        self.assertEqual(res, '9')
200
201    def test_get_with_POST(self):
202        req = webapp2.Request.blank('/?1=2&1=3&3=4', POST={5: 6, 7: 8},
203                                    unicode_errors='ignore')
204
205        res = req.get('1')
206        self.assertEqual(res, '2')
207
208        res = req.get('1', allow_multiple=True)
209        self.assertEqual(res, ['2', '3'])
210
211        res = req.get('8')
212        self.assertEqual(res, '')
213
214        res = req.get('8', allow_multiple=True)
215        self.assertEqual(res, [])
216
217        res = req.get('8', default_value='9')
218        self.assertEqual(res, '9')
219
220    def test_arguments(self):
221        req = webapp2.Request.blank('/?1=2&3=4', POST='5=6&7=8')
222
223        res = req.arguments()
224        self.assertEqual(res, ['1', '3', '5', '7'])
225
226    def test_get_range(self):
227        req = webapp2.Request.blank('/')
228        res = req.get_range('1', min_value=None, max_value=None, default=None)
229        self.assertEqual(res, None)
230
231        req = webapp2.Request.blank('/?1=2')
232        res = req.get_range('1', min_value=None, max_value=None, default=0)
233        self.assertEqual(res, 2)
234
235        req = webapp2.Request.blank('/?1=foo')
236        res = req.get_range('1', min_value=1, max_value=99, default=100)
237        self.assertEqual(res, 99)
238
239    def test_issue_3426(self):
240        """When the content-type is 'application/x-www-form-urlencoded' and
241        POST data is empty the content-type is dropped by Google appengine.
242        """
243        req = webapp2.Request.blank('/', environ={
244            'REQUEST_METHOD': 'GET',
245            'CONTENT_TYPE': 'application/x-www-form-urlencoded',
246        })
247        self.assertEqual(req.method, 'GET')
248        self.assertEqual(req.content_type, 'application/x-www-form-urlencoded')
249
250    # XXX: These tests fail when request charset is set to utf-8 by default.
251    # Otherwise they pass.
252    '''
253    def test_get_with_FieldStorage(self):
254        if not test_base.check_webob_version(1.0):
255            return
256        # A valid request without a Content-Length header should still read
257        # the full body.
258        # Also test parity between as_string and from_string / from_file.
259        import cgi
260        req = webapp2.Request.from_string(_test_req)
261        self.assertTrue(isinstance(req, webapp2.Request))
262        self.assertTrue(not repr(req).endswith('(invalid WSGI environ)>'))
263        self.assertTrue('\n' not in req.http_version or '\r' in req.http_version)
264        self.assertTrue(',' not in req.host)
265        self.assertTrue(req.content_length is not None)
266        self.assertEqual(req.content_length, 337)
267        self.assertTrue('foo' in req.body)
268        bar_contents = "these are the contents of the file 'bar.txt'\r\n"
269        self.assertTrue(bar_contents in req.body)
270        self.assertEqual(req.params['foo'], 'foo')
271        bar = req.params['bar']
272        self.assertTrue(isinstance(bar, cgi.FieldStorage))
273        self.assertEqual(bar.type, 'application/octet-stream')
274        bar.file.seek(0)
275        self.assertEqual(bar.file.read(), bar_contents)
276
277        bar = req.get_all('bar')
278        self.assertEqual(bar[0], bar_contents)
279
280        # out should equal contents, except for the Content-Length header,
281        # so insert that.
282        _test_req_copy = _test_req.replace('Content-Type',
283                            'Content-Length: 337\r\nContent-Type')
284        self.assertEqual(str(req), _test_req_copy)
285
286        req2 = webapp2.Request.from_string(_test_req2)
287        self.assertTrue('host' not in req2.headers)
288        self.assertEqual(str(req2), _test_req2.rstrip())
289        self.assertRaises(ValueError,
290                          webapp2.Request.from_string, _test_req2 + 'xx')
291
292    def test_issue_5118(self):
293        """Unable to read POST variables ONCE self.request.body is read."""
294        if not test_base.check_webob_version(1.0):
295            return
296        import cgi
297        req = webapp2.Request.from_string(_test_req)
298        fieldStorage = req.POST.get('bar')
299        self.assertTrue(isinstance(fieldStorage, cgi.FieldStorage))
300        self.assertEqual(fieldStorage.type, 'application/octet-stream')
301        # Double read.
302        fieldStorage = req.POST.get('bar')
303        self.assertTrue(isinstance(fieldStorage, cgi.FieldStorage))
304        self.assertEqual(fieldStorage.type, 'application/octet-stream')
305        # Now read the body.
306        x = req.body
307        fieldStorage = req.POST.get('bar')
308        self.assertTrue(isinstance(fieldStorage, cgi.FieldStorage))
309        self.assertEqual(fieldStorage.type, 'application/octet-stream')
310    '''
311
312if __name__ == '__main__':
313    test_base.main()
314