blob: 398c53a96ee2a287ecb6119f3a030ca16869acaf [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
Senthil Kumarana5e0eaf2012-07-07 14:29:58 -070012from platform import python_implementation
13
Antoine Pitrou8f5731b2009-11-03 17:20:10 +000014import os
15import re
16import sys
Thomas Wouters0e3f5912006-08-11 14:57:12 +000017
Benjamin Petersonee8712c2008-05-20 21:35:26 +000018from test import support
Thomas Wouters0e3f5912006-08-11 14:57:12 +000019
20class MockServer(WSGIServer):
21 """Non-socket HTTP server"""
22
23 def __init__(self, server_address, RequestHandlerClass):
24 BaseServer.__init__(self, server_address, RequestHandlerClass)
25 self.server_bind()
26
27 def server_bind(self):
28 host, port = self.server_address
29 self.server_name = host
30 self.server_port = port
31 self.setup_environ()
32
33
34class MockHandler(WSGIRequestHandler):
35 """Non-socket HTTP handler"""
36 def setup(self):
37 self.connection = self.request
38 self.rfile, self.wfile = self.connection
39
40 def finish(self):
41 pass
42
43
44
45
46
47def hello_app(environ,start_response):
48 start_response("200 OK", [
49 ('Content-Type','text/plain'),
50 ('Date','Mon, 05 Jun 2006 18:49:54 GMT')
51 ])
Phillip J. Ebye1594222010-11-02 22:28:59 +000052 return [b"Hello, world!"]
Thomas Wouters0e3f5912006-08-11 14:57:12 +000053
Guido van Rossum6a10e022007-08-08 17:01:45 +000054def run_amock(app=hello_app, data=b"GET / HTTP/1.0\n\n"):
Thomas Wouters0e3f5912006-08-11 14:57:12 +000055 server = make_server("", 80, app, MockServer, MockHandler)
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000056 inp = BufferedReader(BytesIO(data))
Antoine Pitrou38a66ad2009-01-03 18:41:49 +000057 out = BytesIO()
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000058 olderr = sys.stderr
59 err = sys.stderr = StringIO()
Thomas Wouters0e3f5912006-08-11 14:57:12 +000060
61 try:
Jeremy Hyltone6b59c52007-08-10 19:13:33 +000062 server.finish_request((inp, out), ("127.0.0.1",8888))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000063 finally:
64 sys.stderr = olderr
65
66 return out.getvalue(), err.getvalue()
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90def compare_generic_iter(make_it,match):
91 """Utility to compare a generic 2.1/2.2+ iterator with an iterable
92
93 If running under Python 2.2+, this tests the iterator using iter()/next(),
94 as well as __getitem__. 'make_it' must be a function returning a fresh
95 iterator to be tested (since this may test the iterator twice)."""
96
97 it = make_it()
98 n = 0
99 for item in match:
100 if not it[n]==item: raise AssertionError
101 n+=1
102 try:
103 it[n]
104 except IndexError:
105 pass
106 else:
107 raise AssertionError("Too many items from __getitem__",it)
108
109 try:
110 iter, StopIteration
111 except NameError:
112 pass
113 else:
114 # Only test iter mode under 2.2+
115 it = make_it()
116 if not iter(it) is it: raise AssertionError
117 for item in match:
Georg Brandla18af4e2007-04-21 15:47:16 +0000118 if not next(it) == item: raise AssertionError
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000119 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000120 next(it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000121 except StopIteration:
122 pass
123 else:
Georg Brandla18af4e2007-04-21 15:47:16 +0000124 raise AssertionError("Too many items from .__next__()", it)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000125
126
127
128
129
130
131class IntegrationTests(TestCase):
132
133 def check_hello(self, out, has_length=True):
Senthil Kumarana5e0eaf2012-07-07 14:29:58 -0700134 pyver = (python_implementation() + "/" +
135 sys.version.split()[0])
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000136 self.assertEqual(out,
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000137 ("HTTP/1.0 200 OK\r\n"
Senthil Kumarana5e0eaf2012-07-07 14:29:58 -0700138 "Server: WSGIServer/0.2 " + pyver +"\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000139 "Content-Type: text/plain\r\n"
140 "Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" +
141 (has_length and "Content-Length: 13\r\n" or "") +
142 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000143 "Hello, world!").encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000144 )
145
146 def test_plain_hello(self):
147 out, err = run_amock()
148 self.check_hello(out)
149
150 def test_validated_hello(self):
151 out, err = run_amock(validator(hello_app))
152 # the middleware doesn't support len(), so content-length isn't there
153 self.check_hello(out, has_length=False)
154
155 def test_simple_validation_error(self):
156 def bad_app(environ,start_response):
157 start_response("200 OK", ('Content-Type','text/plain'))
158 return ["Hello, world!"]
159 out, err = run_amock(validator(bad_app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000160 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000161 b"A server error occurred. Please contact the administrator."
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000162 ))
163 self.assertEqual(
164 err.splitlines()[-2],
165 "AssertionError: Headers (('Content-Type', 'text/plain')) must"
Martin v. Löwis250ad612008-04-07 05:43:42 +0000166 " be of type list: <class 'tuple'>"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000167 )
168
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000169 def test_wsgi_input(self):
170 def bad_app(e,s):
171 e["wsgi.input"].read()
Phillip J. Ebye1594222010-11-02 22:28:59 +0000172 s("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000173 return [b"data"]
174 out, err = run_amock(validator(bad_app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000175 self.assertTrue(out.endswith(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000176 b"A server error occurred. Please contact the administrator."
177 ))
178 self.assertEqual(
179 err.splitlines()[-2], "AssertionError"
180 )
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000181
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000182 def test_bytes_validation(self):
183 def app(e, s):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000184 s("200 OK", [
185 ("Content-Type", "text/plain; charset=utf-8"),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000186 ("Date", "Wed, 24 Dec 2008 13:29:32 GMT"),
187 ])
188 return [b"data"]
189 out, err = run_amock(validator(app))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000190 self.assertTrue(err.endswith('"GET / HTTP/1.0" 200 4\n'))
Antoine Pitrou5f817412009-01-03 18:49:41 +0000191 ver = sys.version.split()[0].encode('ascii')
Senthil Kumarana5e0eaf2012-07-07 14:29:58 -0700192 py = python_implementation().encode('ascii')
193 pyver = py + b"/" + ver
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000194 self.assertEqual(
195 b"HTTP/1.0 200 OK\r\n"
Senthil Kumarana5e0eaf2012-07-07 14:29:58 -0700196 b"Server: WSGIServer/0.2 "+ pyver + b"\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000197 b"Content-Type: text/plain; charset=utf-8\r\n"
198 b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n"
199 b"\r\n"
200 b"data",
201 out)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000202
203
204
205
206class UtilityTests(TestCase):
207
208 def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
209 env = {'SCRIPT_NAME':sn_in,'PATH_INFO':pi_in}
210 util.setup_testing_defaults(env)
211 self.assertEqual(util.shift_path_info(env),part)
212 self.assertEqual(env['PATH_INFO'],pi_out)
213 self.assertEqual(env['SCRIPT_NAME'],sn_out)
214 return env
215
216 def checkDefault(self, key, value, alt=None):
217 # Check defaulting when empty
218 env = {}
219 util.setup_testing_defaults(env)
Ezio Melottie9615932010-01-24 19:26:24 +0000220 if isinstance(value, StringIO):
221 self.assertIsInstance(env[key], StringIO)
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000222 elif isinstance(value,BytesIO):
Ezio Melottie9615932010-01-24 19:26:24 +0000223 self.assertIsInstance(env[key],BytesIO)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000224 else:
Ezio Melottie9615932010-01-24 19:26:24 +0000225 self.assertEqual(env[key], value)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000226
227 # Check existing value
228 env = {key:alt}
229 util.setup_testing_defaults(env)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000230 self.assertTrue(env[key] is alt)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000231
232 def checkCrossDefault(self,key,value,**kw):
233 util.setup_testing_defaults(kw)
234 self.assertEqual(kw[key],value)
235
236 def checkAppURI(self,uri,**kw):
237 util.setup_testing_defaults(kw)
238 self.assertEqual(util.application_uri(kw),uri)
239
240 def checkReqURI(self,uri,query=1,**kw):
241 util.setup_testing_defaults(kw)
242 self.assertEqual(util.request_uri(kw,query),uri)
243
244
245
246
247
248
249 def checkFW(self,text,size,match):
250
251 def make_it(text=text,size=size):
252 return util.FileWrapper(StringIO(text),size)
253
254 compare_generic_iter(make_it,match)
255
256 it = make_it()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000257 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000258
259 for item in it:
260 pass
261
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000262 self.assertFalse(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000263
264 it.close()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000265 self.assertTrue(it.filelike.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000266
267
268 def testSimpleShifts(self):
269 self.checkShift('','/', '', '/', '')
270 self.checkShift('','/x', 'x', '/x', '')
271 self.checkShift('/','', None, '/', '')
272 self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
273 self.checkShift('/a','/x/', 'x', '/a/x', '/')
274
275
276 def testNormalizedShifts(self):
277 self.checkShift('/a/b', '/../y', '..', '/a', '/y')
278 self.checkShift('', '/../y', '..', '', '/y')
279 self.checkShift('/a/b', '//y', 'y', '/a/b/y', '')
280 self.checkShift('/a/b', '//y/', 'y', '/a/b/y', '/')
281 self.checkShift('/a/b', '/./y', 'y', '/a/b/y', '')
282 self.checkShift('/a/b', '/./y/', 'y', '/a/b/y', '/')
283 self.checkShift('/a/b', '///./..//y/.//', '..', '/a', '/y/')
284 self.checkShift('/a/b', '///', '', '/a/b/', '')
285 self.checkShift('/a/b', '/.//', '', '/a/b/', '')
286 self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
287 self.checkShift('/a/b', '/.', None, '/a/b', '')
288
289
290 def testDefaults(self):
291 for key, value in [
292 ('SERVER_NAME','127.0.0.1'),
293 ('SERVER_PORT', '80'),
294 ('SERVER_PROTOCOL','HTTP/1.0'),
295 ('HTTP_HOST','127.0.0.1'),
296 ('REQUEST_METHOD','GET'),
297 ('SCRIPT_NAME',''),
298 ('PATH_INFO','/'),
299 ('wsgi.version', (1,0)),
300 ('wsgi.run_once', 0),
301 ('wsgi.multithread', 0),
302 ('wsgi.multiprocess', 0),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000303 ('wsgi.input', BytesIO()),
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000304 ('wsgi.errors', StringIO()),
305 ('wsgi.url_scheme','http'),
306 ]:
307 self.checkDefault(key,value)
308
309
310 def testCrossDefaults(self):
311 self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
312 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
313 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="1")
314 self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="yes")
315 self.checkCrossDefault('wsgi.url_scheme',"http",HTTPS="foo")
316 self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
317 self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
318
319
320 def testGuessScheme(self):
321 self.assertEqual(util.guess_scheme({}), "http")
322 self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
323 self.assertEqual(util.guess_scheme({'HTTPS':"on"}), "https")
324 self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
325 self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
326
327
328
329
330
331 def testAppURIs(self):
332 self.checkAppURI("http://127.0.0.1/")
333 self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000334 self.checkAppURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000335 self.checkAppURI("http://spam.example.com:2071/",
336 HTTP_HOST="spam.example.com:2071", SERVER_PORT="2071")
337 self.checkAppURI("http://spam.example.com/",
338 SERVER_NAME="spam.example.com")
339 self.checkAppURI("http://127.0.0.1/",
340 HTTP_HOST="127.0.0.1", SERVER_NAME="spam.example.com")
341 self.checkAppURI("https://127.0.0.1/", HTTPS="on")
342 self.checkAppURI("http://127.0.0.1:8000/", SERVER_PORT="8000",
343 HTTP_HOST=None)
344
345 def testReqURIs(self):
346 self.checkReqURI("http://127.0.0.1/")
347 self.checkReqURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
Guido van Rossum52dbbb92008-08-18 21:44:30 +0000348 self.checkReqURI("http://127.0.0.1/sp%C3%A4m", SCRIPT_NAME="/späm")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000349 self.checkReqURI("http://127.0.0.1/spammity/spam",
350 SCRIPT_NAME="/spammity", PATH_INFO="/spam")
Senthil Kumaran299fa4c2010-12-29 06:25:42 +0000351 self.checkReqURI("http://127.0.0.1/spammity/spam;ham",
352 SCRIPT_NAME="/spammity", PATH_INFO="/spam;ham")
353 self.checkReqURI("http://127.0.0.1/spammity/spam;cookie=1234,5678",
354 SCRIPT_NAME="/spammity", PATH_INFO="/spam;cookie=1234,5678")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000355 self.checkReqURI("http://127.0.0.1/spammity/spam?say=ni",
356 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
357 self.checkReqURI("http://127.0.0.1/spammity/spam", 0,
358 SCRIPT_NAME="/spammity", PATH_INFO="/spam",QUERY_STRING="say=ni")
359
360 def testFileWrapper(self):
361 self.checkFW("xyz"*50, 120, ["xyz"*40,"xyz"*10])
362
363 def testHopByHop(self):
364 for hop in (
365 "Connection Keep-Alive Proxy-Authenticate Proxy-Authorization "
366 "TE Trailers Transfer-Encoding Upgrade"
367 ).split():
368 for alt in hop, hop.title(), hop.upper(), hop.lower():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000369 self.assertTrue(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000370
371 # Not comprehensive, just a few random header names
372 for hop in (
373 "Accept Cache-Control Date Pragma Trailer Via Warning"
374 ).split():
375 for alt in hop, hop.title(), hop.upper(), hop.lower():
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000376 self.assertFalse(util.is_hop_by_hop(alt))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000377
378class HeaderTests(TestCase):
379
380 def testMappingInterface(self):
381 test = [('x','y')]
382 self.assertEqual(len(Headers([])),0)
383 self.assertEqual(len(Headers(test[:])),1)
384 self.assertEqual(Headers(test[:]).keys(), ['x'])
385 self.assertEqual(Headers(test[:]).values(), ['y'])
386 self.assertEqual(Headers(test[:]).items(), test)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000387 self.assertFalse(Headers(test).items() is test) # must be copy!
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000388
389 h=Headers([])
390 del h['foo'] # should not raise an error
391
392 h['Foo'] = 'bar'
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000393 for m in h.__contains__, h.get, h.get_all, h.__getitem__:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000394 self.assertTrue(m('foo'))
395 self.assertTrue(m('Foo'))
396 self.assertTrue(m('FOO'))
397 self.assertFalse(m('bar'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000398
399 self.assertEqual(h['foo'],'bar')
400 h['foo'] = 'baz'
401 self.assertEqual(h['FOO'],'baz')
402 self.assertEqual(h.get_all('foo'),['baz'])
403
404 self.assertEqual(h.get("foo","whee"), "baz")
405 self.assertEqual(h.get("zoo","whee"), "whee")
406 self.assertEqual(h.setdefault("foo","whee"), "baz")
407 self.assertEqual(h.setdefault("zoo","whee"), "whee")
408 self.assertEqual(h["foo"],"baz")
409 self.assertEqual(h["zoo"],"whee")
410
411 def testRequireList(self):
412 self.assertRaises(TypeError, Headers, "foo")
413
414
415 def testExtras(self):
416 h = Headers([])
417 self.assertEqual(str(h),'\r\n')
418
419 h.add_header('foo','bar',baz="spam")
420 self.assertEqual(h['foo'], 'bar; baz="spam"')
421 self.assertEqual(str(h),'foo: bar; baz="spam"\r\n\r\n')
422
423 h.add_header('Foo','bar',cheese=None)
424 self.assertEqual(h.get_all('foo'),
425 ['bar; baz="spam"', 'bar; cheese'])
426
427 self.assertEqual(str(h),
428 'foo: bar; baz="spam"\r\n'
429 'Foo: bar; cheese\r\n'
430 '\r\n'
431 )
432
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000433class ErrorHandler(BaseCGIHandler):
434 """Simple handler subclass for testing BaseHandler"""
435
Antoine Pitrou8f5731b2009-11-03 17:20:10 +0000436 # BaseHandler records the OS environment at import time, but envvars
437 # might have been changed later by other tests, which trips up
438 # HandlerTests.testEnviron().
439 os_environ = dict(os.environ.items())
440
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000441 def __init__(self,**kw):
442 setup_testing_defaults(kw)
443 BaseCGIHandler.__init__(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000444 self, BytesIO(), BytesIO(), StringIO(), kw,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000445 multithread=True, multiprocess=True
446 )
447
448class TestHandler(ErrorHandler):
449 """Simple handler subclass for testing BaseHandler, w/error passthru"""
450
451 def handle_error(self):
452 raise # for testing, we want to see what's happening
453
454
455
456
457
458
459
460
461
462
463
464class HandlerTests(TestCase):
465
466 def checkEnvironAttrs(self, handler):
467 env = handler.environ
468 for attr in [
469 'version','multithread','multiprocess','run_once','file_wrapper'
470 ]:
471 if attr=='file_wrapper' and handler.wsgi_file_wrapper is None:
472 continue
473 self.assertEqual(getattr(handler,'wsgi_'+attr),env['wsgi.'+attr])
474
475 def checkOSEnviron(self,handler):
476 empty = {}; setup_testing_defaults(empty)
477 env = handler.environ
478 from os import environ
479 for k,v in environ.items():
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000480 if k not in empty:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000481 self.assertEqual(env[k],v)
482 for k,v in empty.items():
Benjamin Peterson577473f2010-01-19 00:09:57 +0000483 self.assertIn(k, env)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000484
485 def testEnviron(self):
486 h = TestHandler(X="Y")
487 h.setup_environ()
488 self.checkEnvironAttrs(h)
489 self.checkOSEnviron(h)
490 self.assertEqual(h.environ["X"],"Y")
491
492 def testCGIEnviron(self):
493 h = BaseCGIHandler(None,None,None,{})
494 h.setup_environ()
495 for key in 'wsgi.url_scheme', 'wsgi.input', 'wsgi.errors':
Benjamin Peterson577473f2010-01-19 00:09:57 +0000496 self.assertIn(key, h.environ)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000497
498 def testScheme(self):
499 h=TestHandler(HTTPS="on"); h.setup_environ()
500 self.assertEqual(h.environ['wsgi.url_scheme'],'https')
501 h=TestHandler(); h.setup_environ()
502 self.assertEqual(h.environ['wsgi.url_scheme'],'http')
503
504
505 def testAbstractMethods(self):
506 h = BaseHandler()
507 for name in [
508 '_flush','get_stdin','get_stderr','add_cgi_vars'
509 ]:
510 self.assertRaises(NotImplementedError, getattr(h,name))
511 self.assertRaises(NotImplementedError, h._write, "test")
512
513
514 def testContentLength(self):
515 # Demo one reason iteration is better than write()... ;)
516
517 def trivial_app1(e,s):
518 s('200 OK',[])
Phillip J. Ebye1594222010-11-02 22:28:59 +0000519 return [e['wsgi.url_scheme'].encode('iso-8859-1')]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000520
521 def trivial_app2(e,s):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000522 s('200 OK',[])(e['wsgi.url_scheme'].encode('iso-8859-1'))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000523 return []
524
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000525 def trivial_app3(e,s):
526 s('200 OK',[])
527 return ['\u0442\u0435\u0441\u0442'.encode("utf-8")]
528
Antoine Pitroub715fac2011-01-06 17:17:04 +0000529 def trivial_app4(e,s):
530 # Simulate a response to a HEAD request
531 s('200 OK',[('Content-Length', '12345')])
532 return []
533
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000534 h = TestHandler()
535 h.run(trivial_app1)
536 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000537 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000538 "Content-Length: 4\r\n"
539 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000540 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000541
542 h = TestHandler()
543 h.run(trivial_app2)
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 "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000547 "http").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000548
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000549 h = TestHandler()
550 h.run(trivial_app3)
551 self.assertEqual(h.stdout.getvalue(),
552 b'Status: 200 OK\r\n'
553 b'Content-Length: 8\r\n'
554 b'\r\n'
555 b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000556
Antoine Pitroub715fac2011-01-06 17:17:04 +0000557 h = TestHandler()
558 h.run(trivial_app4)
559 self.assertEqual(h.stdout.getvalue(),
560 b'Status: 200 OK\r\n'
561 b'Content-Length: 12345\r\n'
562 b'\r\n')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000563
564 def testBasicErrorOutput(self):
565
566 def non_error_app(e,s):
567 s('200 OK',[])
568 return []
569
570 def error_app(e,s):
571 raise AssertionError("This should be caught by handler")
572
573 h = ErrorHandler()
574 h.run(non_error_app)
575 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000576 ("Status: 200 OK\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000577 "Content-Length: 0\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000578 "\r\n").encode("iso-8859-1"))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000579 self.assertEqual(h.stderr.getvalue(),"")
580
581 h = ErrorHandler()
582 h.run(error_app)
583 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000584 ("Status: %s\r\n"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000585 "Content-Type: text/plain\r\n"
586 "Content-Length: %d\r\n"
Phillip J. Ebye1594222010-11-02 22:28:59 +0000587 "\r\n" % (h.error_status,len(h.error_body))).encode('iso-8859-1')
588 + h.error_body)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000589
Benjamin Peterson577473f2010-01-19 00:09:57 +0000590 self.assertIn("AssertionError", h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000591
592 def testErrorAfterOutput(self):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000593 MSG = b"Some output has been sent"
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000594 def error_app(e,s):
595 s("200 OK",[])(MSG)
596 raise AssertionError("This should be caught by handler")
597
598 h = ErrorHandler()
599 h.run(error_app)
600 self.assertEqual(h.stdout.getvalue(),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000601 ("Status: 200 OK\r\n"
Phillip J. Ebye1594222010-11-02 22:28:59 +0000602 "\r\n".encode("iso-8859-1")+MSG))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000603 self.assertIn("AssertionError", h.stderr.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000604
605
606 def testHeaderFormats(self):
607
608 def non_error_app(e,s):
609 s('200 OK',[])
610 return []
611
612 stdpat = (
613 r"HTTP/%s 200 OK\r\n"
614 r"Date: \w{3}, [ 0123]\d \w{3} \d{4} \d\d:\d\d:\d\d GMT\r\n"
615 r"%s" r"Content-Length: 0\r\n" r"\r\n"
616 )
617 shortpat = (
618 "Status: 200 OK\r\n" "Content-Length: 0\r\n" "\r\n"
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000619 ).encode("iso-8859-1")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000620
621 for ssw in "FooBar/1.0", None:
622 sw = ssw and "Server: %s\r\n" % ssw or ""
623
624 for version in "1.0", "1.1":
625 for proto in "HTTP/0.9", "HTTP/1.0", "HTTP/1.1":
626
627 h = TestHandler(SERVER_PROTOCOL=proto)
628 h.origin_server = False
629 h.http_version = version
630 h.server_software = ssw
631 h.run(non_error_app)
632 self.assertEqual(shortpat,h.stdout.getvalue())
633
634 h = TestHandler(SERVER_PROTOCOL=proto)
635 h.origin_server = True
636 h.http_version = version
637 h.server_software = ssw
638 h.run(non_error_app)
639 if proto=="HTTP/0.9":
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000640 self.assertEqual(h.stdout.getvalue(),b"")
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000641 else:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000642 self.assertTrue(
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000643 re.match((stdpat%(version,sw)).encode("iso-8859-1"),
644 h.stdout.getvalue()),
645 ((stdpat%(version,sw)).encode("iso-8859-1"),
646 h.stdout.getvalue())
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000647 )
648
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000649 def testBytesData(self):
650 def app(e, s):
Phillip J. Ebye1594222010-11-02 22:28:59 +0000651 s("200 OK", [
652 ("Content-Type", "text/plain; charset=utf-8"),
Antoine Pitrou38a66ad2009-01-03 18:41:49 +0000653 ])
654 return [b"data"]
655
656 h = TestHandler()
657 h.run(app)
658 self.assertEqual(b"Status: 200 OK\r\n"
659 b"Content-Type: text/plain; charset=utf-8\r\n"
660 b"Content-Length: 4\r\n"
661 b"\r\n"
662 b"data",
663 h.stdout.getvalue())
664
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000665# This epilogue is needed for compatibility with the Python 2.5 regrtest module
666
667def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000668 support.run_unittest(__name__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000669
670if __name__ == "__main__":
671 test_main()
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
701# the above lines intentionally left blank