# 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 15 03:25:59 2015 +0000 @@ -65,6 +65,21 @@ 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. If no actual line has been received or parsed, + it is set to the empty string, and sending a response will log an + empty request line. + .. 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 15 03:25:59 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 15 03:25:59 2015 +0000 @@ -616,6 +616,11 @@ self.verify_expected_headers(result[1:-1]) self.verify_get_called() self.assertEqual(result[-1], b'Data\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.items(), ()) 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'Data\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.items(), ()) 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'Data\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'Data\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(close_values) + self.handler.handle_one_request = handle_one_request + + close_values = iter((True,)) + self.handler.handle() + self.assertRaises(StopIteration, next, close_values) + + close_values = iter((False, False, True)) + self.handler.handle() + self.assertRaises(StopIteration, next, close_values) class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): """ Test url parsing """