blob: 438d8ec8951fe2f75b2c9f56787cc09e2071834d [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
Thomas Wouters0e3f5912006-08-11 14:57:12 +000012import re, sys
13
Benjamin Petersonee8712c2008-05-20 21:35:26 +000014from test import support
Thomas Wouters0e3f5912006-08-11 14:57:12 +000015
16class MockServer(WSGIServer):
17 """Non-socket HTTP server"""
18
19 def __init__(self, server_address, RequestHandlerClass):
20 BaseServer.__init__(self, server_address, RequestHandlerClass)
21 self.server_bind()
22
23 def server_bind(self):
24 host, port = self.server_address
25 self.server_name = host
26 self.server_port = port
27 self.setup_environ()
28
29
30class MockHandler(WSGIRequestHandler):
31 """Non-socket HTTP handler"""
32 def setup(self):
33 self.connection = self.request
34 self.rfile, self.wfile = self.connection
35
36 def finish(self):
37 pass
38
39
40
41
42
43def hello_app(environ,start_response):
44 start_response("200 OK", [
45 ('Content-Type','text/plain'),
46 ('Date','Mon, 05 Jun 2006 18:49:54 GMT')
47 ])
48 return ["Hello, world!"]
49
Guido van Rossum6a10e022007-08-08 17:01:45 +000050def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
Thomas Wouters0e3f5912006-08-11 14:57:12 +000051 server = make_server("", 80, app, MockServer, MockHandler)
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000052 inp = BufferedReader(BytesIO(data))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +000053 out = BytesIO()
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000054 olderr = sys.stderr
55 err = sys.stderr = StringIO()
Thomas Wouters0e3f5912006-08-11 14:57:12 +000056
57 try:
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000058 server.finish_request((inp, out), ("127.0.0.1",8888))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000059 finally:
60 sys.stderr = olderr
61
62 return out.getvalue(), err.getvalue()
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86def compare_generic_iter(make_it,match):
87 """Utility to compare a generic 2.1/2.2+ iterator with an iterable
88
89 If running under Python 2.2+, this tests the iterator using iter()/next(),
90 as well as __getitem__. 'make_it' must be a function returning a fresh
91 iterator to be tested (since this may test the iterator twice)."""
92
93 it = make_it()
94 n = 0
95 for item in match:
96 if not it[n]==item: raise AssertionError
97 n+=1
98 try:
99 it[n]
100 except IndexError:
101 pass
102 else:
103 raise AssertionError("Too many items from __getitem__",it)
104
105 try:
106 iter, StopIteration
107 except NameError:
108 pass
109 else:
110 # Only test iter mode under 2.2+
111 it = make_it()
112 if not iter(it) is it: raise AssertionError
113 for item in match:
Georg Brandla18af4e2007-04-21 15:47:16 +0000114 if not next(it) == item: raise AssertionError
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000115 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000116 next(it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000117 except StopIteration:
118 pass
119 else:
Georg Brandla18af4e2007-04-21 15:47:16 +0000120 raise AssertionError("Too many items from .__next__()", it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000121
122
123
124
125
126
127class IntegrationTests(TestCase):
128
129 def check_hello(self, out, has_length=True):
130 self.assertEqual(out,
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000131 ("HTTP/1.0 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000132 "Server: WSGIServer/0.1 Python/"+sys.version.split()[0]+"\r\n"
133 "Content-Type: text/plain\r\n"
134 "Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" +
135 (has_length and "Content-Length: 13\r\n" or "") +
136 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000137 "Hello, world!").encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000138 )
139
140 def test_plain_hello(self):
141 out, err = run_amock()
142 self.check_hello(out)
143
144 def test_validated_hello(self):
145 out, err = run_amock(validator(hello_app))
146 # the middleware doesn't support len(), so content-length isn't there
147 self.check_hello(out, has_length=False)
148
149 def test_simple_validation_error(self):
150 def bad_app(environ,start_response):
151 start_response("200 OK", ('Content-Type','text/plain'))
152 return ["Hello, world!"]
153 out, err = run_amock(validator(bad_app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000154 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000155 b"A server error occurred. Please contact the administrator."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000156 ))
157 self.assertEqual(
158 err.splitlines()[-2],
159 "AssertionError: Headers (('Content-Type', 'text/plain')) must"
Martin v. Löwis250ad612008-04-07 05:43:42 +0000160 " be of type list: <class 'tuple'>"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000161 )
162
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000163 def test_wsgi_input(self):
164 def bad_app(e,s):
165 e["wsgi.input"].read()
166 s(b"200 OK", [(b"Content-Type", b"text/plain; charset=utf-8")])
167 return [b"data"]
168 out, err = run_amock(validator(bad_app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000169 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000170 b"A server error occurred. Please contact the administrator."
171 ))
172 self.assertEqual(
173 err.splitlines()[-2], "AssertionError"
174 )
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000175
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000176 def test_bytes_validation(self):
177 def app(e, s):
178 s(b"200 OK", [
179 (b"Content-Type", b"text/plain; charset=utf-8"),
180 ("Date", "Wed, 24 Dec 2008 13:29:32 GMT"),
181 ])
182 return [b"data"]
183 out, err = run_amock(validator(app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000184 self.assertTrue(err.endswith('"GET / HTTP/1.0" 200 4\n'))
Antoine Pitrou5f817412009-01-03 18:49:41 +0000185 ver = sys.version.split()[0].encode('ascii')
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000186 self.assertEqual(
187 b"HTTP/1.0 200 OK\r\n"
Antoine Pitrou5f817412009-01-03 18:49:41 +0000188 b"Server: WSGIServer/0.1 Python/" + ver + b"\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000189 b"Content-Type: text/plain; charset=utf-8\r\n"
190 b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n"
191 b"\r\n"
192 b"data",
193 out)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000194
195
196
197
198class UtilityTests(TestCase):
199
200 def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
201 env = {'SCRIPT_NAME':sn_in,'PATH_INFO':pi_in}
202 util.setup_testing_defaults(env)
203 self.assertEqual(util.shift_path_info(env),part)
204 self.assertEqual(env['PATH_INFO'],pi_out)
205 self.assertEqual(env['SCRIPT_NAME'],sn_out)
206 return env
207
208 def checkDefault(self, key, value, alt=None):
209 # Check defaulting when empty
210 env = {}
211 util.setup_testing_defaults(env)
212 if isinstance(value,StringIO):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000213 self.assertTrue(isinstance(env[key],StringIO))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000214 elif isinstance(value,BytesIO):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000215 self.assertTrue(isinstance(env[key],BytesIO))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000216 else:
217 self.assertEqual(env[key],value)
218
219 # Check existing value
220 env = {key:alt}
221 util.setup_testing_defaults(env)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000222 self.assertTrue(env[key] is alt)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000223
224 def checkCrossDefault(self,key,value,**kw):
225 util.setup_testing_defaults(kw)
226 self.assertEqual(kw[key],value)
227
228 def checkAppURI(self,uri,**kw):
229 util.setup_testing_defaults(kw)
230 self.assertEqual(util.application_uri(kw),uri)
231
232 def checkReqURI(self,uri,query=1,**kw):
233 util.setup_testing_defaults(kw)
234 self.assertEqual(util.request_uri(kw,query),uri)
235
236
237
238
239
240
241 def checkFW(self,text,size,match):
242
243 def make_it(text=text,size=size):
244 return util.FileWrapper(StringIO(text),size)
245
246 compare_generic_iter(make_it,match)
247
248 it = make_it()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000249 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000250
251 for item in it:
252 pass
253
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000254 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000255
256 it.close()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000257 self.assertTrue(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000258
259
260 def testSimpleShifts(self):
261 self.checkShift('','/', '', '/', '')
262 self.checkShift('','/x', 'x', '/x', '')
263 self.checkShift('/','', None, '/', '')
264 self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
265 self.checkShift('/a','/x/', 'x', '/a/x', '/')
266
267
268 def testNormalizedShifts(self):
269 self.checkShift('/a/b', '/../y', '..', '/a', '/y')
270 self.checkShift('', '/../y', '..', '', '/y')
271 self.checkShift('/a/b', '//y', 'y', '/a/b/y', '')
272 self.checkShift('/a/b', '//y/', 'y', '/a/b/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/.//', '..', '/a', '/y/')
276 self.checkShift('/a/b', '///', '', '/a/b/', '')
277 self.checkShift('/a/b', '/.//', '', '/a/b/', '')
278 self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
279 self.checkShift('/a/b', '/.', None, '/a/b', '')
280
281
282 def testDefaults(self):
283 for key, value in [
284 ('SERVER_NAME','127.0.0.1'),
285 ('SERVER_PORT', '80'),
286 ('SERVER_PROTOCOL','HTTP/1.0'),
287 ('HTTP_HOST','127.0.0.1'),
288 ('REQUEST_METHOD','GET'),
289 ('SCRIPT_NAME',''),
290 ('PATH_INFO','/'),
291 ('wsgi.version', (1,0)),
292 ('wsgi.run_once', 0),
293 ('wsgi.multithread', 0),
294 ('wsgi.multiprocess', 0),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000295 ('wsgi.input', BytesIO()),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000296 ('wsgi.errors', StringIO()),
297 ('wsgi.url_scheme','http'),
298 ]:
299 self.checkDefault(key,value)
300
301
302 def testCrossDefaults(self):
303 self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
304 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
305 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="1")
306 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="yes")
307 self.checkCrossDefault('wsgi.url_scheme',"http",HTTPS="foo")
308 self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
309 self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
310
311
312 def testGuessScheme(self):
313 self.assertEqual(util.guess_scheme({}), "http")
314 self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
315 self.assertEqual(util.guess_scheme({'HTTPS':"on"}), "https")
316 self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
317 self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
318
319
320
321
322
323 def testAppURIs(self):
324 self.checkAppURI("http://127.0.0.1/")
325 self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000326 self.checkAppURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000327 self.checkAppURI("http://spam.example.com:2071/",
328 HTTP_HOST="spam.example.com:2071", SERVER_PORT="2071")
329 self.checkAppURI("http://spam.example.com/",
330 SERVER_NAME="spam.example.com")
331 self.checkAppURI("http://127.0.0.1/",
332 HTTP_HOST="127.0.0.1", SERVER_NAME="spam.example.com")
333 self.checkAppURI("https://127.0.0.1/", HTTPS="on")
334 self.checkAppURI("http://127.0.0.1:8000/", SERVER_PORT="8000",
335 HTTP_HOST=None)
336
337 def testReqURIs(self):
338 self.checkReqURI("http://127.0.0.1/")
339 self.checkReqURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000340 self.checkReqURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000341 self.checkReqURI("http://127.0.0.1/spammity/spam",
342 SCRIPT_NAME="/spammity", PATH_INFO="/spam")
343 self.checkReqURI("http://127.0.0.1/spammity/spam?say=ni",
344 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
345 self.checkReqURI("http://127.0.0.1/spammity/spam", 0,
346 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
347
348 def testFileWrapper(self):
349 self.checkFW("xyz"*50, 120, ["xyz"*40,"xyz"*10])
350
351 def testHopByHop(self):
352 for hop in (
353 "Connection Keep-Alive Proxy-Authenticate Proxy-Authorization "
354 "TE Trailers Transfer-Encoding Upgrade"
355 ).split():
356 for alt in hop, hop.title(), hop.upper(), hop.lower():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000357 self.assertTrue(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000358
359 # Not comprehensive, just a few random header names
360 for hop in (
361 "Accept Cache-Control Date Pragma Trailer Via Warning"
362 ).split():
363 for alt in hop, hop.title(), hop.upper(), hop.lower():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000364 self.assertFalse(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000365
366class HeaderTests(TestCase):
367
368 def testMappingInterface(self):
369 test = [('x','y')]
370 self.assertEqual(len(Headers([])),0)
371 self.assertEqual(len(Headers(test[:])),1)
372 self.assertEqual(Headers(test[:]).keys(), ['x'])
373 self.assertEqual(Headers(test[:]).values(), ['y'])
374 self.assertEqual(Headers(test[:]).items(), test)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000375 self.assertFalse(Headers(test).items() is test) # must be copy!
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000376
377 h=Headers([])
378 del h['foo'] # should not raise an error
379
380 h['Foo'] = 'bar'
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000381 for m in h.__contains__, h.get, h.get_all, h.__getitem__:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000382 self.assertTrue(m('foo'))
383 self.assertTrue(m('Foo'))
384 self.assertTrue(m('FOO'))
385 self.assertFalse(m('bar'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000386
387 self.assertEqual(h['foo'],'bar')
388 h['foo'] = 'baz'
389 self.assertEqual(h['FOO'],'baz')
390 self.assertEqual(h.get_all('foo'),['baz'])
391
392 self.assertEqual(h.get("foo","whee"), "baz")
393 self.assertEqual(h.get("zoo","whee"), "whee")
394 self.assertEqual(h.setdefault("foo","whee"), "baz")
395 self.assertEqual(h.setdefault("zoo","whee"), "whee")
396 self.assertEqual(h["foo"],"baz")
397 self.assertEqual(h["zoo"],"whee")
398
399 def testRequireList(self):
400 self.assertRaises(TypeError, Headers, "foo")
401
402
403 def testExtras(self):
404 h = Headers([])
405 self.assertEqual(str(h),'\r\n')
406
407 h.add_header('foo','bar',baz="spam")
408 self.assertEqual(h['foo'], 'bar; baz="spam"')
409 self.assertEqual(str(h),'foo: bar; baz="spam"\r\n\r\n')
410
411 h.add_header('Foo','bar',cheese=None)
412 self.assertEqual(h.get_all('foo'),
413 ['bar; baz="spam"', 'bar; cheese'])
414
415 self.assertEqual(str(h),
416 'foo: bar; baz="spam"\r\n'
417 'Foo: bar; cheese\r\n'
418 '\r\n'
419 )
420
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000421 def testBytes(self):
422 h = Headers([
423 (b"Content-Type", b"text/plain; charset=utf-8"),
424 ])
425 self.assertEqual("text/plain; charset=utf-8", h.get("Content-Type"))
426
427 h[b"Foo"] = bytes(b"bar")
428 self.assertEqual("bar", h.get("Foo"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000429 self.assertEqual("bar", h.get(b"Foo"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000430
431 h.setdefault(b"Bar", b"foo")
432 self.assertEqual("foo", h.get("Bar"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000433 self.assertEqual("foo", h.get(b"Bar"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000434
435 h.add_header(b'content-disposition', b'attachment',
436 filename=b'bud.gif')
437 self.assertEqual('attachment; filename="bud.gif"',
438 h.get("content-disposition"))
439
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000440 del h['content-disposition']
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000441 self.assertTrue(b'content-disposition' not in h)
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000442
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000443
444class ErrorHandler(BaseCGIHandler):
445 """Simple handler subclass for testing BaseHandler"""
446
447 def __init__(self,**kw):
448 setup_testing_defaults(kw)
449 BaseCGIHandler.__init__(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000450 self, BytesIO(), BytesIO(), StringIO(), kw,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000451 multithread=True, multiprocess=True
452 )
453
454class TestHandler(ErrorHandler):
455 """Simple handler subclass for testing BaseHandler, w/error passthru"""
456
457 def handle_error(self):
458 raise # for testing, we want to see what's happening
459
460
461
462
463
464
465
466
467
468
469
470class HandlerTests(TestCase):
471
472 def checkEnvironAttrs(self, handler):
473 env = handler.environ
474 for attr in [
475 'version','multithread','multiprocess','run_once','file_wrapper'
476 ]:
477 if attr=='file_wrapper' and handler.wsgi_file_wrapper is None:
478 continue
479 self.assertEqual(getattr(handler,'wsgi_'+attr),env['wsgi.'+attr])
480
481 def checkOSEnviron(self,handler):
482 empty = {}; setup_testing_defaults(empty)
483 env = handler.environ
484 from os import environ
485 for k,v in environ.items():
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000486 if k not in empty:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000487 self.assertEqual(env[k],v)
488 for k,v in empty.items():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000489 self.assertTrue(k in env)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000490
491 def testEnviron(self):
492 h = TestHandler(X="Y")
493 h.setup_environ()
494 self.checkEnvironAttrs(h)
495 self.checkOSEnviron(h)
496 self.assertEqual(h.environ["X"],"Y")
497
498 def testCGIEnviron(self):
499 h = BaseCGIHandler(None,None,None,{})
500 h.setup_environ()
501 for key in 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors':
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000502 self.assertTrue(key in h.environ)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000503
504 def testScheme(self):
505 h=TestHandler(HTTPS="on"); h.setup_environ()
506 self.assertEqual(h.environ['wsgi.url_scheme'],'https')
507 h=TestHandler(); h.setup_environ()
508 self.assertEqual(h.environ['wsgi.url_scheme'],'http')
509
510
511 def testAbstractMethods(self):
512 h = BaseHandler()
513 for name in [
514 '_flush','get_stdin','get_stderr','add_cgi_vars'
515 ]:
516 self.assertRaises(NotImplementedError, getattr(h,name))
517 self.assertRaises(NotImplementedError, h._write, "test")
518
519
520 def testContentLength(self):
521 # Demo one reason iteration is better than write()... ;)
522
523 def trivial_app1(e,s):
524 s('200 OK',[])
525 return [e['wsgi.url_scheme']]
526
527 def trivial_app2(e,s):
528 s('200 OK',[])(e['wsgi.url_scheme'])
529 return []
530
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000531 def trivial_app3(e,s):
532 s('200 OK',[])
533 return ['\u0442\u0435\u0441\u0442'.encode("utf-8")]
534
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000535 h = TestHandler()
536 h.run(trivial_app1)
537 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000538 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000539 "Content-Length: 4\r\n"
540 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000541 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000542
543 h = TestHandler()
544 h.run(trivial_app2)
545 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000546 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000547 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000548 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000549
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000550 h = TestHandler()
551 h.run(trivial_app3)
552 self.assertEqual(h.stdout.getvalue(),
553 b'Status: 200 OK\r\n'
554 b'Content-Length: 8\r\n'
555 b'\r\n'
556 b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000557
558
559
560
561
562
563 def testBasicErrorOutput(self):
564
565 def non_error_app(e,s):
566 s('200 OK',[])
567 return []
568
569 def error_app(e,s):
570 raise AssertionError("This should be caught by handler")
571
572 h = ErrorHandler()
573 h.run(non_error_app)
574 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000575 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000576 "Content-Length: 0\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000577 "\r\n").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000578 self.assertEqual(h.stderr.getvalue(),"")
579
580 h = ErrorHandler()
581 h.run(error_app)
582 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000583 ("Status: %s\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000584 "Content-Type: text/plain\r\n"
585 "Content-Length: %d\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000586 "\r\n%s" % (h.error_status,len(h.error_body),h.error_body)
587 ).encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000588
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000589 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000590
591 def testErrorAfterOutput(self):
592 MSG = "Some output has been sent"
593 def error_app(e,s):
594 s("200 OK",[])(MSG)
595 raise AssertionError("This should be caught by handler")
596
597 h = ErrorHandler()
598 h.run(error_app)
599 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000600 ("Status: 200 OK\r\n"
601 "\r\n"+MSG).encode("iso-8859-1"))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000602 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000603
604
605 def testHeaderFormats(self):
606
607 def non_error_app(e,s):
608 s('200 OK',[])
609 return []
610
611 stdpat = (
612 r"HTTP/%s 200 OK\r\n"
613 r"Date: \w{3}, [ 0123]\d \w{3} \d{4} \d\d:\d\d:\d\d GMT\r\n"
614 r"%s" r"Content-Length: 0\r\n" r"\r\n"
615 )
616 shortpat = (
617 "Status: 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000618 ).encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000619
620 for ssw in "FooBar/1.0", None:
621 sw = ssw and "Server: %s\r\n" % ssw or ""
622
623 for version in "1.0", "1.1":
624 for proto in "HTTP/0.9", "HTTP/1.0", "HTTP/1.1":
625
626 h = TestHandler(SERVER_PROTOCOL=proto)
627 h.origin_server = False
628 h.http_version = version
629 h.server_software = ssw
630 h.run(non_error_app)
631 self.assertEqual(shortpat,h.stdout.getvalue())
632
633 h = TestHandler(SERVER_PROTOCOL=proto)
634 h.origin_server = True
635 h.http_version = version
636 h.server_software = ssw
637 h.run(non_error_app)
638 if proto=="HTTP/0.9":
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000639 self.assertEqual(h.stdout.getvalue(),b"")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000640 else:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000641 self.assertTrue(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000642 re.match((stdpat%(version,sw)).encode("iso-8859-1"),
643 h.stdout.getvalue()),
644 ((stdpat%(version,sw)).encode("iso-8859-1"),
645 h.stdout.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000646 )
647
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000648 def testBytesData(self):
649 def app(e, s):
650 s(b"200 OK", [
651 (b"Content-Type", b"text/plain; charset=utf-8"),
652 ])
653 return [b"data"]
654
655 h = TestHandler()
656 h.run(app)
657 self.assertEqual(b"Status: 200 OK\r\n"
658 b"Content-Type: text/plain; charset=utf-8\r\n"
659 b"Content-Length: 4\r\n"
660 b"\r\n"
661 b"data",
662 h.stdout.getvalue())
663
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000664# This epilogue is needed for compatibility with the Python 2.5 regrtest module
665
666def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000667 support.run_unittest(__name__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000668
669if __name__ == "__main__":
670 test_main()
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
696
697
698
699
700# the above lines intentionally left blank