blob: a08f66b7b476b36d07de2cc68a14f9095dbae882 [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 Pitrou8f5731b2009-11-03 17:20:10 +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 ])
Phillip J. Ebye1594222010-11-02 22:28:59 +000050 return [b"Hello, world!"]
Thomas Wouters0e3f5912006-08-11 14:57:12 +000051
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"
Phillip J. Ebyb6d4a8e2010-11-03 22:39:01 +0000134 "Server: WSGIServer/0.2 Python/"+sys.version.split()[0]+"\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000135 "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))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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()
Phillip J. Ebye1594222010-11-02 22:28:59 +0000168 s("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000169 return [b"data"]
170 out, err = run_amock(validator(bad_app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000180 s("200 OK", [
181 ("Content-Type", "text/plain; charset=utf-8"),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000182 ("Date", "Wed, 24 Dec 2008 13:29:32 GMT"),
183 ])
184 return [b"data"]
185 out, err = run_amock(validator(app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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"
Phillip J. Ebyb6d4a8e2010-11-03 22:39:01 +0000190 b"Server: WSGIServer/0.2 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)
Ezio Melottie9615932010-01-24 19:26:24 +0000214 if isinstance(value, StringIO):
215 self.assertIsInstance(env[key], StringIO)
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000216 elif isinstance(value,BytesIO):
Ezio Melottie9615932010-01-24 19:26:24 +0000217 self.assertIsInstance(env[key],BytesIO)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000218 else:
Ezio Melottie9615932010-01-24 19:26:24 +0000219 self.assertEqual(env[key], value)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000220
221 # Check existing value
222 env = {key:alt}
223 util.setup_testing_defaults(env)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000251 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000252
253 for item in it:
254 pass
255
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000256 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000257
258 it.close()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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 Kumaran299fa4c2010-12-29 06:25:42 +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():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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__:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +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
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000427class ErrorHandler(BaseCGIHandler):
428 """Simple handler subclass for testing BaseHandler"""
429
Antoine Pitrou8f5731b2009-11-03 17:20:10 +0000430 # BaseHandler records the OS environment at import time, but envvars
431 # might have been changed later by other tests, which trips up
432 # HandlerTests.testEnviron().
433 os_environ = dict(os.environ.items())
434
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000435 def __init__(self,**kw):
436 setup_testing_defaults(kw)
437 BaseCGIHandler.__init__(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000438 self, BytesIO(), BytesIO(), StringIO(), kw,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000439 multithread=True, multiprocess=True
440 )
441
442class TestHandler(ErrorHandler):
443 """Simple handler subclass for testing BaseHandler, w/error passthru"""
444
445 def handle_error(self):
446 raise # for testing, we want to see what's happening
447
448
449
450
451
452
453
454
455
456
457
458class HandlerTests(TestCase):
459
460 def checkEnvironAttrs(self, handler):
461 env = handler.environ
462 for attr in [
463 'version','multithread','multiprocess','run_once','file_wrapper'
464 ]:
465 if attr=='file_wrapper' and handler.wsgi_file_wrapper is None:
466 continue
467 self.assertEqual(getattr(handler,'wsgi_'+attr),env['wsgi.'+attr])
468
469 def checkOSEnviron(self,handler):
470 empty = {}; setup_testing_defaults(empty)
471 env = handler.environ
472 from os import environ
473 for k,v in environ.items():
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000474 if k not in empty:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000475 self.assertEqual(env[k],v)
476 for k,v in empty.items():
Benjamin Peterson577473f2010-01-19 00:09:57 +0000477 self.assertIn(k, env)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000478
479 def testEnviron(self):
480 h = TestHandler(X="Y")
481 h.setup_environ()
482 self.checkEnvironAttrs(h)
483 self.checkOSEnviron(h)
484 self.assertEqual(h.environ["X"],"Y")
485
486 def testCGIEnviron(self):
487 h = BaseCGIHandler(None,None,None,{})
488 h.setup_environ()
489 for key in 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors':
Benjamin Peterson577473f2010-01-19 00:09:57 +0000490 self.assertIn(key, h.environ)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000491
492 def testScheme(self):
493 h=TestHandler(HTTPS="on"); h.setup_environ()
494 self.assertEqual(h.environ['wsgi.url_scheme'],'https')
495 h=TestHandler(); h.setup_environ()
496 self.assertEqual(h.environ['wsgi.url_scheme'],'http')
497
498
499 def testAbstractMethods(self):
500 h = BaseHandler()
501 for name in [
502 '_flush','get_stdin','get_stderr','add_cgi_vars'
503 ]:
504 self.assertRaises(NotImplementedError, getattr(h,name))
505 self.assertRaises(NotImplementedError, h._write, "test")
506
507
508 def testContentLength(self):
509 # Demo one reason iteration is better than write()... ;)
510
511 def trivial_app1(e,s):
512 s('200 OK',[])
Phillip J. Ebye1594222010-11-02 22:28:59 +0000513 return [e['wsgi.url_scheme'].encode('iso-8859-1')]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000514
515 def trivial_app2(e,s):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000516 s('200 OK',[])(e['wsgi.url_scheme'].encode('iso-8859-1'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000517 return []
518
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000519 def trivial_app3(e,s):
520 s('200 OK',[])
521 return ['\u0442\u0435\u0441\u0442'.encode("utf-8")]
522
Antoine Pitroub715fac2011-01-06 17:17:04 +0000523 def trivial_app4(e,s):
524 # Simulate a response to a HEAD request
525 s('200 OK',[('Content-Length', '12345')])
526 return []
527
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000528 h = TestHandler()
529 h.run(trivial_app1)
530 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000531 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000532 "Content-Length: 4\r\n"
533 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000534 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000535
536 h = TestHandler()
537 h.run(trivial_app2)
538 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000539 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000540 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000541 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000542
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000543 h = TestHandler()
544 h.run(trivial_app3)
545 self.assertEqual(h.stdout.getvalue(),
546 b'Status: 200 OK\r\n'
547 b'Content-Length: 8\r\n'
548 b'\r\n'
549 b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000550
Antoine Pitroub715fac2011-01-06 17:17:04 +0000551 h = TestHandler()
552 h.run(trivial_app4)
553 self.assertEqual(h.stdout.getvalue(),
554 b'Status: 200 OK\r\n'
555 b'Content-Length: 12345\r\n'
556 b'\r\n')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000557
558 def testBasicErrorOutput(self):
559
560 def non_error_app(e,s):
561 s('200 OK',[])
562 return []
563
564 def error_app(e,s):
565 raise AssertionError("This should be caught by handler")
566
567 h = ErrorHandler()
568 h.run(non_error_app)
569 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000570 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000571 "Content-Length: 0\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000572 "\r\n").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000573 self.assertEqual(h.stderr.getvalue(),"")
574
575 h = ErrorHandler()
576 h.run(error_app)
577 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000578 ("Status: %s\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000579 "Content-Type: text/plain\r\n"
580 "Content-Length: %d\r\n"
Phillip J. Ebye1594222010-11-02 22:28:59 +0000581 "\r\n" % (h.error_status,len(h.error_body))).encode('iso-8859-1')
582 + h.error_body)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000583
Benjamin Peterson577473f2010-01-19 00:09:57 +0000584 self.assertIn("AssertionError", h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000585
586 def testErrorAfterOutput(self):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000587 MSG = b"Some output has been sent"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000588 def error_app(e,s):
589 s("200 OK",[])(MSG)
590 raise AssertionError("This should be caught by handler")
591
592 h = ErrorHandler()
593 h.run(error_app)
594 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000595 ("Status: 200 OK\r\n"
Phillip J. Ebye1594222010-11-02 22:28:59 +0000596 "\r\n".encode("iso-8859-1")+MSG))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000597 self.assertIn("AssertionError", h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000598
599
600 def testHeaderFormats(self):
601
602 def non_error_app(e,s):
603 s('200 OK',[])
604 return []
605
606 stdpat = (
607 r"HTTP/%s 200 OK\r\n"
608 r"Date: \w{3}, [ 0123]\d \w{3} \d{4} \d\d:\d\d:\d\d GMT\r\n"
609 r"%s" r"Content-Length: 0\r\n" r"\r\n"
610 )
611 shortpat = (
612 "Status: 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000613 ).encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000614
615 for ssw in "FooBar/1.0", None:
616 sw = ssw and "Server: %s\r\n" % ssw or ""
617
618 for version in "1.0", "1.1":
619 for proto in "HTTP/0.9", "HTTP/1.0", "HTTP/1.1":
620
621 h = TestHandler(SERVER_PROTOCOL=proto)
622 h.origin_server = False
623 h.http_version = version
624 h.server_software = ssw
625 h.run(non_error_app)
626 self.assertEqual(shortpat,h.stdout.getvalue())
627
628 h = TestHandler(SERVER_PROTOCOL=proto)
629 h.origin_server = True
630 h.http_version = version
631 h.server_software = ssw
632 h.run(non_error_app)
633 if proto=="HTTP/0.9":
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000634 self.assertEqual(h.stdout.getvalue(),b"")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000635 else:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000636 self.assertTrue(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000637 re.match((stdpat%(version,sw)).encode("iso-8859-1"),
638 h.stdout.getvalue()),
639 ((stdpat%(version,sw)).encode("iso-8859-1"),
640 h.stdout.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000641 )
642
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000643 def testBytesData(self):
644 def app(e, s):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000645 s("200 OK", [
646 ("Content-Type", "text/plain; charset=utf-8"),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000647 ])
648 return [b"data"]
649
650 h = TestHandler()
651 h.run(app)
652 self.assertEqual(b"Status: 200 OK\r\n"
653 b"Content-Type: text/plain; charset=utf-8\r\n"
654 b"Content-Length: 4\r\n"
655 b"\r\n"
656 b"data",
657 h.stdout.getvalue())
658
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000659# This epilogue is needed for compatibility with the Python 2.5 regrtest module
660
661def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000662 support.run_unittest(__name__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000663
664if __name__ == "__main__":
665 test_main()
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695# the above lines intentionally left blank