blob: 345016399756f4a544afbec934a1314dd8c323f7 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001from __future__ import nested_scopes # Backward compat for 2.1
Guido van Rossumd8faa362007-04-27 19:54:29 +00002from unittest import TestCase
Thomas Wouters0e3f5912006-08-11 14:57:12 +00003from wsgiref.util import setup_testing_defaults
4from wsgiref.headers import Headers
5from wsgiref.handlers import BaseHandler, BaseCGIHandler
6from wsgiref import util
7from wsgiref.validate import validator
8from wsgiref.simple_server import WSGIServer, WSGIRequestHandler, demo_app
9from wsgiref.simple_server import make_server
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000010from io import StringIO, BytesIO, BufferedReader
Alexandre Vassalottice261952008-05-12 02:31:37 +000011from socketserver import BaseServer
Antoine Pitroud6fcd562009-11-03 17:21:14 +000012import os
13import re
14import sys
Thomas Wouters0e3f5912006-08-11 14:57:12 +000015
Benjamin Petersonee8712c2008-05-20 21:35:26 +000016from test import support
Thomas Wouters0e3f5912006-08-11 14:57:12 +000017
18class MockServer(WSGIServer):
19 """Non-socket HTTP server"""
20
21 def __init__(self, server_address, RequestHandlerClass):
22 BaseServer.__init__(self, server_address, RequestHandlerClass)
23 self.server_bind()
24
25 def server_bind(self):
26 host, port = self.server_address
27 self.server_name = host
28 self.server_port = port
29 self.setup_environ()
30
31
32class MockHandler(WSGIRequestHandler):
33 """Non-socket HTTP handler"""
34 def setup(self):
35 self.connection = self.request
36 self.rfile, self.wfile = self.connection
37
38 def finish(self):
39 pass
40
41
42
43
44
45def hello_app(environ,start_response):
46 start_response("200 OK", [
47 ('Content-Type','text/plain'),
48 ('Date','Mon, 05 Jun 2006 18:49:54 GMT')
49 ])
50 return ["Hello, world!"]
51
Guido van Rossum6a10e022007-08-08 17:01:45 +000052def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
Thomas Wouters0e3f5912006-08-11 14:57:12 +000053 server = make_server("", 80, app, MockServer, MockHandler)
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000054 inp = BufferedReader(BytesIO(data))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +000055 out = BytesIO()
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000056 olderr = sys.stderr
57 err = sys.stderr = StringIO()
Thomas Wouters0e3f5912006-08-11 14:57:12 +000058
59 try:
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000060 server.finish_request((inp, out), ("127.0.0.1",8888))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000061 finally:
62 sys.stderr = olderr
63
64 return out.getvalue(), err.getvalue()
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88def compare_generic_iter(make_it,match):
89 """Utility to compare a generic 2.1/2.2+ iterator with an iterable
90
91 If running under Python 2.2+, this tests the iterator using iter()/next(),
92 as well as __getitem__. 'make_it' must be a function returning a fresh
93 iterator to be tested (since this may test the iterator twice)."""
94
95 it = make_it()
96 n = 0
97 for item in match:
98 if not it[n]==item: raise AssertionError
99 n+=1
100 try:
101 it[n]
102 except IndexError:
103 pass
104 else:
105 raise AssertionError("Too many items from __getitem__",it)
106
107 try:
108 iter, StopIteration
109 except NameError:
110 pass
111 else:
112 # Only test iter mode under 2.2+
113 it = make_it()
114 if not iter(it) is it: raise AssertionError
115 for item in match:
Georg Brandla18af4e2007-04-21 15:47:16 +0000116 if not next(it) == item: raise AssertionError
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000117 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000118 next(it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000119 except StopIteration:
120 pass
121 else:
Georg Brandla18af4e2007-04-21 15:47:16 +0000122 raise AssertionError("Too many items from .__next__()", it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000123
124
125
126
127
128
129class IntegrationTests(TestCase):
130
131 def check_hello(self, out, has_length=True):
132 self.assertEqual(out,
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000133 ("HTTP/1.0 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000134 "Server: WSGIServer/0.1 Python/"+sys.version.split()[0]+"\r\n"
135 "Content-Type: text/plain\r\n"
136 "Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" +
137 (has_length and "Content-Length: 13\r\n" or "") +
138 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000139 "Hello, world!").encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000140 )
141
142 def test_plain_hello(self):
143 out, err = run_amock()
144 self.check_hello(out)
145
146 def test_validated_hello(self):
147 out, err = run_amock(validator(hello_app))
148 # the middleware doesn't support len(), so content-length isn't there
149 self.check_hello(out, has_length=False)
150
151 def test_simple_validation_error(self):
152 def bad_app(environ,start_response):
153 start_response("200 OK", ('Content-Type','text/plain'))
154 return ["Hello, world!"]
155 out, err = run_amock(validator(bad_app))
Georg Brandlab91fde2009-08-13 08:51:18 +0000156 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000157 b"A server error occurred. Please contact the administrator."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000158 ))
159 self.assertEqual(
160 err.splitlines()[-2],
161 "AssertionError: Headers (('Content-Type', 'text/plain')) must"
Martin v. Löwis250ad612008-04-07 05:43:42 +0000162 " be of type list: <class 'tuple'>"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000163 )
164
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000165 def test_wsgi_input(self):
166 def bad_app(e,s):
167 e["wsgi.input"].read()
168 s(b"200 OK", [(b"Content-Type", b"text/plain; charset=utf-8")])
169 return [b"data"]
170 out, err = run_amock(validator(bad_app))
Georg Brandlab91fde2009-08-13 08:51:18 +0000171 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000172 b"A server error occurred. Please contact the administrator."
173 ))
174 self.assertEqual(
175 err.splitlines()[-2], "AssertionError"
176 )
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000177
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000178 def test_bytes_validation(self):
179 def app(e, s):
180 s(b"200 OK", [
181 (b"Content-Type", b"text/plain; charset=utf-8"),
182 ("Date", "Wed, 24 Dec 2008 13:29:32 GMT"),
183 ])
184 return [b"data"]
185 out, err = run_amock(validator(app))
Georg Brandlab91fde2009-08-13 08:51:18 +0000186 self.assertTrue(err.endswith('"GET / HTTP/1.0" 200 4\n'))
Antoine Pitrou5f817412009-01-03 18:49:41 +0000187 ver = sys.version.split()[0].encode('ascii')
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000188 self.assertEqual(
189 b"HTTP/1.0 200 OK\r\n"
Antoine Pitrou5f817412009-01-03 18:49:41 +0000190 b"Server: WSGIServer/0.1 Python/" + ver + b"\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000191 b"Content-Type: text/plain; charset=utf-8\r\n"
192 b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n"
193 b"\r\n"
194 b"data",
195 out)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000196
197
198
199
200class UtilityTests(TestCase):
201
202 def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
203 env = {'SCRIPT_NAME':sn_in,'PATH_INFO':pi_in}
204 util.setup_testing_defaults(env)
205 self.assertEqual(util.shift_path_info(env),part)
206 self.assertEqual(env['PATH_INFO'],pi_out)
207 self.assertEqual(env['SCRIPT_NAME'],sn_out)
208 return env
209
210 def checkDefault(self, key, value, alt=None):
211 # Check defaulting when empty
212 env = {}
213 util.setup_testing_defaults(env)
214 if isinstance(value,StringIO):
Georg Brandlab91fde2009-08-13 08:51:18 +0000215 self.assertTrue(isinstance(env[key],StringIO))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000216 elif isinstance(value,BytesIO):
Georg Brandlab91fde2009-08-13 08:51:18 +0000217 self.assertTrue(isinstance(env[key],BytesIO))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000218 else:
219 self.assertEqual(env[key],value)
220
221 # Check existing value
222 env = {key:alt}
223 util.setup_testing_defaults(env)
Georg Brandlab91fde2009-08-13 08:51:18 +0000224 self.assertTrue(env[key] is alt)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000225
226 def checkCrossDefault(self,key,value,**kw):
227 util.setup_testing_defaults(kw)
228 self.assertEqual(kw[key],value)
229
230 def checkAppURI(self,uri,**kw):
231 util.setup_testing_defaults(kw)
232 self.assertEqual(util.application_uri(kw),uri)
233
234 def checkReqURI(self,uri,query=1,**kw):
235 util.setup_testing_defaults(kw)
236 self.assertEqual(util.request_uri(kw,query),uri)
237
238
239
240
241
242
243 def checkFW(self,text,size,match):
244
245 def make_it(text=text,size=size):
246 return util.FileWrapper(StringIO(text),size)
247
248 compare_generic_iter(make_it,match)
249
250 it = make_it()
Georg Brandlab91fde2009-08-13 08:51:18 +0000251 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000252
253 for item in it:
254 pass
255
Georg Brandlab91fde2009-08-13 08:51:18 +0000256 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000257
258 it.close()
Georg Brandlab91fde2009-08-13 08:51:18 +0000259 self.assertTrue(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000260
261
262 def testSimpleShifts(self):
263 self.checkShift('','/', '', '/', '')
264 self.checkShift('','/x', 'x', '/x', '')
265 self.checkShift('/','', None, '/', '')
266 self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
267 self.checkShift('/a','/x/', 'x', '/a/x', '/')
268
269
270 def testNormalizedShifts(self):
271 self.checkShift('/a/b', '/../y', '..', '/a', '/y')
272 self.checkShift('', '/../y', '..', '', '/y')
273 self.checkShift('/a/b', '//y', 'y', '/a/b/y', '')
274 self.checkShift('/a/b', '//y/', 'y', '/a/b/y', '/')
275 self.checkShift('/a/b', '/./y', 'y', '/a/b/y', '')
276 self.checkShift('/a/b', '/./y/', 'y', '/a/b/y', '/')
277 self.checkShift('/a/b', '///./..//y/.//', '..', '/a', '/y/')
278 self.checkShift('/a/b', '///', '', '/a/b/', '')
279 self.checkShift('/a/b', '/.//', '', '/a/b/', '')
280 self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
281 self.checkShift('/a/b', '/.', None, '/a/b', '')
282
283
284 def testDefaults(self):
285 for key, value in [
286 ('SERVER_NAME','127.0.0.1'),
287 ('SERVER_PORT', '80'),
288 ('SERVER_PROTOCOL','HTTP/1.0'),
289 ('HTTP_HOST','127.0.0.1'),
290 ('REQUEST_METHOD','GET'),
291 ('SCRIPT_NAME',''),
292 ('PATH_INFO','/'),
293 ('wsgi.version', (1,0)),
294 ('wsgi.run_once', 0),
295 ('wsgi.multithread', 0),
296 ('wsgi.multiprocess', 0),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000297 ('wsgi.input', BytesIO()),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000298 ('wsgi.errors', StringIO()),
299 ('wsgi.url_scheme','http'),
300 ]:
301 self.checkDefault(key,value)
302
303
304 def testCrossDefaults(self):
305 self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
306 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
307 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="1")
308 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="yes")
309 self.checkCrossDefault('wsgi.url_scheme',"http",HTTPS="foo")
310 self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
311 self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
312
313
314 def testGuessScheme(self):
315 self.assertEqual(util.guess_scheme({}), "http")
316 self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
317 self.assertEqual(util.guess_scheme({'HTTPS':"on"}), "https")
318 self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
319 self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
320
321
322
323
324
325 def testAppURIs(self):
326 self.checkAppURI("http://127.0.0.1/")
327 self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000328 self.checkAppURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000329 self.checkAppURI("http://spam.example.com:2071/",
330 HTTP_HOST="spam.example.com:2071", SERVER_PORT="2071")
331 self.checkAppURI("http://spam.example.com/",
332 SERVER_NAME="spam.example.com")
333 self.checkAppURI("http://127.0.0.1/",
334 HTTP_HOST="127.0.0.1", SERVER_NAME="spam.example.com")
335 self.checkAppURI("https://127.0.0.1/", HTTPS="on")
336 self.checkAppURI("http://127.0.0.1:8000/", SERVER_PORT="8000",
337 HTTP_HOST=None)
338
339 def testReqURIs(self):
340 self.checkReqURI("http://127.0.0.1/")
341 self.checkReqURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000342 self.checkReqURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000343 self.checkReqURI("http://127.0.0.1/spammity/spam",
344 SCRIPT_NAME="/spammity", PATH_INFO="/spam")
Senthil Kumarane5f8e842010-12-29 06:29:58 +0000345 self.checkReqURI("http://127.0.0.1/spammity/spam;ham",
346 SCRIPT_NAME="/spammity", PATH_INFO="/spam;ham")
347 self.checkReqURI("http://127.0.0.1/spammity/spam;cookie=1234,5678",
348 SCRIPT_NAME="/spammity", PATH_INFO="/spam;cookie=1234,5678")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000349 self.checkReqURI("http://127.0.0.1/spammity/spam?say=ni",
350 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
351 self.checkReqURI("http://127.0.0.1/spammity/spam", 0,
352 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
353
354 def testFileWrapper(self):
355 self.checkFW("xyz"*50, 120, ["xyz"*40,"xyz"*10])
356
357 def testHopByHop(self):
358 for hop in (
359 "Connection Keep-Alive Proxy-Authenticate Proxy-Authorization "
360 "TE Trailers Transfer-Encoding Upgrade"
361 ).split():
362 for alt in hop, hop.title(), hop.upper(), hop.lower():
Georg Brandlab91fde2009-08-13 08:51:18 +0000363 self.assertTrue(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000364
365 # Not comprehensive, just a few random header names
366 for hop in (
367 "Accept Cache-Control Date Pragma Trailer Via Warning"
368 ).split():
369 for alt in hop, hop.title(), hop.upper(), hop.lower():
Georg Brandlab91fde2009-08-13 08:51:18 +0000370 self.assertFalse(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000371
372class HeaderTests(TestCase):
373
374 def testMappingInterface(self):
375 test = [('x','y')]
376 self.assertEqual(len(Headers([])),0)
377 self.assertEqual(len(Headers(test[:])),1)
378 self.assertEqual(Headers(test[:]).keys(), ['x'])
379 self.assertEqual(Headers(test[:]).values(), ['y'])
380 self.assertEqual(Headers(test[:]).items(), test)
Georg Brandlab91fde2009-08-13 08:51:18 +0000381 self.assertFalse(Headers(test).items() is test) # must be copy!
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000382
383 h=Headers([])
384 del h['foo'] # should not raise an error
385
386 h['Foo'] = 'bar'
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000387 for m in h.__contains__, h.get, h.get_all, h.__getitem__:
Georg Brandlab91fde2009-08-13 08:51:18 +0000388 self.assertTrue(m('foo'))
389 self.assertTrue(m('Foo'))
390 self.assertTrue(m('FOO'))
391 self.assertFalse(m('bar'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000392
393 self.assertEqual(h['foo'],'bar')
394 h['foo'] = 'baz'
395 self.assertEqual(h['FOO'],'baz')
396 self.assertEqual(h.get_all('foo'),['baz'])
397
398 self.assertEqual(h.get("foo","whee"), "baz")
399 self.assertEqual(h.get("zoo","whee"), "whee")
400 self.assertEqual(h.setdefault("foo","whee"), "baz")
401 self.assertEqual(h.setdefault("zoo","whee"), "whee")
402 self.assertEqual(h["foo"],"baz")
403 self.assertEqual(h["zoo"],"whee")
404
405 def testRequireList(self):
406 self.assertRaises(TypeError, Headers, "foo")
407
408
409 def testExtras(self):
410 h = Headers([])
411 self.assertEqual(str(h),'\r\n')
412
413 h.add_header('foo','bar',baz="spam")
414 self.assertEqual(h['foo'], 'bar; baz="spam"')
415 self.assertEqual(str(h),'foo: bar; baz="spam"\r\n\r\n')
416
417 h.add_header('Foo','bar',cheese=None)
418 self.assertEqual(h.get_all('foo'),
419 ['bar; baz="spam"', 'bar; cheese'])
420
421 self.assertEqual(str(h),
422 'foo: bar; baz="spam"\r\n'
423 'Foo: bar; cheese\r\n'
424 '\r\n'
425 )
426
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000427 def testBytes(self):
428 h = Headers([
429 (b"Content-Type", b"text/plain; charset=utf-8"),
430 ])
431 self.assertEqual("text/plain; charset=utf-8", h.get("Content-Type"))
432
433 h[b"Foo"] = bytes(b"bar")
434 self.assertEqual("bar", h.get("Foo"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000435 self.assertEqual("bar", h.get(b"Foo"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000436
437 h.setdefault(b"Bar", b"foo")
438 self.assertEqual("foo", h.get("Bar"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000439 self.assertEqual("foo", h.get(b"Bar"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000440
441 h.add_header(b'content-disposition', b'attachment',
442 filename=b'bud.gif')
443 self.assertEqual('attachment; filename="bud.gif"',
444 h.get("content-disposition"))
445
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000446 del h['content-disposition']
Georg Brandlab91fde2009-08-13 08:51:18 +0000447 self.assertTrue(b'content-disposition' not in h)
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000448
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000449
450class ErrorHandler(BaseCGIHandler):
451 """Simple handler subclass for testing BaseHandler"""
452
Antoine Pitroud6fcd562009-11-03 17:21:14 +0000453 # BaseHandler records the OS environment at import time, but envvars
454 # might have been changed later by other tests, which trips up
455 # HandlerTests.testEnviron().
456 os_environ = dict(os.environ.items())
457
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000458 def __init__(self,**kw):
459 setup_testing_defaults(kw)
460 BaseCGIHandler.__init__(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000461 self, BytesIO(), BytesIO(), StringIO(), kw,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000462 multithread=True, multiprocess=True
463 )
464
465class TestHandler(ErrorHandler):
466 """Simple handler subclass for testing BaseHandler, w/error passthru"""
467
468 def handle_error(self):
469 raise # for testing, we want to see what's happening
470
471
472
473
474
475
476
477
478
479
480
481class HandlerTests(TestCase):
482
483 def checkEnvironAttrs(self, handler):
484 env = handler.environ
485 for attr in [
486 'version','multithread','multiprocess','run_once','file_wrapper'
487 ]:
488 if attr=='file_wrapper' and handler.wsgi_file_wrapper is None:
489 continue
490 self.assertEqual(getattr(handler,'wsgi_'+attr),env['wsgi.'+attr])
491
492 def checkOSEnviron(self,handler):
493 empty = {}; setup_testing_defaults(empty)
494 env = handler.environ
495 from os import environ
496 for k,v in environ.items():
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000497 if k not in empty:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000498 self.assertEqual(env[k],v)
499 for k,v in empty.items():
Georg Brandlab91fde2009-08-13 08:51:18 +0000500 self.assertTrue(k in env)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000501
502 def testEnviron(self):
503 h = TestHandler(X="Y")
504 h.setup_environ()
505 self.checkEnvironAttrs(h)
506 self.checkOSEnviron(h)
507 self.assertEqual(h.environ["X"],"Y")
508
509 def testCGIEnviron(self):
510 h = BaseCGIHandler(None,None,None,{})
511 h.setup_environ()
512 for key in 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors':
Georg Brandlab91fde2009-08-13 08:51:18 +0000513 self.assertTrue(key in h.environ)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000514
515 def testScheme(self):
516 h=TestHandler(HTTPS="on"); h.setup_environ()
517 self.assertEqual(h.environ['wsgi.url_scheme'],'https')
518 h=TestHandler(); h.setup_environ()
519 self.assertEqual(h.environ['wsgi.url_scheme'],'http')
520
521
522 def testAbstractMethods(self):
523 h = BaseHandler()
524 for name in [
525 '_flush','get_stdin','get_stderr','add_cgi_vars'
526 ]:
527 self.assertRaises(NotImplementedError, getattr(h,name))
528 self.assertRaises(NotImplementedError, h._write, "test")
529
530
531 def testContentLength(self):
532 # Demo one reason iteration is better than write()... ;)
533
534 def trivial_app1(e,s):
535 s('200 OK',[])
536 return [e['wsgi.url_scheme']]
537
538 def trivial_app2(e,s):
539 s('200 OK',[])(e['wsgi.url_scheme'])
540 return []
541
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000542 def trivial_app3(e,s):
543 s('200 OK',[])
544 return ['\u0442\u0435\u0441\u0442'.encode("utf-8")]
545
Antoine Pitrou59bf7382011-01-06 17:18:32 +0000546 def trivial_app4(e,s):
547 # Simulate a response to a HEAD request
548 s('200 OK',[('Content-Length', '12345')])
549 return []
550
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000551 h = TestHandler()
552 h.run(trivial_app1)
553 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000554 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000555 "Content-Length: 4\r\n"
556 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000557 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000558
559 h = TestHandler()
560 h.run(trivial_app2)
561 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000562 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000563 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000564 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000565
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000566 h = TestHandler()
567 h.run(trivial_app3)
568 self.assertEqual(h.stdout.getvalue(),
569 b'Status: 200 OK\r\n'
570 b'Content-Length: 8\r\n'
571 b'\r\n'
572 b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000573
Antoine Pitrou59bf7382011-01-06 17:18:32 +0000574 h = TestHandler()
575 h.run(trivial_app4)
576 self.assertEqual(h.stdout.getvalue(),
577 b'Status: 200 OK\r\n'
578 b'Content-Length: 12345\r\n'
579 b'\r\n')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000580
581 def testBasicErrorOutput(self):
582
583 def non_error_app(e,s):
584 s('200 OK',[])
585 return []
586
587 def error_app(e,s):
588 raise AssertionError("This should be caught by handler")
589
590 h = ErrorHandler()
591 h.run(non_error_app)
592 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000593 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000594 "Content-Length: 0\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000595 "\r\n").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000596 self.assertEqual(h.stderr.getvalue(),"")
597
598 h = ErrorHandler()
599 h.run(error_app)
600 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000601 ("Status: %s\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000602 "Content-Type: text/plain\r\n"
603 "Content-Length: %d\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000604 "\r\n%s" % (h.error_status,len(h.error_body),h.error_body)
605 ).encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000606
Georg Brandlab91fde2009-08-13 08:51:18 +0000607 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000608
609 def testErrorAfterOutput(self):
610 MSG = "Some output has been sent"
611 def error_app(e,s):
612 s("200 OK",[])(MSG)
613 raise AssertionError("This should be caught by handler")
614
615 h = ErrorHandler()
616 h.run(error_app)
617 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000618 ("Status: 200 OK\r\n"
619 "\r\n"+MSG).encode("iso-8859-1"))
Georg Brandlab91fde2009-08-13 08:51:18 +0000620 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000621
622
623 def testHeaderFormats(self):
624
625 def non_error_app(e,s):
626 s('200 OK',[])
627 return []
628
629 stdpat = (
630 r"HTTP/%s 200 OK\r\n"
631 r"Date: \w{3}, [ 0123]\d \w{3} \d{4} \d\d:\d\d:\d\d GMT\r\n"
632 r"%s" r"Content-Length: 0\r\n" r"\r\n"
633 )
634 shortpat = (
635 "Status: 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000636 ).encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000637
638 for ssw in "FooBar/1.0", None:
639 sw = ssw and "Server: %s\r\n" % ssw or ""
640
641 for version in "1.0", "1.1":
642 for proto in "HTTP/0.9", "HTTP/1.0", "HTTP/1.1":
643
644 h = TestHandler(SERVER_PROTOCOL=proto)
645 h.origin_server = False
646 h.http_version = version
647 h.server_software = ssw
648 h.run(non_error_app)
649 self.assertEqual(shortpat,h.stdout.getvalue())
650
651 h = TestHandler(SERVER_PROTOCOL=proto)
652 h.origin_server = True
653 h.http_version = version
654 h.server_software = ssw
655 h.run(non_error_app)
656 if proto=="HTTP/0.9":
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000657 self.assertEqual(h.stdout.getvalue(),b"")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000658 else:
Georg Brandlab91fde2009-08-13 08:51:18 +0000659 self.assertTrue(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000660 re.match((stdpat%(version,sw)).encode("iso-8859-1"),
661 h.stdout.getvalue()),
662 ((stdpat%(version,sw)).encode("iso-8859-1"),
663 h.stdout.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000664 )
665
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000666 def testBytesData(self):
667 def app(e, s):
668 s(b"200 OK", [
669 (b"Content-Type", b"text/plain; charset=utf-8"),
670 ])
671 return [b"data"]
672
673 h = TestHandler()
674 h.run(app)
675 self.assertEqual(b"Status: 200 OK\r\n"
676 b"Content-Type: text/plain; charset=utf-8\r\n"
677 b"Content-Length: 4\r\n"
678 b"\r\n"
679 b"data",
680 h.stdout.getvalue())
681
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000682# This epilogue is needed for compatibility with the Python 2.5 regrtest module
683
684def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000685 support.run_unittest(__name__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000686
687if __name__ == "__main__":
688 test_main()
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718# the above lines intentionally left blank