Issue #3709: a flush_headers method to BaseHTTPRequestHandler which manages the

sending of headers to output stream and flushing the internal headers buffer.
Patch contribution by Andrew Schaaf
This commit is contained in:
Senthil Kumaran 2011-05-09 23:25:02 +08:00
parent 87cf220972
commit c7ae19b6a7
4 changed files with 93 additions and 31 deletions

View File

@ -179,16 +179,17 @@ of which this module provides three different variants:
.. method:: send_response(code, message=None)
Sends a response header and logs the accepted request. The HTTP response
line is sent, followed by *Server* and *Date* headers. The values for
these two headers are picked up from the :meth:`version_string` and
:meth:`date_time_string` methods, respectively.
Adds a response header to the headers buffer and logs the accepted
request. The HTTP response line is sent, followed by *Server* and
*Date* headers. The values for these two headers are picked up from
the :meth:`version_string` and :meth:`date_time_string` methods,
respectively.
.. method:: send_header(keyword, 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*
Adds the HTTP header to an internal buffer which will be written to the
output stream when either :meth:`end_headers` or :meth:`flush_headers`
is invoked. *keyword* should specify the header keyword, with *value*
specifying its value.
.. versionchanged:: 3.2 Storing the headers in an internal buffer
@ -205,11 +206,19 @@ of which this module provides three different variants:
.. method:: end_headers()
Write the buffered HTTP headers to the output stream and send a blank
line, indicating the end of the HTTP headers in the response.
Adds a blank line
(indicating the end of the HTTP headers in the response)
to the headers buffer and calls :meth:`flush_headers()`
.. versionchanged:: 3.2 Writing the buffered headers to the output stream.
.. method:: flush_headers()
Finally send the headers to the output stream and flush the internal
headers buffer.
.. versionadded:: 3.3
.. method:: log_request(code='-', size='-')
Logs an accepted (successful) request. *code* should specify the numeric

View File

@ -355,6 +355,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""
self.send_response_only(100)
self.flush_headers()
return True
def handle_one_request(self):
@ -432,7 +433,8 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
self.wfile.write(content.encode('UTF-8', 'replace'))
def send_response(self, code, message=None):
"""Send the response header and log the response code.
"""Add the response header to the headers buffer and log the
response code.
Also send two standard headers with the server software
version and the current date.
@ -451,11 +453,14 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
else:
message = ''
if self.request_version != 'HTTP/0.9':
self.wfile.write(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode('latin-1', 'strict'))
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
self._headers_buffer.append(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode(
'latin-1', 'strict'))
def send_header(self, keyword, value):
"""Send a MIME header."""
"""Send a MIME header to the headers buffer."""
if self.request_version != 'HTTP/0.9':
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
@ -472,6 +477,10 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""Send the blank line ending the MIME headers."""
if self.request_version != 'HTTP/0.9':
self._headers_buffer.append(b"\r\n")
self.flush_headers()
def flush_headers(self):
if hasattr(self, '_headers_buffer'):
self.wfile.write(b"".join(self._headers_buffer))
self._headers_buffer = []
@ -1081,6 +1090,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
env.setdefault(k, "")
self.send_response(200, "Script output follows")
self.flush_headers()
decoded_query = query.replace('+', ' ')

View File

@ -461,6 +461,23 @@ class RejectingSocketlessRequestHandler(SocketlessRequestHandler):
self.send_error(417)
return False
class AuditableBytesIO:
def __init__(self):
self.datas = []
def write(self, data):
self.datas.append(data)
def getData(self):
return b''.join(self.datas)
@property
def numWrites(self):
return len(self.datas)
class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
"""Test the functionality of the BaseHTTPServer.
@ -527,27 +544,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
def test_header_buffering_of_send_error(self):
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'
output = AuditableBytesIO()
handler = SocketlessRequestHandler()
handler.rfile = input
handler.wfile = output
handler.request_version = 'HTTP/1.1'
handler.requestline = ''
handler.command = None
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')
handler.send_error(418)
self.assertEqual(output.numWrites, 2)
def test_header_buffering_of_send_response_only(self):
input = BytesIO(b'GET / HTTP/1.1\r\n\r\n')
output = AuditableBytesIO()
handler = SocketlessRequestHandler()
handler.rfile = input
handler.wfile = output
handler.request_version = 'HTTP/1.1'
handler.send_response_only(418)
self.assertEqual(output.numWrites, 0)
handler.end_headers()
self.assertEqual(output.numWrites, 1)
def test_header_buffering_of_send_header(self):
input = BytesIO(b'GET / HTTP/1.1\r\n\r\n')
output = AuditableBytesIO()
handler = SocketlessRequestHandler()
handler.rfile = input
handler.wfile = output
handler.request_version = 'HTTP/1.1'
handler.send_header('Foo', 'foo')
handler.send_header('bar', 'bar')
self.assertEqual(output.numWrites, 0)
handler.end_headers()
self.assertEqual(output.getData(), b'Foo: foo\r\nbar: bar\r\n\r\n')
self.assertEqual(output.numWrites, 1)
def test_header_unbuffered_when_continue(self):

View File

@ -140,6 +140,10 @@ Core and Builtins
Library
-------
- Issue #3709: a flush_headers method to BaseHTTPRequestHandler which manages
the sending of headers to output stream and flushing the internal headers
buffer. Patch contribution by Andrew Schaaf
- Issue #11743: Rewrite multiprocessing connection classes in pure Python.
- Issue #11164: Stop trying to use _xmlplus in the xml module.