[docs] Document more BaseHTTPRequestHandler attributes (issue 23410)
vadmium+py at gmail.com
vadmium+py at gmail.com
Sun Feb 15 04:03:34 CET 2015
Reviewers: demian,
https://bugs.python.org/review/23410/diff/13865/Doc/library/http.server.rst
File Doc/library/http.server.rst (right):
https://bugs.python.org/review/23410/diff/13865/Doc/library/http.server.rst#newcode79
Doc/library/http.server.rst:79: public methods, even if no actual line
has been parsed from
On 2015/02/14 02:35:35, demian wrote:
> I may be misinterpreting either what you're saying here or what's
actually going
> on in server.py, but it seems to me that if a line isn't parsed from
the
> request, the connection is simply closed and no further methods are
called. From
> what you have here, it sounds more like server execution will continue
even if
> there isn't a request line parsed.
The basic implementation actually sets requestline = "" and sends a
REQUEST_URI_TOO_LONG (414) response in one case, before dropping the
connection. In any case, the log_request() method requires requestline
to be set, and it is called by send_response(). I will try to explain
this better in an updated patch.
Please review this at https://bugs.python.org/review/23410/
Affected files:
Doc/library/http.server.rst
Lib/http/server.py
Lib/test/test_httpservers.py
# HG changeset patch
# Parent 5ea093a41198b7ca4d8fc4f8d626cbf2aacc81ed
diff -r 5ea093a41198 Doc/library/http.server.rst
--- a/Doc/library/http.server.rst Sat Feb 07 22:20:48 2015 -0800
+++ b/Doc/library/http.server.rst Sun Feb 08 10:17:45 2015 +0000
@@ -65,6 +65,20 @@
Contains the server instance.
+ .. attribute:: close_connection
+
+ Flag that should be set before :meth:`handle_one_request` returns,
+ indicating if another request may be expected, or if the connection
+ should be shut down.
+
+ .. attribute:: requestline
+
+ Normally contains the HTTP request line except for the
+ terminating CRLF, as a text string. This is always set by the
+ :meth:`handle_one_request` method before it invokes other
+ public methods, even if no actual line has been parsed from
+ the request.
+
.. attribute:: command
Contains the command (request type). For example, ``'GET'``.
diff -r 5ea093a41198 Lib/http/server.py
--- a/Lib/http/server.py Sat Feb 07 22:20:48 2015 -0800
+++ b/Lib/http/server.py Sun Feb 08 10:17:45 2015 +0000
@@ -272,7 +272,7 @@
"""
self.command = None # set in case of error on the first line
self.request_version = version = self.default_request_version
- self.close_connection = 1
+ self.close_connection = True
requestline = str(self.raw_requestline, 'iso-8859-1')
requestline = requestline.rstrip('\r\n')
self.requestline = requestline
@@ -302,7 +302,7 @@
"Bad request version (%r)" % version)
return False
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
- self.close_connection = 0
+ self.close_connection = False
if version_number >= (2, 0):
self.send_error(
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
@@ -310,7 +310,7 @@
return False
elif len(words) == 2:
command, path = words
- self.close_connection = 1
+ self.close_connection = True
if command != 'GET':
self.send_error(
HTTPStatus.BAD_REQUEST,
@@ -337,10 +337,10 @@
conntype = self.headers.get('Connection', "")
if conntype.lower() == 'close':
- self.close_connection = 1
+ self.close_connection = True
elif (conntype.lower() == 'keep-alive' and
self.protocol_version >= "HTTP/1.1"):
- self.close_connection = 0
+ self.close_connection = False
# Examine the headers and look for an Expect directive
expect = self.headers.get('Expect', "")
if (expect.lower() == "100-continue" and
@@ -385,7 +385,7 @@
self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
return
if not self.raw_requestline:
- self.close_connection = 1
+ self.close_connection = True
return
if not self.parse_request():
# An error code has been sent, just exit
@@ -402,12 +402,12 @@
except socket.timeout as e:
#a read or a write timed out. Discard this connection
self.log_error("Request timed out: %r", e)
- self.close_connection = 1
+ self.close_connection = True
return
def handle(self):
"""Handle multiple requests if necessary."""
- self.close_connection = 1
+ self.close_connection = True
self.handle_one_request()
while not self.close_connection:
@@ -493,9 +493,9 @@
if keyword.lower() == 'connection':
if value.lower() == 'close':
- self.close_connection = 1
+ self.close_connection = True
elif value.lower() == 'keep-alive':
- self.close_connection = 0
+ self.close_connection = False
def end_headers(self):
"""Send the blank line ending the MIME headers."""
diff -r 5ea093a41198 Lib/test/test_httpservers.py
--- a/Lib/test/test_httpservers.py Sat Feb 07 22:20:48 2015 -0800
+++ b/Lib/test/test_httpservers.py Sun Feb 08 10:17:45 2015 +0000
@@ -616,6 +616,11 @@
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+ self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1')
+ self.assertEqual(self.handler.command, 'GET')
+ self.assertEqual(self.handler.path, '/')
+ self.assertEqual(self.handler.request_version, 'HTTP/1.1')
+ self.assertSequenceEqual(self.handler.headers.values(), ())
def test_http_1_0(self):
result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n')
@@ -623,6 +628,11 @@
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+ self.assertEqual(self.handler.requestline, 'GET / HTTP/1.0')
+ self.assertEqual(self.handler.command, 'GET')
+ self.assertEqual(self.handler.path, '/')
+ self.assertEqual(self.handler.request_version, 'HTTP/1.0')
+ self.assertSequenceEqual(self.handler.headers.values(), ())
def test_http_0_9(self):
result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n')
@@ -636,6 +646,12 @@
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+ self.assertEqual(self.handler.requestline, 'GET / HTTP/1.0')
+ self.assertEqual(self.handler.command, 'GET')
+ self.assertEqual(self.handler.path, '/')
+ self.assertEqual(self.handler.request_version, 'HTTP/1.0')
+ headers = (("Expect", "100-continue"),)
+ self.assertSequenceEqual(self.handler.headers.items(), headers)
def test_with_continue_1_1(self):
result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n')
@@ -645,6 +661,12 @@
self.verify_expected_headers(result[2:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+ self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1')
+ self.assertEqual(self.handler.command, 'GET')
+ self.assertEqual(self.handler.path, '/')
+ self.assertEqual(self.handler.request_version, 'HTTP/1.1')
+ headers = (("Expect", "100-continue"),)
+ self.assertSequenceEqual(self.handler.headers.items(), headers)
def test_header_buffering_of_send_error(self):
@@ -730,6 +752,7 @@
result = self.send_typical_request(b'GET ' + b'x' * 65537)
self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
self.assertFalse(self.handler.get_called)
+ self.assertIsInstance(self.handler.requestline, str)
def test_header_length(self):
# Issue #6791: same for headers
@@ -737,6 +760,22 @@
b'GET / HTTP/1.1\r\nX-Foo: bar' + b'r' * 65537 + b'\r\n\r\n')
self.assertEqual(result[0], b'HTTP/1.1 400 Line too long\r\n')
self.assertFalse(self.handler.get_called)
+ self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1')
+
+ def test_close_connection(self):
+ # handle_one_request() should be repeatedly called until
+ # it sets close_connection
+ def handle_one_request():
+ self.handler.close_connection = next(exit_values)
+ self.handler.handle_one_request = handle_one_request
+
+ exit_values = iter((True,))
+ self.handler.handle()
+ self.assertRaises(StopIteration, next, exit_values)
+
+ exit_values = iter((False, False, True))
+ self.handler.handle()
+ self.assertRaises(StopIteration, next, exit_values)
class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
""" Test url parsing """
More information about the docs
mailing list