summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/http.server.rst21
-rw-r--r--Lib/http/server.py9
-rw-r--r--Lib/test/test_httpservers.py43
-rw-r--r--Misc/NEWS4
4 files changed, 68 insertions, 9 deletions
diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst
index 2f28012..f68d220 100644
--- a/Doc/library/http.server.rst
+++ b/Doc/library/http.server.rst
@@ -182,22 +182,29 @@ of which this module provides three different variants:
.. method:: send_header(keyword, value)
- Writes a specific HTTP header to the output stream. *keyword* should
- specify the header keyword, with *value* specifying its value.
+ Stores the HTTP header to an internal buffer which will be written to the
+ output stream when :meth:`end_headers` method is invoked.
+ *keyword* should specify the header keyword, with *value*
+ specifying its value.
+
+ .. versionchanged:: 3.2 Storing the headers in an internal buffer
+
.. method:: send_response_only(code, message=None)
Sends the reponse header only, used for the purposes when ``100
- Continue`` response is sent by the server to the client. If the *message*
- is not specified, the HTTP message corresponding the response *code* is
- sent.
+ Continue`` response is sent by the server to the client. The headers not
+ buffered and sent directly the output stream.If the *message* is not
+ specified, the HTTP message corresponding the response *code* is sent.
.. versionadded:: 3.2
.. method:: end_headers()
- Sends a blank line, indicating the end of the HTTP headers in the
- response.
+ Write the buffered HTTP headers to the output stream and send a blank
+ line, indicating the end of the HTTP headers in the response.
+
+ .. versionchanged:: 3.2 Writing the buffered headers to the output stream.
.. method:: log_request(code='-', size='-')
diff --git a/Lib/http/server.py b/Lib/http/server.py
index 8a6ba70..5ebfd25 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -443,7 +443,10 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
def send_header(self, keyword, value):
"""Send a MIME header."""
if self.request_version != 'HTTP/0.9':
- self.wfile.write(("%s: %s\r\n" % (keyword, value)).encode('ASCII', 'strict'))
+ if not hasattr(self, '_headers_buffer'):
+ self._headers_buffer = []
+ self._headers_buffer.append(
+ ("%s: %s\r\n" % (keyword, value)).encode('ASCII', 'strict'))
if keyword.lower() == 'connection':
if value.lower() == 'close':
@@ -454,7 +457,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
def end_headers(self):
"""Send the blank line ending the MIME headers."""
if self.request_version != 'HTTP/0.9':
- self.wfile.write(b"\r\n")
+ self._headers_buffer.append(b"\r\n")
+ self.wfile.write(b"".join(self._headers_buffer))
+ self._headers_buffer = []
def log_request(self, code='-', size='-'):
"""Log an accepted request.
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
index daf37b2..86ce912 100644
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -511,6 +511,49 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+ def test_header_buffering(self):
+
+ def _readAndReseek(f):
+ pos = f.tell()
+ f.seek(0)
+ data = f.read()
+ f.seek(pos)
+ return data
+
+ input = BytesIO(b'GET / HTTP/1.1\r\n\r\n')
+ output = BytesIO()
+ self.handler.rfile = input
+ self.handler.wfile = output
+ self.handler.request_version = 'HTTP/1.1'
+
+ self.handler.send_header('Foo', 'foo')
+ self.handler.send_header('bar', 'bar')
+ self.assertEqual(_readAndReseek(output), b'')
+ self.handler.end_headers()
+ self.assertEqual(_readAndReseek(output),
+ b'Foo: foo\r\nbar: bar\r\n\r\n')
+
+ def test_header_unbuffered_when_continue(self):
+
+ def _readAndReseek(f):
+ pos = f.tell()
+ f.seek(0)
+ data = f.read()
+ f.seek(pos)
+ return data
+
+ input = BytesIO(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n')
+ output = BytesIO()
+ self.handler.rfile = input
+ self.handler.wfile = output
+ self.handler.request_version = 'HTTP/1.1'
+
+ self.handler.handle_one_request()
+ self.assertNotEqual(_readAndReseek(output), b'')
+ result = _readAndReseek(output).split(b'\r\n')
+ self.assertEqual(result[0], b'HTTP/1.1 100 Continue')
+ self.assertEqual(result[1], b'HTTP/1.1 200 OK')
+
def test_with_continue_rejected(self):
usual_handler = self.handler # Save to avoid breaking any subsequent tests.
self.handler = RejectingSocketlessRequestHandler()
diff --git a/Misc/NEWS b/Misc/NEWS
index 8f09b5f..e68a9d7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,10 @@ Core and Builtins
Library
-------
+- Issue #3709: BaseHTTPRequestHandler will buffer the headers and write to
+ output stream only when end_headers is invoked. This is a speedup and an
+ internal optimization. Patch by endian.
+
- Issue #10220: Added inspect.getgeneratorstate. Initial patch by
Rodolpho Eckhardt.