Demian Brecht added the comment:
Latest patch should address all comments. It also fixes the same issue in error
logging which wasn’t previously accounted for. The test file has also been
updated with using HTTPStatus where possible rather than hard coded ints. This
is consistent with other areas in the http package.
----------
Added file: http://bugs.python.org/file38193/issue21793_logfix_3.patch
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue21793>
_______________________________________
diff -r 46bfddb14cbe Lib/http/server.py
--- a/Lib/http/server.py Fri Feb 20 10:34:20 2015 -0500
+++ b/Lib/http/server.py Fri Feb 20 17:37:25 2015 -0800
@@ -442,6 +442,10 @@
message = shortmsg
if explain is None:
explain = longmsg
+
+ if isinstance(code, HTTPStatus):
+ code = code.value
+
self.log_error("code %d, message %s", code, message)
# using _quote_html to prevent Cross Site Scripting attacks (see bug
#1100201)
content = (self.error_message_format %
@@ -517,6 +521,8 @@
This is called by send_response().
"""
+ if isinstance(code, HTTPStatus):
+ code = code.value
self.log_message('"%s" %s %s',
self.requestline, str(code), str(size))
diff -r 46bfddb14cbe Lib/test/test_httpservers.py
--- a/Lib/test/test_httpservers.py Fri Feb 20 10:34:20 2015 -0500
+++ b/Lib/test/test_httpservers.py Fri Feb 20 17:37:25 2015 -0800
@@ -6,7 +6,7 @@
from http.server import BaseHTTPRequestHandler, HTTPServer, \
SimpleHTTPRequestHandler, CGIHTTPRequestHandler
-from http import server
+from http import server, HTTPStatus
import os
import sys
@@ -79,13 +79,13 @@
default_request_version = 'HTTP/1.1'
def do_TEST(self):
- self.send_response(204)
+ self.send_response(HTTPStatus.NO_CONTENT)
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_response(HTTPStatus.NO_CONTENT)
self.send_header('Content-Type', 'text/html')
self.send_header('Connection', 'keep-alive')
self.end_headers()
@@ -94,7 +94,7 @@
self.send_error(999)
def do_NOTFOUND(self):
- self.send_error(404)
+ self.send_error(HTTPStatus.NOT_FOUND)
def do_EXPLAINERROR(self):
self.send_error(999, "Short Message",
@@ -122,35 +122,35 @@
def test_command(self):
self.con.request('GET', '/')
res = self.con.getresponse()
- self.assertEqual(res.status, 501)
+ self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED)
def test_request_line_trimming(self):
self.con._http_vsn_str = 'HTTP/1.1\n'
self.con.putrequest('XYZBOGUS', '/')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 501)
+ self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED)
def test_version_bogus(self):
self.con._http_vsn_str = 'FUBAR'
self.con.putrequest('GET', '/')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 400)
+ self.assertEqual(res.status, HTTPStatus.BAD_REQUEST)
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.assertEqual(res.status, 400)
+ self.assertEqual(res.status, HTTPStatus.BAD_REQUEST)
def test_version_none_get(self):
self.con._http_vsn_str = ''
self.con.putrequest('GET', '/')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 501)
+ self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED)
def test_version_none(self):
# Test that a valid method is rejected when not HTTP/1.x
@@ -158,7 +158,7 @@
self.con.putrequest('CUSTOM', '/')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 400)
+ self.assertEqual(res.status, HTTPStatus.BAD_REQUEST)
def test_version_invalid(self):
self.con._http_vsn = 99
@@ -166,21 +166,21 @@
self.con.putrequest('GET', '/')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 505)
+ self.assertEqual(res.status, HTTPStatus.HTTP_VERSION_NOT_SUPPORTED)
def test_send_blank(self):
self.con._http_vsn_str = ''
self.con.putrequest('', '')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 400)
+ self.assertEqual(res.status, HTTPStatus.BAD_REQUEST)
def test_header_close(self):
self.con.putrequest('GET', '/')
self.con.putheader('Connection', 'close')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 501)
+ self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED)
def test_head_keep_alive(self):
self.con._http_vsn_str = 'HTTP/1.1'
@@ -188,12 +188,12 @@
self.con.putheader('Connection', 'keep-alive')
self.con.endheaders()
res = self.con.getresponse()
- self.assertEqual(res.status, 501)
+ self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED)
def test_handler(self):
self.con.request('TEST', '/')
res = self.con.getresponse()
- self.assertEqual(res.status, 204)
+ self.assertEqual(res.status, HTTPStatus.NO_CONTENT)
def test_return_header_keep_alive(self):
self.con.request('KEEP', '/')
@@ -230,11 +230,48 @@
# Issue #16088: standard error responses should have a content-length
self.con.request('NOTFOUND', '/')
res = self.con.getresponse()
- self.assertEqual(res.status, 404)
+ self.assertEqual(res.status, HTTPStatus.NOT_FOUND)
+
data = res.read()
self.assertEqual(int(res.getheader('Content-Length')), len(data))
+class RequestHandlerLoggingTestCase(BaseTestCase):
+ class request_handler(BaseHTTPRequestHandler):
+ protocol_version = 'HTTP/1.1'
+ default_request_version = 'HTTP/1.1'
+
+ def do_GET(self):
+ self.send_response(HTTPStatus.OK)
+ self.end_headers()
+
+ def do_ERROR(self):
+ self.send_error(HTTPStatus.NOT_FOUND, 'File not found')
+
+ def test_get(self):
+ self.con = http.client.HTTPConnection(self.HOST, self.PORT)
+ self.con.connect()
+
+ with support.captured_stderr() as err:
+ self.con.request('GET', '/')
+ self.con.getresponse()
+
+ self.assertTrue(
+ err.getvalue().endswith('"GET / HTTP/1.1" 200 -\n'))
+
+ def test_err(self):
+ self.con = http.client.HTTPConnection(self.HOST, self.PORT)
+ self.con.connect()
+
+ with support.captured_stderr() as err:
+ self.con.request('ERROR', '/')
+ self.con.getresponse()
+
+ lines = err.getvalue().split('\n')
+ self.assertTrue(lines[0].endswith('code 404, message File not found'))
+ self.assertTrue(lines[1].endswith('"ERROR / HTTP/1.1" 404 -'))
+
+
class SimpleHTTPServerTestCase(BaseTestCase):
class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
pass
@@ -285,52 +322,52 @@
if name != 'test': # Ignore a filename created in setUp().
filename = name
break
- body = self.check_status_and_reason(response, 200)
+ body = self.check_status_and_reason(response, HTTPStatus.OK)
quotedname = urllib.parse.quote(filename, errors='surrogatepass')
self.assertIn(('href="%s"' % quotedname)
.encode(enc, 'surrogateescape'), body)
self.assertIn(('>%s<' % html.escape(filename))
.encode(enc, 'surrogateescape'), body)
response = self.request(self.tempdir_name + '/' + quotedname)
- self.check_status_and_reason(response, 200,
+ self.check_status_and_reason(response, HTTPStatus.OK,
data=support.TESTFN_UNDECODABLE)
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)
+ self.check_status_and_reason(response, HTTPStatus.OK, data=self.data)
# check for trailing "/" which should return 404. See Issue17324
response = self.request(self.tempdir_name + '/test/')
- self.check_status_and_reason(response, 404)
+ self.check_status_and_reason(response, HTTPStatus.NOT_FOUND)
response = self.request(self.tempdir_name + '/')
- self.check_status_and_reason(response, 200)
+ self.check_status_and_reason(response, HTTPStatus.OK)
response = self.request(self.tempdir_name)
- self.check_status_and_reason(response, 301)
+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY)
response = self.request(self.tempdir_name + '/?hi=2')
- self.check_status_and_reason(response, 200)
+ self.check_status_and_reason(response, HTTPStatus.OK)
response = self.request(self.tempdir_name + '?hi=1')
- self.check_status_and_reason(response, 301)
+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY)
self.assertEqual(response.getheader("Location"),
self.tempdir_name + "/?hi=1")
response = self.request('/ThisDoesNotExist')
- self.check_status_and_reason(response, 404)
+ self.check_status_and_reason(response, HTTPStatus.NOT_FOUND)
response = self.request('/' + 'ThisDoesNotExist' + '/')
- self.check_status_and_reason(response, 404)
+ self.check_status_and_reason(response, HTTPStatus.NOT_FOUND)
with open(os.path.join(self.tempdir_name, 'index.html'), 'w') as f:
response = self.request('/' + self.tempdir_name + '/')
- self.check_status_and_reason(response, 200)
+ self.check_status_and_reason(response, HTTPStatus.OK)
# chmod() doesn't work as expected on Windows, and filesystem
# permissions are ignored by root on Unix.
if os.name == 'posix' and os.geteuid() != 0:
os.chmod(self.tempdir, 0)
response = self.request(self.tempdir_name + '/')
- self.check_status_and_reason(response, 404)
+ self.check_status_and_reason(response, HTTPStatus.NOT_FOUND)
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.check_status_and_reason(response, HTTPStatus.OK)
self.assertEqual(response.getheader('content-length'),
str(len(self.data)))
self.assertEqual(response.getheader('content-type'),
@@ -338,12 +375,12 @@
def test_invalid_requests(self):
response = self.request('/', method='FOO')
- self.check_status_and_reason(response, 501)
+ self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED)
# requests must be case sensitive,so this should fail too
response = self.request('/', method='custom')
- self.check_status_and_reason(response, 501)
+ self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED)
response = self.request('/', method='GETs')
- self.check_status_and_reason(response, 501)
+ self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED)
cgi_file1 = """\
@@ -490,12 +527,13 @@
def test_headers_and_content(self):
res = self.request('/cgi-bin/file1.py')
- self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
(res.read(), res.getheader('Content-type'), res.status))
def test_issue19435(self):
res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh')
- self.assertEqual(res.status, 404)
+ self.assertEqual(res.status, HTTPStatus.NOT_FOUND)
def test_post(self):
params = urllib.parse.urlencode(
@@ -508,38 +546,43 @@
def test_invaliduri(self):
res = self.request('/cgi-bin/invalid')
res.read()
- self.assertEqual(res.status, 404)
+ self.assertEqual(res.status, HTTPStatus.NOT_FOUND)
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.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
- (res.read(), res.getheader('Content-type'), res.status))
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
+ (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.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
- (res.read(), res.getheader('Content-type'), res.status))
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
+ (res.read(), res.getheader('Content-type'), res.status))
def test_os_environ_is_not_altered(self):
signature = "Test CGI Server"
os.environ['SERVER_SOFTWARE'] = signature
res = self.request('/cgi-bin/file1.py')
- self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
- (res.read(), res.getheader('Content-type'), res.status))
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
+ (res.read(), res.getheader('Content-type'), res.status))
self.assertEqual(os.environ['SERVER_SOFTWARE'], signature)
def test_urlquote_decoding_in_cgi_check(self):
res = self.request('/cgi-bin%2ffile1.py')
- self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
- (res.read(), res.getheader('Content-type'), res.status))
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
+ (res.read(), res.getheader('Content-type'), res.status))
def test_nested_cgi_path_issue21323(self):
res = self.request('/cgi-bin/child-dir/file3.py')
- self.assertEqual((b'Hello World' + self.linesep, 'text/html', 200),
- (res.read(), res.getheader('Content-type'), res.status))
+ self.assertEqual(
+ (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
+ (res.read(), res.getheader('Content-type'), res.status))
class SocketlessRequestHandler(SimpleHTTPRequestHandler):
@@ -549,7 +592,7 @@
def do_GET(self):
self.get_called = True
- self.send_response(200)
+ self.send_response(HTTPStatus.OK)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(b'<html><body>Data</body></html>\r\n')
@@ -559,7 +602,7 @@
class RejectingSocketlessRequestHandler(SocketlessRequestHandler):
def handle_expect_100(self):
- self.send_error(417)
+ self.send_error(HTTPStatus.EXPECTATION_FAILED)
return False
@@ -816,6 +859,7 @@
cwd = os.getcwd()
try:
support.run_unittest(
+ RequestHandlerLoggingTestCase,
BaseHTTPRequestHandlerTestCase,
BaseHTTPServerTestCase,
SimpleHTTPServerTestCase,
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com