blob: 93807c1959bb49f3a46e1a04c1fe69b7dd2765de [file] [log] [blame]
Georg Brandlf899dfa2008-05-18 09:12:20 +00001"""Unittests for the various HTTPServer modules.
2
3Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
4Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
5"""
6
Georg Brandlf899dfa2008-05-18 09:12:20 +00007import os
8import sys
Senthil Kumaranb643fc62010-09-30 06:40:56 +00009import re
Georg Brandlf899dfa2008-05-18 09:12:20 +000010import base64
Martin Panter0cf2cf22016-04-18 03:45:18 +000011import ntpath
Georg Brandlf899dfa2008-05-18 09:12:20 +000012import shutil
13import urllib
14import httplib
15import tempfile
Georg Brandlf899dfa2008-05-18 09:12:20 +000016import unittest
Senthil Kumaran5f7e7342012-04-12 02:23:23 +080017import CGIHTTPServer
Senthil Kumaranb643fc62010-09-30 06:40:56 +000018
Senthil Kumaran5f7e7342012-04-12 02:23:23 +080019
20from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
21from SimpleHTTPServer import SimpleHTTPRequestHandler
22from CGIHTTPServer import CGIHTTPRequestHandler
Senthil Kumaranb643fc62010-09-30 06:40:56 +000023from StringIO import StringIO
Georg Brandlf899dfa2008-05-18 09:12:20 +000024from test import test_support
Senthil Kumaran5f7e7342012-04-12 02:23:23 +080025
26
Victor Stinner6a102812010-04-27 23:55:59 +000027threading = test_support.import_module('threading')
Georg Brandlf899dfa2008-05-18 09:12:20 +000028
29
30class NoLogRequestHandler:
31 def log_message(self, *args):
32 # don't write log messages to stderr
33 pass
34
Antoine Pitrou2623ae92010-12-16 17:18:49 +000035class SocketlessRequestHandler(SimpleHTTPRequestHandler):
Senthil Kumaranb643fc62010-09-30 06:40:56 +000036 def __init__(self):
37 self.get_called = False
38 self.protocol_version = "HTTP/1.1"
39
40 def do_GET(self):
41 self.get_called = True
42 self.send_response(200)
43 self.send_header('Content-Type', 'text/html')
44 self.end_headers()
Antoine Pitrou2623ae92010-12-16 17:18:49 +000045 self.wfile.write(b'<html><body>Data</body></html>\r\n')
Senthil Kumaranb643fc62010-09-30 06:40:56 +000046
Senthil Kumaran5f7e7342012-04-12 02:23:23 +080047 def log_message(self, fmt, *args):
Senthil Kumaranb643fc62010-09-30 06:40:56 +000048 pass
49
50
Georg Brandlf899dfa2008-05-18 09:12:20 +000051class TestServerThread(threading.Thread):
52 def __init__(self, test_object, request_handler):
53 threading.Thread.__init__(self)
54 self.request_handler = request_handler
55 self.test_object = test_object
Georg Brandlf899dfa2008-05-18 09:12:20 +000056
57 def run(self):
58 self.server = HTTPServer(('', 0), self.request_handler)
59 self.test_object.PORT = self.server.socket.getsockname()[1]
Antoine Pitrou1ca8c192010-04-25 21:15:50 +000060 self.test_object.server_started.set()
61 self.test_object = None
Georg Brandlf899dfa2008-05-18 09:12:20 +000062 try:
Antoine Pitrou1ca8c192010-04-25 21:15:50 +000063 self.server.serve_forever(0.05)
Georg Brandlf899dfa2008-05-18 09:12:20 +000064 finally:
65 self.server.server_close()
66
67 def stop(self):
68 self.server.shutdown()
Victor Stinner14635182018-06-04 23:53:52 +020069 self.join()
Georg Brandlf899dfa2008-05-18 09:12:20 +000070
71
72class BaseTestCase(unittest.TestCase):
73 def setUp(self):
Antoine Pitrou85bd5872009-10-27 18:50:52 +000074 self._threads = test_support.threading_setup()
Nick Coghlan87c03b32009-10-17 15:23:08 +000075 os.environ = test_support.EnvironmentVarGuard()
Antoine Pitrou1ca8c192010-04-25 21:15:50 +000076 self.server_started = threading.Event()
Georg Brandlf899dfa2008-05-18 09:12:20 +000077 self.thread = TestServerThread(self, self.request_handler)
78 self.thread.start()
Antoine Pitrou1ca8c192010-04-25 21:15:50 +000079 self.server_started.wait()
Georg Brandlf899dfa2008-05-18 09:12:20 +000080
81 def tearDown(self):
Georg Brandlf899dfa2008-05-18 09:12:20 +000082 self.thread.stop()
Nick Coghlan87c03b32009-10-17 15:23:08 +000083 os.environ.__exit__()
Antoine Pitrou85bd5872009-10-27 18:50:52 +000084 test_support.threading_cleanup(*self._threads)
Georg Brandlf899dfa2008-05-18 09:12:20 +000085
86 def request(self, uri, method='GET', body=None, headers={}):
87 self.connection = httplib.HTTPConnection('localhost', self.PORT)
88 self.connection.request(method, uri, body, headers)
89 return self.connection.getresponse()
90
Senthil Kumaranb643fc62010-09-30 06:40:56 +000091class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
Ezio Melottic2077b02011-03-16 12:34:31 +020092 """Test the functionality of the BaseHTTPServer focussing on
Senthil Kumaranb643fc62010-09-30 06:40:56 +000093 BaseHTTPRequestHandler.
94 """
95
96 HTTPResponseMatch = re.compile('HTTP/1.[0-9]+ 200 OK')
97
98 def setUp (self):
99 self.handler = SocketlessRequestHandler()
100
101 def send_typical_request(self, message):
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800102 input_msg = StringIO(message)
Senthil Kumaranb643fc62010-09-30 06:40:56 +0000103 output = StringIO()
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800104 self.handler.rfile = input_msg
Senthil Kumaranb643fc62010-09-30 06:40:56 +0000105 self.handler.wfile = output
106 self.handler.handle_one_request()
107 output.seek(0)
108 return output.readlines()
109
110 def verify_get_called(self):
111 self.assertTrue(self.handler.get_called)
112
113 def verify_expected_headers(self, headers):
114 for fieldName in 'Server: ', 'Date: ', 'Content-Type: ':
115 self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1)
116
117 def verify_http_server_response(self, response):
118 match = self.HTTPResponseMatch.search(response)
Serhiy Storchaka528bed82014-02-08 14:49:55 +0200119 self.assertIsNotNone(match)
Senthil Kumaranb643fc62010-09-30 06:40:56 +0000120
121 def test_http_1_1(self):
122 result = self.send_typical_request('GET / HTTP/1.1\r\n\r\n')
123 self.verify_http_server_response(result[0])
124 self.verify_expected_headers(result[1:-1])
125 self.verify_get_called()
126 self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
127
128 def test_http_1_0(self):
129 result = self.send_typical_request('GET / HTTP/1.0\r\n\r\n')
130 self.verify_http_server_response(result[0])
131 self.verify_expected_headers(result[1:-1])
132 self.verify_get_called()
133 self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
134
135 def test_http_0_9(self):
136 result = self.send_typical_request('GET / HTTP/0.9\r\n\r\n')
137 self.assertEqual(len(result), 1)
138 self.assertEqual(result[0], '<html><body>Data</body></html>\r\n')
139 self.verify_get_called()
140
141 def test_with_continue_1_0(self):
142 result = self.send_typical_request('GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n')
143 self.verify_http_server_response(result[0])
144 self.verify_expected_headers(result[1:-1])
145 self.verify_get_called()
146 self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
147
Antoine Pitrou2623ae92010-12-16 17:18:49 +0000148 def test_request_length(self):
149 # Issue #10714: huge request lines are discarded, to avoid Denial
150 # of Service attacks.
151 result = self.send_typical_request(b'GET ' + b'x' * 65537)
152 self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
153 self.assertFalse(self.handler.get_called)
154
Georg Brandlf899dfa2008-05-18 09:12:20 +0000155
156class BaseHTTPServerTestCase(BaseTestCase):
157 class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
158 protocol_version = 'HTTP/1.1'
159 default_request_version = 'HTTP/1.1'
160
161 def do_TEST(self):
162 self.send_response(204)
163 self.send_header('Content-Type', 'text/html')
164 self.send_header('Connection', 'close')
165 self.end_headers()
166
167 def do_KEEP(self):
168 self.send_response(204)
169 self.send_header('Content-Type', 'text/html')
170 self.send_header('Connection', 'keep-alive')
171 self.end_headers()
172
173 def do_KEYERROR(self):
174 self.send_error(999)
175
176 def do_CUSTOM(self):
177 self.send_response(999)
178 self.send_header('Content-Type', 'text/html')
179 self.send_header('Connection', 'close')
180 self.end_headers()
181
Martin Panter6af1c492016-06-08 07:16:14 +0000182 def do_SEND_ERROR(self):
183 self.send_error(int(self.path[1:]))
184
185 def do_HEAD(self):
186 self.send_error(int(self.path[1:]))
187
Georg Brandlf899dfa2008-05-18 09:12:20 +0000188 def setUp(self):
189 BaseTestCase.setUp(self)
190 self.con = httplib.HTTPConnection('localhost', self.PORT)
191 self.con.connect()
192
193 def test_command(self):
194 self.con.request('GET', '/')
195 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000196 self.assertEqual(res.status, 501)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000197
198 def test_request_line_trimming(self):
199 self.con._http_vsn_str = 'HTTP/1.1\n'
R David Murray3eb76fc2014-06-24 16:49:24 -0400200 self.con.putrequest('XYZBOGUS', '/')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000201 self.con.endheaders()
202 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000203 self.assertEqual(res.status, 501)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000204
205 def test_version_bogus(self):
206 self.con._http_vsn_str = 'FUBAR'
207 self.con.putrequest('GET', '/')
208 self.con.endheaders()
209 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000210 self.assertEqual(res.status, 400)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000211
212 def test_version_digits(self):
213 self.con._http_vsn_str = 'HTTP/9.9.9'
214 self.con.putrequest('GET', '/')
215 self.con.endheaders()
216 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000217 self.assertEqual(res.status, 400)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000218
219 def test_version_none_get(self):
220 self.con._http_vsn_str = ''
221 self.con.putrequest('GET', '/')
222 self.con.endheaders()
223 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000224 self.assertEqual(res.status, 501)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000225
226 def test_version_none(self):
R David Murray3eb76fc2014-06-24 16:49:24 -0400227 # Test that a valid method is rejected when not HTTP/1.x
Georg Brandlf899dfa2008-05-18 09:12:20 +0000228 self.con._http_vsn_str = ''
R David Murray3eb76fc2014-06-24 16:49:24 -0400229 self.con.putrequest('CUSTOM', '/')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000230 self.con.endheaders()
231 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000232 self.assertEqual(res.status, 400)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000233
234 def test_version_invalid(self):
235 self.con._http_vsn = 99
236 self.con._http_vsn_str = 'HTTP/9.9'
237 self.con.putrequest('GET', '/')
238 self.con.endheaders()
239 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000240 self.assertEqual(res.status, 505)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000241
242 def test_send_blank(self):
243 self.con._http_vsn_str = ''
244 self.con.putrequest('', '')
245 self.con.endheaders()
246 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000247 self.assertEqual(res.status, 400)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000248
249 def test_header_close(self):
250 self.con.putrequest('GET', '/')
251 self.con.putheader('Connection', 'close')
252 self.con.endheaders()
253 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000254 self.assertEqual(res.status, 501)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000255
256 def test_head_keep_alive(self):
257 self.con._http_vsn_str = 'HTTP/1.1'
258 self.con.putrequest('GET', '/')
259 self.con.putheader('Connection', 'keep-alive')
260 self.con.endheaders()
261 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000262 self.assertEqual(res.status, 501)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000263
264 def test_handler(self):
265 self.con.request('TEST', '/')
266 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000267 self.assertEqual(res.status, 204)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000268
269 def test_return_header_keep_alive(self):
270 self.con.request('KEEP', '/')
271 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000272 self.assertEqual(res.getheader('Connection'), 'keep-alive')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000273 self.con.request('TEST', '/')
Brian Curtin55b62512010-10-31 00:36:01 +0000274 self.addCleanup(self.con.close)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000275
276 def test_internal_key_error(self):
277 self.con.request('KEYERROR', '/')
278 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000279 self.assertEqual(res.status, 999)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000280
281 def test_return_custom_status(self):
282 self.con.request('CUSTOM', '/')
283 res = self.con.getresponse()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000284 self.assertEqual(res.status, 999)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000285
Martin Panter6af1c492016-06-08 07:16:14 +0000286 def test_send_error(self):
287 allow_transfer_encoding_codes = (205, 304)
288 for code in (101, 102, 204, 205, 304):
289 self.con.request('SEND_ERROR', '/{}'.format(code))
290 res = self.con.getresponse()
291 self.assertEqual(code, res.status)
292 self.assertEqual(None, res.getheader('Content-Length'))
293 self.assertEqual(None, res.getheader('Content-Type'))
294 if code not in allow_transfer_encoding_codes:
295 self.assertEqual(None, res.getheader('Transfer-Encoding'))
296
297 data = res.read()
298 self.assertEqual(b'', data)
299
300 def test_head_via_send_error(self):
301 allow_transfer_encoding_codes = (205, 304)
302 for code in (101, 200, 204, 205, 304):
303 self.con.request('HEAD', '/{}'.format(code))
304 res = self.con.getresponse()
305 self.assertEqual(code, res.status)
306 if code == 200:
307 self.assertEqual(None, res.getheader('Content-Length'))
308 self.assertIn('text/html', res.getheader('Content-Type'))
309 else:
310 self.assertEqual(None, res.getheader('Content-Length'))
311 self.assertEqual(None, res.getheader('Content-Type'))
312 if code not in allow_transfer_encoding_codes:
313 self.assertEqual(None, res.getheader('Transfer-Encoding'))
314
315 data = res.read()
316 self.assertEqual(b'', data)
317
Georg Brandlf899dfa2008-05-18 09:12:20 +0000318
319class SimpleHTTPServerTestCase(BaseTestCase):
320 class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
321 pass
322
323 def setUp(self):
324 BaseTestCase.setUp(self)
Georg Brandl7bb16532008-05-20 06:47:31 +0000325 self.cwd = os.getcwd()
326 basetempdir = tempfile.gettempdir()
327 os.chdir(basetempdir)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000328 self.data = 'We are the knights who say Ni!'
Georg Brandl7bb16532008-05-20 06:47:31 +0000329 self.tempdir = tempfile.mkdtemp(dir=basetempdir)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000330 self.tempdir_name = os.path.basename(self.tempdir)
Martin Panteraf58c852016-04-09 04:56:10 +0000331 self.base_url = '/' + self.tempdir_name
Georg Brandl7bb16532008-05-20 06:47:31 +0000332 temp = open(os.path.join(self.tempdir, 'test'), 'wb')
333 temp.write(self.data)
334 temp.close()
Georg Brandlf899dfa2008-05-18 09:12:20 +0000335
336 def tearDown(self):
337 try:
Georg Brandl7bb16532008-05-20 06:47:31 +0000338 os.chdir(self.cwd)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000339 try:
340 shutil.rmtree(self.tempdir)
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800341 except OSError:
Georg Brandlf899dfa2008-05-18 09:12:20 +0000342 pass
343 finally:
344 BaseTestCase.tearDown(self)
345
346 def check_status_and_reason(self, response, status, data=None):
347 body = response.read()
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000348 self.assertTrue(response)
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000349 self.assertEqual(response.status, status)
350 self.assertIsNotNone(response.reason)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000351 if data:
352 self.assertEqual(data, body)
353
354 def test_get(self):
355 #constructs the path relative to the root directory of the HTTPServer
Martin Panteraf58c852016-04-09 04:56:10 +0000356 response = self.request(self.base_url + '/test')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000357 self.check_status_and_reason(response, 200, data=self.data)
Senthil Kumarand4fac042013-09-13 00:18:55 -0700358 # check for trailing "/" which should return 404. See Issue17324
Martin Panteraf58c852016-04-09 04:56:10 +0000359 response = self.request(self.base_url + '/test/')
Senthil Kumarand4fac042013-09-13 00:18:55 -0700360 self.check_status_and_reason(response, 404)
Martin Panteraf58c852016-04-09 04:56:10 +0000361 response = self.request(self.base_url + '/')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000362 self.check_status_and_reason(response, 200)
Martin Panteraf58c852016-04-09 04:56:10 +0000363 response = self.request(self.base_url)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000364 self.check_status_and_reason(response, 301)
Martin Panteraf58c852016-04-09 04:56:10 +0000365 response = self.request(self.base_url + '/?hi=2')
Benjamin Petersona71cfc52014-12-26 10:53:43 -0600366 self.check_status_and_reason(response, 200)
Martin Panteraf58c852016-04-09 04:56:10 +0000367 response = self.request(self.base_url + '?hi=1')
Benjamin Petersona71cfc52014-12-26 10:53:43 -0600368 self.check_status_and_reason(response, 301)
369 self.assertEqual(response.getheader("Location"),
Martin Panteraf58c852016-04-09 04:56:10 +0000370 self.base_url + "/?hi=1")
Georg Brandlf899dfa2008-05-18 09:12:20 +0000371 response = self.request('/ThisDoesNotExist')
372 self.check_status_and_reason(response, 404)
373 response = self.request('/' + 'ThisDoesNotExist' + '/')
374 self.check_status_and_reason(response, 404)
Benjamin Peterson3c0027b2014-04-04 13:59:33 -0400375 with open(os.path.join(self.tempdir_name, 'index.html'), 'w') as fp:
Martin Panteraf58c852016-04-09 04:56:10 +0000376 response = self.request(self.base_url + '/')
Benjamin Peterson3c0027b2014-04-04 13:59:33 -0400377 self.check_status_and_reason(response, 200)
378 # chmod() doesn't work as expected on Windows, and filesystem
379 # permissions are ignored by root on Unix.
380 if os.name == 'posix' and os.geteuid() != 0:
381 os.chmod(self.tempdir, 0)
Martin Panteraf58c852016-04-09 04:56:10 +0000382 response = self.request(self.base_url + '/')
Benjamin Peterson3c0027b2014-04-04 13:59:33 -0400383 self.check_status_and_reason(response, 404)
384 os.chmod(self.tempdir, 0755)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000385
386 def test_head(self):
387 response = self.request(
Martin Panteraf58c852016-04-09 04:56:10 +0000388 self.base_url + '/test', method='HEAD')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000389 self.check_status_and_reason(response, 200)
390 self.assertEqual(response.getheader('content-length'),
391 str(len(self.data)))
392 self.assertEqual(response.getheader('content-type'),
393 'application/octet-stream')
394
395 def test_invalid_requests(self):
396 response = self.request('/', method='FOO')
397 self.check_status_and_reason(response, 501)
398 # requests must be case sensitive,so this should fail too
Terry Jan Reedy0daddbd2014-10-18 17:10:02 -0400399 response = self.request('/', method='custom')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000400 self.check_status_and_reason(response, 501)
401 response = self.request('/', method='GETs')
402 self.check_status_and_reason(response, 501)
403
Martin Panteraf58c852016-04-09 04:56:10 +0000404 def test_path_without_leading_slash(self):
405 response = self.request(self.tempdir_name + '/test')
Martin Panterec3c2452016-04-09 13:45:52 +0000406 self.check_status_and_reason(response, 200, data=self.data)
Martin Panteraf58c852016-04-09 04:56:10 +0000407 response = self.request(self.tempdir_name + '/test/')
Martin Panterec3c2452016-04-09 13:45:52 +0000408 self.check_status_and_reason(response, 404)
Martin Panteraf58c852016-04-09 04:56:10 +0000409 response = self.request(self.tempdir_name + '/')
Martin Panterec3c2452016-04-09 13:45:52 +0000410 self.check_status_and_reason(response, 200)
Martin Panteraf58c852016-04-09 04:56:10 +0000411 response = self.request(self.tempdir_name)
Martin Panterec3c2452016-04-09 13:45:52 +0000412 self.check_status_and_reason(response, 301)
Martin Panteraf58c852016-04-09 04:56:10 +0000413 response = self.request(self.tempdir_name + '/?hi=2')
Martin Panterec3c2452016-04-09 13:45:52 +0000414 self.check_status_and_reason(response, 200)
Martin Panteraf58c852016-04-09 04:56:10 +0000415 response = self.request(self.tempdir_name + '?hi=1')
Martin Panterec3c2452016-04-09 13:45:52 +0000416 self.check_status_and_reason(response, 301)
Martin Panteraf58c852016-04-09 04:56:10 +0000417 self.assertEqual(response.getheader("Location"),
418 self.tempdir_name + "/?hi=1")
419
Georg Brandlf899dfa2008-05-18 09:12:20 +0000420
421cgi_file1 = """\
422#!%s
423
424print "Content-type: text/html"
425print
426print "Hello World"
427"""
428
429cgi_file2 = """\
430#!%s
431import cgi
432
433print "Content-type: text/html"
434print
435
436form = cgi.FieldStorage()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000437print "%%s, %%s, %%s" %% (form.getfirst("spam"), form.getfirst("eggs"),
438 form.getfirst("bacon"))
Georg Brandlf899dfa2008-05-18 09:12:20 +0000439"""
440
Martin Pantercff22eb2015-10-03 05:38:07 +0000441cgi_file4 = """\
442#!%s
443import os
444
445print("Content-type: text/html")
Martin Panterad6a99c2016-09-10 10:38:22 +0000446print("")
Martin Pantercff22eb2015-10-03 05:38:07 +0000447
448print(os.environ["%s"])
449"""
450
Charles-François Natali09f87142011-11-02 19:32:54 +0100451
452@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
453 "This test can't be run reliably as root (issue #13308).")
Georg Brandlf899dfa2008-05-18 09:12:20 +0000454class CGIHTTPServerTestCase(BaseTestCase):
455 class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler):
456 pass
457
458 def setUp(self):
459 BaseTestCase.setUp(self)
460 self.parent_dir = tempfile.mkdtemp()
461 self.cgi_dir = os.path.join(self.parent_dir, 'cgi-bin')
Ned Deilyc8937622014-07-12 22:01:15 -0700462 self.cgi_child_dir = os.path.join(self.cgi_dir, 'child-dir')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000463 os.mkdir(self.cgi_dir)
Ned Deilyc8937622014-07-12 22:01:15 -0700464 os.mkdir(self.cgi_child_dir)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000465
Florent Xicluna0805e6e2010-03-22 17:18:18 +0000466 # The shebang line should be pure ASCII: use symlink if possible.
467 # See issue #7668.
468 if hasattr(os, 'symlink'):
469 self.pythonexe = os.path.join(self.parent_dir, 'python')
470 os.symlink(sys.executable, self.pythonexe)
471 else:
472 self.pythonexe = sys.executable
473
Benjamin Peterson1ef959a2013-10-30 12:43:09 -0400474 self.nocgi_path = os.path.join(self.parent_dir, 'nocgi.py')
475 with open(self.nocgi_path, 'w') as fp:
476 fp.write(cgi_file1 % self.pythonexe)
477 os.chmod(self.nocgi_path, 0777)
478
Georg Brandlf899dfa2008-05-18 09:12:20 +0000479 self.file1_path = os.path.join(self.cgi_dir, 'file1.py')
480 with open(self.file1_path, 'w') as file1:
Florent Xicluna0805e6e2010-03-22 17:18:18 +0000481 file1.write(cgi_file1 % self.pythonexe)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000482 os.chmod(self.file1_path, 0777)
483
484 self.file2_path = os.path.join(self.cgi_dir, 'file2.py')
485 with open(self.file2_path, 'w') as file2:
Florent Xicluna0805e6e2010-03-22 17:18:18 +0000486 file2.write(cgi_file2 % self.pythonexe)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000487 os.chmod(self.file2_path, 0777)
488
Ned Deilyc8937622014-07-12 22:01:15 -0700489 self.file3_path = os.path.join(self.cgi_child_dir, 'file3.py')
490 with open(self.file3_path, 'w') as file3:
491 file3.write(cgi_file1 % self.pythonexe)
492 os.chmod(self.file3_path, 0777)
493
Martin Pantercff22eb2015-10-03 05:38:07 +0000494 self.file4_path = os.path.join(self.cgi_dir, 'file4.py')
495 with open(self.file4_path, 'w') as file4:
496 file4.write(cgi_file4 % (self.pythonexe, 'QUERY_STRING'))
497 os.chmod(self.file4_path, 0o777)
498
Georg Brandl7bb16532008-05-20 06:47:31 +0000499 self.cwd = os.getcwd()
Georg Brandlf899dfa2008-05-18 09:12:20 +0000500 os.chdir(self.parent_dir)
501
502 def tearDown(self):
503 try:
Georg Brandl7bb16532008-05-20 06:47:31 +0000504 os.chdir(self.cwd)
Florent Xicluna0805e6e2010-03-22 17:18:18 +0000505 if self.pythonexe != sys.executable:
506 os.remove(self.pythonexe)
Benjamin Peterson1ef959a2013-10-30 12:43:09 -0400507 os.remove(self.nocgi_path)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000508 os.remove(self.file1_path)
509 os.remove(self.file2_path)
Ned Deilyc8937622014-07-12 22:01:15 -0700510 os.remove(self.file3_path)
Martin Pantercff22eb2015-10-03 05:38:07 +0000511 os.remove(self.file4_path)
Ned Deilyc8937622014-07-12 22:01:15 -0700512 os.rmdir(self.cgi_child_dir)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000513 os.rmdir(self.cgi_dir)
514 os.rmdir(self.parent_dir)
515 finally:
516 BaseTestCase.tearDown(self)
517
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800518 def test_url_collapse_path(self):
Senthil Kumaranfb2e8742012-04-11 03:07:57 +0800519 # verify tail is the last portion and head is the rest on proper urls
Gregory P. Smith923ba362009-04-06 06:33:26 +0000520 test_vectors = {
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800521 '': '//',
Gregory P. Smith923ba362009-04-06 06:33:26 +0000522 '..': IndexError,
523 '/.//..': IndexError,
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800524 '/': '//',
525 '//': '//',
526 '/\\': '//\\',
527 '/.//': '//',
528 'cgi-bin/file1.py': '/cgi-bin/file1.py',
529 '/cgi-bin/file1.py': '/cgi-bin/file1.py',
530 'a': '//a',
531 '/a': '//a',
532 '//a': '//a',
533 './a': '//a',
534 './C:/': '/C:/',
535 '/a/b': '/a/b',
536 '/a/b/': '/a/b/',
537 '/a/b/.': '/a/b/',
538 '/a/b/c/..': '/a/b/',
539 '/a/b/c/../d': '/a/b/d',
540 '/a/b/c/../d/e/../f': '/a/b/d/f',
541 '/a/b/c/../d/e/../../f': '/a/b/f',
542 '/a/b/c/../d/e/.././././..//f': '/a/b/f',
Gregory P. Smith923ba362009-04-06 06:33:26 +0000543 '../a/b/c/../d/e/.././././..//f': IndexError,
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800544 '/a/b/c/../d/e/../../../f': '/a/f',
545 '/a/b/c/../d/e/../../../../f': '//f',
Gregory P. Smith923ba362009-04-06 06:33:26 +0000546 '/a/b/c/../d/e/../../../../../f': IndexError,
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800547 '/a/b/c/../d/e/../../../../f/..': '//',
548 '/a/b/c/../d/e/../../../../f/../.': '//',
Gregory P. Smith923ba362009-04-06 06:33:26 +0000549 }
550 for path, expected in test_vectors.iteritems():
551 if isinstance(expected, type) and issubclass(expected, Exception):
552 self.assertRaises(expected,
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800553 CGIHTTPServer._url_collapse_path, path)
Gregory P. Smith923ba362009-04-06 06:33:26 +0000554 else:
Senthil Kumaran5f7e7342012-04-12 02:23:23 +0800555 actual = CGIHTTPServer._url_collapse_path(path)
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000556 self.assertEqual(expected, actual,
557 msg='path = %r\nGot: %r\nWanted: %r' %
558 (path, actual, expected))
Gregory P. Smith923ba362009-04-06 06:33:26 +0000559
Georg Brandlf899dfa2008-05-18 09:12:20 +0000560 def test_headers_and_content(self):
561 res = self.request('/cgi-bin/file1.py')
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000562 self.assertEqual(('Hello World\n', 'text/html', 200),
563 (res.read(), res.getheader('Content-type'), res.status))
Georg Brandlf899dfa2008-05-18 09:12:20 +0000564
Benjamin Peterson1ef959a2013-10-30 12:43:09 -0400565 def test_issue19435(self):
566 res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh')
567 self.assertEqual(res.status, 404)
568
Georg Brandlf899dfa2008-05-18 09:12:20 +0000569 def test_post(self):
570 params = urllib.urlencode({'spam' : 1, 'eggs' : 'python', 'bacon' : 123456})
571 headers = {'Content-type' : 'application/x-www-form-urlencoded'}
572 res = self.request('/cgi-bin/file2.py', 'POST', params, headers)
573
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000574 self.assertEqual(res.read(), '1, python, 123456\n')
Georg Brandlf899dfa2008-05-18 09:12:20 +0000575
576 def test_invaliduri(self):
577 res = self.request('/cgi-bin/invalid')
578 res.read()
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000579 self.assertEqual(res.status, 404)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000580
581 def test_authorization(self):
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000582 headers = {'Authorization' : 'Basic %s' %
583 base64.b64encode('username:pass')}
Georg Brandlf899dfa2008-05-18 09:12:20 +0000584 res = self.request('/cgi-bin/file1.py', 'GET', headers=headers)
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000585 self.assertEqual(('Hello World\n', 'text/html', 200),
586 (res.read(), res.getheader('Content-type'), res.status))
Georg Brandlf899dfa2008-05-18 09:12:20 +0000587
Gregory P. Smith923ba362009-04-06 06:33:26 +0000588 def test_no_leading_slash(self):
589 # http://bugs.python.org/issue2254
590 res = self.request('cgi-bin/file1.py')
Florent Xiclunabc27c6a2010-03-19 18:34:55 +0000591 self.assertEqual(('Hello World\n', 'text/html', 200),
Gregory P. Smith923ba362009-04-06 06:33:26 +0000592 (res.read(), res.getheader('Content-type'), res.status))
593
Senthil Kumarana9bd0cc2010-10-03 18:16:52 +0000594 def test_os_environ_is_not_altered(self):
595 signature = "Test CGI Server"
596 os.environ['SERVER_SOFTWARE'] = signature
597 res = self.request('/cgi-bin/file1.py')
598 self.assertEqual((b'Hello World\n', 'text/html', 200),
599 (res.read(), res.getheader('Content-type'), res.status))
600 self.assertEqual(os.environ['SERVER_SOFTWARE'], signature)
Georg Brandlf899dfa2008-05-18 09:12:20 +0000601
Benjamin Peterson8d24d772014-06-14 18:36:29 -0700602 def test_urlquote_decoding_in_cgi_check(self):
603 res = self.request('/cgi-bin%2ffile1.py')
604 self.assertEqual((b'Hello World\n', 'text/html', 200),
605 (res.read(), res.getheader('Content-type'), res.status))
606
Ned Deilyc8937622014-07-12 22:01:15 -0700607 def test_nested_cgi_path_issue21323(self):
608 res = self.request('/cgi-bin/child-dir/file3.py')
609 self.assertEqual((b'Hello World\n', 'text/html', 200),
610 (res.read(), res.getheader('Content-type'), res.status))
611
Martin Pantercff22eb2015-10-03 05:38:07 +0000612 def test_query_with_multiple_question_mark(self):
613 res = self.request('/cgi-bin/file4.py?a=b?c=d')
614 self.assertEqual(
615 (b'a=b?c=d\n', 'text/html', 200),
616 (res.read(), res.getheader('Content-type'), res.status))
617
Martin Panter74c76c82015-10-03 05:55:46 +0000618 def test_query_with_continuous_slashes(self):
619 res = self.request('/cgi-bin/file4.py?k=aa%2F%2Fbb&//q//p//=//a//b//')
620 self.assertEqual(
621 (b'k=aa%2F%2Fbb&//q//p//=//a//b//\n',
622 'text/html', 200),
623 (res.read(), res.getheader('Content-type'), res.status))
624
Antoine Pitrou47d9b0e2010-12-16 17:11:34 +0000625
Antoine Pitrou47d9b0e2010-12-16 17:11:34 +0000626class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
627 """ Test url parsing """
628 def setUp(self):
629 self.translated = os.getcwd()
630 self.translated = os.path.join(self.translated, 'filename')
631 self.handler = SocketlessRequestHandler()
632
633 def test_query_arguments(self):
634 path = self.handler.translate_path('/filename')
635 self.assertEqual(path, self.translated)
636 path = self.handler.translate_path('/filename?foo=bar')
637 self.assertEqual(path, self.translated)
638 path = self.handler.translate_path('/filename?a=b&spam=eggs#zot')
639 self.assertEqual(path, self.translated)
640
641 def test_start_with_double_slash(self):
642 path = self.handler.translate_path('//filename')
643 self.assertEqual(path, self.translated)
644 path = self.handler.translate_path('//filename?foo=bar')
645 self.assertEqual(path, self.translated)
646
Martin Panter0cf2cf22016-04-18 03:45:18 +0000647 def test_windows_colon(self):
648 import SimpleHTTPServer
649 with test_support.swap_attr(SimpleHTTPServer.os, 'path', ntpath):
650 path = self.handler.translate_path('c:c:c:foo/filename')
651 path = path.replace(ntpath.sep, os.sep)
652 self.assertEqual(path, self.translated)
653
654 path = self.handler.translate_path('\\c:../filename')
655 path = path.replace(ntpath.sep, os.sep)
656 self.assertEqual(path, self.translated)
657
658 path = self.handler.translate_path('c:\\c:..\\foo/filename')
659 path = path.replace(ntpath.sep, os.sep)
660 self.assertEqual(path, self.translated)
661
662 path = self.handler.translate_path('c:c:foo\\c:c:bar/filename')
663 path = path.replace(ntpath.sep, os.sep)
664 self.assertEqual(path, self.translated)
665
Antoine Pitrou47d9b0e2010-12-16 17:11:34 +0000666
Georg Brandlf899dfa2008-05-18 09:12:20 +0000667def test_main(verbose=None):
668 try:
669 cwd = os.getcwd()
Senthil Kumaranb643fc62010-09-30 06:40:56 +0000670 test_support.run_unittest(BaseHTTPRequestHandlerTestCase,
Antoine Pitrou47d9b0e2010-12-16 17:11:34 +0000671 SimpleHTTPRequestHandlerTestCase,
Senthil Kumaranb643fc62010-09-30 06:40:56 +0000672 BaseHTTPServerTestCase,
673 SimpleHTTPServerTestCase,
674 CGIHTTPServerTestCase
675 )
Georg Brandlf899dfa2008-05-18 09:12:20 +0000676 finally:
677 os.chdir(cwd)
678
679if __name__ == '__main__':
680 test_main()