blob: 0b1a6373824fee0f3e5dc8b57439ce324169b2a5 [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")
345 self.checkReqURI("http://127.0.0.1/spammity/spam?say=ni",
346 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
347 self.checkReqURI("http://127.0.0.1/spammity/spam", 0,
348 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
349
350 def testFileWrapper(self):
351 self.checkFW("xyz"*50, 120, ["xyz"*40,"xyz"*10])
352
353 def testHopByHop(self):
354 for hop in (
355 "Connection Keep-Alive Proxy-Authenticate Proxy-Authorization "
356 "TE Trailers Transfer-Encoding Upgrade"
357 ).split():
358 for alt in hop, hop.title(), hop.upper(), hop.lower():
Georg Brandlab91fde2009-08-13 08:51:18 +0000359 self.assertTrue(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000360
361 # Not comprehensive, just a few random header names
362 for hop in (
363 "Accept Cache-Control Date Pragma Trailer Via Warning"
364 ).split():
365 for alt in hop, hop.title(), hop.upper(), hop.lower():
Georg Brandlab91fde2009-08-13 08:51:18 +0000366 self.assertFalse(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000367
368class HeaderTests(TestCase):
369
370 def testMappingInterface(self):
371 test = [('x','y')]
372 self.assertEqual(len(Headers([])),0)
373 self.assertEqual(len(Headers(test[:])),1)
374 self.assertEqual(Headers(test[:]).keys(), ['x'])
375 self.assertEqual(Headers(test[:]).values(), ['y'])
376 self.assertEqual(Headers(test[:]).items(), test)
Georg Brandlab91fde2009-08-13 08:51:18 +0000377 self.assertFalse(Headers(test).items() is test) # must be copy!
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000378
379 h=Headers([])
380 del h['foo'] # should not raise an error
381
382 h['Foo'] = 'bar'
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000383 for m in h.__contains__, h.get, h.get_all, h.__getitem__:
Georg Brandlab91fde2009-08-13 08:51:18 +0000384 self.assertTrue(m('foo'))
385 self.assertTrue(m('Foo'))
386 self.assertTrue(m('FOO'))
387 self.assertFalse(m('bar'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000388
389 self.assertEqual(h['foo'],'bar')
390 h['foo'] = 'baz'
391 self.assertEqual(h['FOO'],'baz')
392 self.assertEqual(h.get_all('foo'),['baz'])
393
394 self.assertEqual(h.get("foo","whee"), "baz")
395 self.assertEqual(h.get("zoo","whee"), "whee")
396 self.assertEqual(h.setdefault("foo","whee"), "baz")
397 self.assertEqual(h.setdefault("zoo","whee"), "whee")
398 self.assertEqual(h["foo"],"baz")
399 self.assertEqual(h["zoo"],"whee")
400
401 def testRequireList(self):
402 self.assertRaises(TypeError, Headers, "foo")
403
404
405 def testExtras(self):
406 h = Headers([])
407 self.assertEqual(str(h),'\r\n')
408
409 h.add_header('foo','bar',baz="spam")
410 self.assertEqual(h['foo'], 'bar; baz="spam"')
411 self.assertEqual(str(h),'foo: bar; baz="spam"\r\n\r\n')
412
413 h.add_header('Foo','bar',cheese=None)
414 self.assertEqual(h.get_all('foo'),
415 ['bar; baz="spam"', 'bar; cheese'])
416
417 self.assertEqual(str(h),
418 'foo: bar; baz="spam"\r\n'
419 'Foo: bar; cheese\r\n'
420 '\r\n'
421 )
422
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000423 def testBytes(self):
424 h = Headers([
425 (b"Content-Type", b"text/plain; charset=utf-8"),
426 ])
427 self.assertEqual("text/plain; charset=utf-8", h.get("Content-Type"))
428
429 h[b"Foo"] = bytes(b"bar")
430 self.assertEqual("bar", h.get("Foo"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000431 self.assertEqual("bar", h.get(b"Foo"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000432
433 h.setdefault(b"Bar", b"foo")
434 self.assertEqual("foo", h.get("Bar"))
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000435 self.assertEqual("foo", h.get(b"Bar"))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000436
437 h.add_header(b'content-disposition', b'attachment',
438 filename=b'bud.gif')
439 self.assertEqual('attachment; filename="bud.gif"',
440 h.get("content-disposition"))
441
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000442 del h['content-disposition']
Georg Brandlab91fde2009-08-13 08:51:18 +0000443 self.assertTrue(b'content-disposition' not in h)
Antoine Pitroua9ecbda2009-01-03 20:28:05 +0000444
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000445
446class ErrorHandler(BaseCGIHandler):
447 """Simple handler subclass for testing BaseHandler"""
448
Antoine Pitroud6fcd562009-11-03 17:21:14 +0000449 # BaseHandler records the OS environment at import time, but envvars
450 # might have been changed later by other tests, which trips up
451 # HandlerTests.testEnviron().
452 os_environ = dict(os.environ.items())
453
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000454 def __init__(self,**kw):
455 setup_testing_defaults(kw)
456 BaseCGIHandler.__init__(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000457 self, BytesIO(), BytesIO(), StringIO(), kw,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000458 multithread=True, multiprocess=True
459 )
460
461class TestHandler(ErrorHandler):
462 """Simple handler subclass for testing BaseHandler, w/error passthru"""
463
464 def handle_error(self):
465 raise # for testing, we want to see what's happening
466
467
468
469
470
471
472
473
474
475
476
477class HandlerTests(TestCase):
478
479 def checkEnvironAttrs(self, handler):
480 env = handler.environ
481 for attr in [
482 'version','multithread','multiprocess','run_once','file_wrapper'
483 ]:
484 if attr=='file_wrapper' and handler.wsgi_file_wrapper is None:
485 continue
486 self.assertEqual(getattr(handler,'wsgi_'+attr),env['wsgi.'+attr])
487
488 def checkOSEnviron(self,handler):
489 empty = {}; setup_testing_defaults(empty)
490 env = handler.environ
491 from os import environ
492 for k,v in environ.items():
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000493 if k not in empty:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000494 self.assertEqual(env[k],v)
495 for k,v in empty.items():
Georg Brandlab91fde2009-08-13 08:51:18 +0000496 self.assertTrue(k in env)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000497
498 def testEnviron(self):
499 h = TestHandler(X="Y")
500 h.setup_environ()
501 self.checkEnvironAttrs(h)
502 self.checkOSEnviron(h)
503 self.assertEqual(h.environ["X"],"Y")
504
505 def testCGIEnviron(self):
506 h = BaseCGIHandler(None,None,None,{})
507 h.setup_environ()
508 for key in 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors':
Georg Brandlab91fde2009-08-13 08:51:18 +0000509 self.assertTrue(key in h.environ)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000510
511 def testScheme(self):
512 h=TestHandler(HTTPS="on"); h.setup_environ()
513 self.assertEqual(h.environ['wsgi.url_scheme'],'https')
514 h=TestHandler(); h.setup_environ()
515 self.assertEqual(h.environ['wsgi.url_scheme'],'http')
516
517
518 def testAbstractMethods(self):
519 h = BaseHandler()
520 for name in [
521 '_flush','get_stdin','get_stderr','add_cgi_vars'
522 ]:
523 self.assertRaises(NotImplementedError, getattr(h,name))
524 self.assertRaises(NotImplementedError, h._write, "test")
525
526
527 def testContentLength(self):
528 # Demo one reason iteration is better than write()... ;)
529
530 def trivial_app1(e,s):
531 s('200 OK',[])
532 return [e['wsgi.url_scheme']]
533
534 def trivial_app2(e,s):
535 s('200 OK',[])(e['wsgi.url_scheme'])
536 return []
537
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000538 def trivial_app3(e,s):
539 s('200 OK',[])
540 return ['\u0442\u0435\u0441\u0442'.encode("utf-8")]
541
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000542 h = TestHandler()
543 h.run(trivial_app1)
544 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000545 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000546 "Content-Length: 4\r\n"
547 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000548 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000549
550 h = TestHandler()
551 h.run(trivial_app2)
552 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000553 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000554 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000555 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000556
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000557 h = TestHandler()
558 h.run(trivial_app3)
559 self.assertEqual(h.stdout.getvalue(),
560 b'Status: 200 OK\r\n'
561 b'Content-Length: 8\r\n'
562 b'\r\n'
563 b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000564
565
566
567
568
569
570 def testBasicErrorOutput(self):
571
572 def non_error_app(e,s):
573 s('200 OK',[])
574 return []
575
576 def error_app(e,s):
577 raise AssertionError("This should be caught by handler")
578
579 h = ErrorHandler()
580 h.run(non_error_app)
581 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000582 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000583 "Content-Length: 0\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000584 "\r\n").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000585 self.assertEqual(h.stderr.getvalue(),"")
586
587 h = ErrorHandler()
588 h.run(error_app)
589 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000590 ("Status: %s\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000591 "Content-Type: text/plain\r\n"
592 "Content-Length: %d\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000593 "\r\n%s" % (h.error_status,len(h.error_body),h.error_body)
594 ).encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000595
Georg Brandlab91fde2009-08-13 08:51:18 +0000596 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000597
598 def testErrorAfterOutput(self):
599 MSG = "Some output has been sent"
600 def error_app(e,s):
601 s("200 OK",[])(MSG)
602 raise AssertionError("This should be caught by handler")
603
604 h = ErrorHandler()
605 h.run(error_app)
606 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000607 ("Status: 200 OK\r\n"
608 "\r\n"+MSG).encode("iso-8859-1"))
Georg Brandlab91fde2009-08-13 08:51:18 +0000609 self.assertTrue("AssertionError" in h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000610
611
612 def testHeaderFormats(self):
613
614 def non_error_app(e,s):
615 s('200 OK',[])
616 return []
617
618 stdpat = (
619 r"HTTP/%s 200 OK\r\n"
620 r"Date: \w{3}, [ 0123]\d \w{3} \d{4} \d\d:\d\d:\d\d GMT\r\n"
621 r"%s" r"Content-Length: 0\r\n" r"\r\n"
622 )
623 shortpat = (
624 "Status: 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000625 ).encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000626
627 for ssw in "FooBar/1.0", None:
628 sw = ssw and "Server: %s\r\n" % ssw or ""
629
630 for version in "1.0", "1.1":
631 for proto in "HTTP/0.9", "HTTP/1.0", "HTTP/1.1":
632
633 h = TestHandler(SERVER_PROTOCOL=proto)
634 h.origin_server = False
635 h.http_version = version
636 h.server_software = ssw
637 h.run(non_error_app)
638 self.assertEqual(shortpat,h.stdout.getvalue())
639
640 h = TestHandler(SERVER_PROTOCOL=proto)
641 h.origin_server = True
642 h.http_version = version
643 h.server_software = ssw
644 h.run(non_error_app)
645 if proto=="HTTP/0.9":
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000646 self.assertEqual(h.stdout.getvalue(),b"")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000647 else:
Georg Brandlab91fde2009-08-13 08:51:18 +0000648 self.assertTrue(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000649 re.match((stdpat%(version,sw)).encode("iso-8859-1"),
650 h.stdout.getvalue()),
651 ((stdpat%(version,sw)).encode("iso-8859-1"),
652 h.stdout.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000653 )
654
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000655 def testBytesData(self):
656 def app(e, s):
657 s(b"200 OK", [
658 (b"Content-Type", b"text/plain; charset=utf-8"),
659 ])
660 return [b"data"]
661
662 h = TestHandler()
663 h.run(app)
664 self.assertEqual(b"Status: 200 OK\r\n"
665 b"Content-Type: text/plain; charset=utf-8\r\n"
666 b"Content-Length: 4\r\n"
667 b"\r\n"
668 b"data",
669 h.stdout.getvalue())
670
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000671# This epilogue is needed for compatibility with the Python 2.5 regrtest module
672
673def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000674 support.run_unittest(__name__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000675
676if __name__ == "__main__":
677 test_main()
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707# the above lines intentionally left blank