mirror of
https://github.com/python/cpython.git
synced 2026-05-09 22:20:38 -04:00
6ead552b47
files are unchanged in this checkin. This checkin is also the first time the environment checking in regrtest has been forward ported to the Py3k branch. This checkin causes test_xmlrpc to fail - see issue 7165 (it's a bug in the 3.x version of xmlrpc.server) I am also getting a failure in test_telnetlib, but it isn't clear yet if that is due to these changes. Recorded merge of revisions 75400-75401,75404,75406,75414,75416,75422,75425-75428,75435,75439,75441-75444,75447-75449,75451-75453,75455-75458,75460-75469,75471-75473,75475-75477,75479-75481,75483,75486-75489 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75400 | r.david.murray | 2009-10-14 23:58:07 +1000 (Wed, 14 Oct 2009) | 6 lines Enhanced Issue 7058 patch, which will not be backported. Refactors the code, adds checks for stdin/out/err, cwd, and sys.path, and adds a new section in the summary for tests that modify the environment (thanks to Ezio Melotti for that suggestion). ........ r75453 | nick.coghlan | 2009-10-17 16:33:05 +1000 (Sat, 17 Oct 2009) | 1 line Correctly restore sys.stdout in test_descr ........ r75456 | nick.coghlan | 2009-10-17 17:30:40 +1000 (Sat, 17 Oct 2009) | 1 line Enhancement to the new environment checking code to print the changed items under -vv. Also includes a small tweak to allow underscores in the names of resources. ........ r75457 | nick.coghlan | 2009-10-17 17:34:27 +1000 (Sat, 17 Oct 2009) | 1 line Formatting tweak so that before and after values are vertically aligned ........ r75458 | nick.coghlan | 2009-10-17 18:21:21 +1000 (Sat, 17 Oct 2009) | 1 line Check and revert expected sys.path alterations ........ r75461 | nick.coghlan | 2009-10-18 00:40:54 +1000 (Sun, 18 Oct 2009) | 1 line Restore original sys.path when running TTK tests ........ r75462 | nick.coghlan | 2009-10-18 01:09:41 +1000 (Sun, 18 Oct 2009) | 1 line Don't invoke reload(sys) and use StringIO objects instead of real files to capture stdin and stdout when needed (ensures all sys attributes remain unmodified after test_xmlrpc runs) ........ r75463 | nick.coghlan | 2009-10-18 01:23:08 +1000 (Sun, 18 Oct 2009) | 1 line Revert changes made to environment in test_httpservers ........ r75465 | nick.coghlan | 2009-10-18 01:45:52 +1000 (Sun, 18 Oct 2009) | 1 line Move restoration of the os.environ object into the context manager where it belongs ........ r75466 | nick.coghlan | 2009-10-18 01:48:16 +1000 (Sun, 18 Oct 2009) | 1 line Also check and restore identity of sys.path, sys.argv and os.environ rather than just their values (this picked up a few more misbehaving tests) ........ r75467 | nick.coghlan | 2009-10-18 01:57:42 +1000 (Sun, 18 Oct 2009) | 1 line Avoid replacing existing modules and sys.path in import tests ........ r75468 | nick.coghlan | 2009-10-18 02:19:51 +1000 (Sun, 18 Oct 2009) | 1 line Don't replace sys.path in test_site ........ r75481 | nick.coghlan | 2009-10-18 15:38:48 +1000 (Sun, 18 Oct 2009) | 1 line Using CleanImport to revert a reload of the os module doesn't work due to function registrations in copy_reg. The perils of reloading modules even for tests... ........ r75486 | nick.coghlan | 2009-10-18 20:29:10 +1000 (Sun, 18 Oct 2009) | 1 line Silence a deprecation warning by using the appropriate replacement construct ........ r75489 | nick.coghlan | 2009-10-18 20:56:21 +1000 (Sun, 18 Oct 2009) | 1 line Restore sys.path in test_tk ........
405 lines
13 KiB
Python
405 lines
13 KiB
Python
"""Unittests for the various HTTPServer modules.
|
|
|
|
Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
|
|
Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
|
|
"""
|
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer, \
|
|
SimpleHTTPRequestHandler, CGIHTTPRequestHandler
|
|
from http import server
|
|
|
|
import os
|
|
import sys
|
|
import base64
|
|
import shutil
|
|
import urllib.parse
|
|
import http.client
|
|
import tempfile
|
|
import threading
|
|
|
|
import unittest
|
|
from test import support
|
|
|
|
class NoLogRequestHandler:
|
|
def log_message(self, *args):
|
|
# don't write log messages to stderr
|
|
pass
|
|
|
|
def read(self, n=None):
|
|
return ''
|
|
|
|
|
|
class TestServerThread(threading.Thread):
|
|
def __init__(self, test_object, request_handler):
|
|
threading.Thread.__init__(self)
|
|
self.request_handler = request_handler
|
|
self.test_object = test_object
|
|
self.test_object.lock.acquire()
|
|
|
|
def run(self):
|
|
self.server = HTTPServer(('', 0), self.request_handler)
|
|
self.test_object.PORT = self.server.socket.getsockname()[1]
|
|
self.test_object.lock.release()
|
|
try:
|
|
self.server.serve_forever()
|
|
finally:
|
|
self.server.server_close()
|
|
|
|
def stop(self):
|
|
self.server.shutdown()
|
|
|
|
|
|
class BaseTestCase(unittest.TestCase):
|
|
def setUp(self):
|
|
os.environ = support.EnvironmentVarGuard()
|
|
self.lock = threading.Lock()
|
|
self.thread = TestServerThread(self, self.request_handler)
|
|
self.thread.start()
|
|
self.lock.acquire()
|
|
|
|
def tearDown(self):
|
|
self.lock.release()
|
|
self.thread.stop()
|
|
os.environ.__exit__()
|
|
|
|
def request(self, uri, method='GET', body=None, headers={}):
|
|
self.connection = http.client.HTTPConnection('localhost', self.PORT)
|
|
self.connection.request(method, uri, body, headers)
|
|
return self.connection.getresponse()
|
|
|
|
|
|
class BaseHTTPServerTestCase(BaseTestCase):
|
|
class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
|
|
protocol_version = 'HTTP/1.1'
|
|
default_request_version = 'HTTP/1.1'
|
|
|
|
def do_TEST(self):
|
|
self.send_response(204)
|
|
self.send_header('Content-Type', 'text/html')
|
|
self.send_header('Connection', 'close')
|
|
self.end_headers()
|
|
|
|
def do_KEEP(self):
|
|
self.send_response(204)
|
|
self.send_header('Content-Type', 'text/html')
|
|
self.send_header('Connection', 'keep-alive')
|
|
self.end_headers()
|
|
|
|
def do_KEYERROR(self):
|
|
self.send_error(999)
|
|
|
|
def do_CUSTOM(self):
|
|
self.send_response(999)
|
|
self.send_header('Content-Type', 'text/html')
|
|
self.send_header('Connection', 'close')
|
|
self.end_headers()
|
|
|
|
def setUp(self):
|
|
BaseTestCase.setUp(self)
|
|
self.con = http.client.HTTPConnection('localhost', self.PORT)
|
|
self.con.connect()
|
|
|
|
def test_command(self):
|
|
self.con.request('GET', '/')
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 501)
|
|
|
|
def test_request_line_trimming(self):
|
|
self.con._http_vsn_str = 'HTTP/1.1\n'
|
|
self.con.putrequest('GET', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 501)
|
|
|
|
def test_version_bogus(self):
|
|
self.con._http_vsn_str = 'FUBAR'
|
|
self.con.putrequest('GET', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 400)
|
|
|
|
def test_version_digits(self):
|
|
self.con._http_vsn_str = 'HTTP/9.9.9'
|
|
self.con.putrequest('GET', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 400)
|
|
|
|
def test_version_none_get(self):
|
|
self.con._http_vsn_str = ''
|
|
self.con.putrequest('GET', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 501)
|
|
|
|
def test_version_none(self):
|
|
self.con._http_vsn_str = ''
|
|
self.con.putrequest('PUT', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 400)
|
|
|
|
def test_version_invalid(self):
|
|
self.con._http_vsn = 99
|
|
self.con._http_vsn_str = 'HTTP/9.9'
|
|
self.con.putrequest('GET', '/')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 505)
|
|
|
|
def test_send_blank(self):
|
|
self.con._http_vsn_str = ''
|
|
self.con.putrequest('', '')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 400)
|
|
|
|
def test_header_close(self):
|
|
self.con.putrequest('GET', '/')
|
|
self.con.putheader('Connection', 'close')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 501)
|
|
|
|
def test_head_keep_alive(self):
|
|
self.con._http_vsn_str = 'HTTP/1.1'
|
|
self.con.putrequest('GET', '/')
|
|
self.con.putheader('Connection', 'keep-alive')
|
|
self.con.endheaders()
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 501)
|
|
|
|
def test_handler(self):
|
|
self.con.request('TEST', '/')
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 204)
|
|
|
|
def test_return_header_keep_alive(self):
|
|
self.con.request('KEEP', '/')
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.getheader('Connection'), 'keep-alive')
|
|
self.con.request('TEST', '/')
|
|
|
|
def test_internal_key_error(self):
|
|
self.con.request('KEYERROR', '/')
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 999)
|
|
|
|
def test_return_custom_status(self):
|
|
self.con.request('CUSTOM', '/')
|
|
res = self.con.getresponse()
|
|
self.assertEquals(res.status, 999)
|
|
|
|
|
|
class SimpleHTTPServerTestCase(BaseTestCase):
|
|
class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
|
|
pass
|
|
|
|
def setUp(self):
|
|
BaseTestCase.setUp(self)
|
|
self.cwd = os.getcwd()
|
|
basetempdir = tempfile.gettempdir()
|
|
os.chdir(basetempdir)
|
|
self.data = b'We are the knights who say Ni!'
|
|
self.tempdir = tempfile.mkdtemp(dir=basetempdir)
|
|
self.tempdir_name = os.path.basename(self.tempdir)
|
|
temp = open(os.path.join(self.tempdir, 'test'), 'wb')
|
|
temp.write(self.data)
|
|
temp.close()
|
|
|
|
def tearDown(self):
|
|
try:
|
|
os.chdir(self.cwd)
|
|
try:
|
|
shutil.rmtree(self.tempdir)
|
|
except:
|
|
pass
|
|
finally:
|
|
BaseTestCase.tearDown(self)
|
|
|
|
def check_status_and_reason(self, response, status, data=None):
|
|
body = response.read()
|
|
self.assertTrue(response)
|
|
self.assertEquals(response.status, status)
|
|
self.assertTrue(response.reason != None)
|
|
if data:
|
|
self.assertEqual(data, body)
|
|
|
|
def test_get(self):
|
|
#constructs the path relative to the root directory of the HTTPServer
|
|
response = self.request(self.tempdir_name + '/test')
|
|
self.check_status_and_reason(response, 200, data=self.data)
|
|
response = self.request(self.tempdir_name + '/')
|
|
self.check_status_and_reason(response, 200)
|
|
response = self.request(self.tempdir_name)
|
|
self.check_status_and_reason(response, 301)
|
|
response = self.request('/ThisDoesNotExist')
|
|
self.check_status_and_reason(response, 404)
|
|
response = self.request('/' + 'ThisDoesNotExist' + '/')
|
|
self.check_status_and_reason(response, 404)
|
|
f = open(os.path.join(self.tempdir_name, 'index.html'), 'w')
|
|
response = self.request('/' + self.tempdir_name + '/')
|
|
self.check_status_and_reason(response, 200)
|
|
if os.name == 'posix':
|
|
# chmod won't work as expected on Windows platforms
|
|
os.chmod(self.tempdir, 0)
|
|
response = self.request(self.tempdir_name + '/')
|
|
self.check_status_and_reason(response, 404)
|
|
os.chmod(self.tempdir, 0o755)
|
|
|
|
def test_head(self):
|
|
response = self.request(
|
|
self.tempdir_name + '/test', method='HEAD')
|
|
self.check_status_and_reason(response, 200)
|
|
self.assertEqual(response.getheader('content-length'),
|
|
str(len(self.data)))
|
|
self.assertEqual(response.getheader('content-type'),
|
|
'application/octet-stream')
|
|
|
|
def test_invalid_requests(self):
|
|
response = self.request('/', method='FOO')
|
|
self.check_status_and_reason(response, 501)
|
|
# requests must be case sensitive,so this should fail too
|
|
response = self.request('/', method='get')
|
|
self.check_status_and_reason(response, 501)
|
|
response = self.request('/', method='GETs')
|
|
self.check_status_and_reason(response, 501)
|
|
|
|
|
|
cgi_file1 = """\
|
|
#!%s
|
|
|
|
print("Content-type: text/html")
|
|
print()
|
|
print("Hello World")
|
|
"""
|
|
|
|
cgi_file2 = """\
|
|
#!%s
|
|
import cgi
|
|
|
|
print("Content-type: text/html")
|
|
print()
|
|
|
|
form = cgi.FieldStorage()
|
|
print("%%s, %%s, %%s" %% (form.getfirst("spam"), form.getfirst("eggs"),\
|
|
form.getfirst("bacon")))
|
|
"""
|
|
|
|
class CGIHTTPServerTestCase(BaseTestCase):
|
|
class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler):
|
|
pass
|
|
|
|
def setUp(self):
|
|
BaseTestCase.setUp(self)
|
|
self.parent_dir = tempfile.mkdtemp()
|
|
self.cgi_dir = os.path.join(self.parent_dir, 'cgi-bin')
|
|
os.mkdir(self.cgi_dir)
|
|
|
|
self.file1_path = os.path.join(self.cgi_dir, 'file1.py')
|
|
with open(self.file1_path, 'w') as file1:
|
|
file1.write(cgi_file1 % sys.executable)
|
|
os.chmod(self.file1_path, 0o777)
|
|
|
|
self.file2_path = os.path.join(self.cgi_dir, 'file2.py')
|
|
with open(self.file2_path, 'w') as file2:
|
|
file2.write(cgi_file2 % sys.executable)
|
|
os.chmod(self.file2_path, 0o777)
|
|
|
|
self.cwd = os.getcwd()
|
|
os.chdir(self.parent_dir)
|
|
|
|
def tearDown(self):
|
|
try:
|
|
os.chdir(self.cwd)
|
|
os.remove(self.file1_path)
|
|
os.remove(self.file2_path)
|
|
os.rmdir(self.cgi_dir)
|
|
os.rmdir(self.parent_dir)
|
|
finally:
|
|
BaseTestCase.tearDown(self)
|
|
|
|
def test_url_collapse_path_split(self):
|
|
test_vectors = {
|
|
'': ('/', ''),
|
|
'..': IndexError,
|
|
'/.//..': IndexError,
|
|
'/': ('/', ''),
|
|
'//': ('/', ''),
|
|
'/\\': ('/', '\\'),
|
|
'/.//': ('/', ''),
|
|
'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
|
|
'/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
|
|
'a': ('/', 'a'),
|
|
'/a': ('/', 'a'),
|
|
'//a': ('/', 'a'),
|
|
'./a': ('/', 'a'),
|
|
'./C:/': ('/C:', ''),
|
|
'/a/b': ('/a', 'b'),
|
|
'/a/b/': ('/a/b', ''),
|
|
'/a/b/c/..': ('/a/b', ''),
|
|
'/a/b/c/../d': ('/a/b', 'd'),
|
|
'/a/b/c/../d/e/../f': ('/a/b/d', 'f'),
|
|
'/a/b/c/../d/e/../../f': ('/a/b', 'f'),
|
|
'/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'),
|
|
'../a/b/c/../d/e/.././././..//f': IndexError,
|
|
'/a/b/c/../d/e/../../../f': ('/a', 'f'),
|
|
'/a/b/c/../d/e/../../../../f': ('/', 'f'),
|
|
'/a/b/c/../d/e/../../../../../f': IndexError,
|
|
'/a/b/c/../d/e/../../../../f/..': ('/', ''),
|
|
}
|
|
for path, expected in test_vectors.items():
|
|
if isinstance(expected, type) and issubclass(expected, Exception):
|
|
self.assertRaises(expected,
|
|
server._url_collapse_path_split, path)
|
|
else:
|
|
actual = server._url_collapse_path_split(path)
|
|
self.assertEquals(expected, actual,
|
|
msg='path = %r\nGot: %r\nWanted: %r' % (
|
|
path, actual, expected))
|
|
|
|
def test_headers_and_content(self):
|
|
res = self.request('/cgi-bin/file1.py')
|
|
self.assertEquals((b'Hello World\n', 'text/html', 200), \
|
|
(res.read(), res.getheader('Content-type'), res.status))
|
|
|
|
def test_post(self):
|
|
params = urllib.parse.urlencode(
|
|
{'spam' : 1, 'eggs' : 'python', 'bacon' : 123456})
|
|
headers = {'Content-type' : 'application/x-www-form-urlencoded'}
|
|
res = self.request('/cgi-bin/file2.py', 'POST', params, headers)
|
|
|
|
self.assertEquals(res.read(), b'1, python, 123456\n')
|
|
|
|
def test_invaliduri(self):
|
|
res = self.request('/cgi-bin/invalid')
|
|
res.read()
|
|
self.assertEquals(res.status, 404)
|
|
|
|
def test_authorization(self):
|
|
headers = {b'Authorization' : b'Basic ' +
|
|
base64.b64encode(b'username:pass')}
|
|
res = self.request('/cgi-bin/file1.py', 'GET', headers=headers)
|
|
self.assertEquals((b'Hello World\n', 'text/html', 200), \
|
|
(res.read(), res.getheader('Content-type'), res.status))
|
|
|
|
def test_no_leading_slash(self):
|
|
# http://bugs.python.org/issue2254
|
|
res = self.request('cgi-bin/file1.py')
|
|
self.assertEquals((b'Hello World\n', 'text/html', 200),
|
|
(res.read(), res.getheader('Content-type'), res.status))
|
|
|
|
|
|
def test_main(verbose=None):
|
|
try:
|
|
cwd = os.getcwd()
|
|
support.run_unittest(BaseHTTPServerTestCase,
|
|
SimpleHTTPServerTestCase,
|
|
CGIHTTPServerTestCase
|
|
)
|
|
finally:
|
|
os.chdir(cwd)
|
|
|
|
if __name__ == '__main__':
|
|
test_main()
|