Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e4dad4f

Browse files
committed
Fix issue3709 - BaseHTTPRequestHandler will buffer the headers and write only on end_headers call.
1 parent a73dc9d commit e4dad4f

4 files changed

Lines changed: 68 additions & 9 deletions

File tree

Doc/library/http.server.rst

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,22 +182,29 @@ of which this module provides three different variants:
182182

183183
.. method:: send_header(keyword, value)
184184

185-
Writes a specific HTTP header to the output stream. *keyword* should
186-
specify the header keyword, with *value* specifying its value.
185+
Stores the HTTP header to an internal buffer which will be written to the
186+
output stream when :meth:`end_headers` method is invoked.
187+
*keyword* should specify the header keyword, with *value*
188+
specifying its value.
189+
190+
.. versionchanged:: 3.2 Storing the headers in an internal buffer
191+
187192

188193
.. method:: send_response_only(code, message=None)
189194

190195
Sends the reponse header only, used for the purposes when ``100
191-
Continue`` response is sent by the server to the client. If the *message*
192-
is not specified, the HTTP message corresponding the response *code* is
193-
sent.
196+
Continue`` response is sent by the server to the client. The headers not
197+
buffered and sent directly the output stream.If the *message* is not
198+
specified, the HTTP message corresponding the response *code* is sent.
194199

195200
.. versionadded:: 3.2
196201

197202
.. method:: end_headers()
198203

199-
Sends a blank line, indicating the end of the HTTP headers in the
200-
response.
204+
Write the buffered HTTP headers to the output stream and send a blank
205+
line, indicating the end of the HTTP headers in the response.
206+
207+
.. versionchanged:: 3.2 Writing the buffered headers to the output stream.
201208

202209
.. method:: log_request(code='-', size='-')
203210

Lib/http/server.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,10 @@ def send_response_only(self, code, message=None):
443443
def send_header(self, keyword, value):
444444
"""Send a MIME header."""
445445
if self.request_version != 'HTTP/0.9':
446-
self.wfile.write(("%s: %s\r\n" % (keyword, value)).encode('ASCII', 'strict'))
446+
if not hasattr(self, '_headers_buffer'):
447+
self._headers_buffer = []
448+
self._headers_buffer.append(
449+
("%s: %s\r\n" % (keyword, value)).encode('ASCII', 'strict'))
447450

448451
if keyword.lower() == 'connection':
449452
if value.lower() == 'close':
@@ -454,7 +457,9 @@ def send_header(self, keyword, value):
454457
def end_headers(self):
455458
"""Send the blank line ending the MIME headers."""
456459
if self.request_version != 'HTTP/0.9':
457-
self.wfile.write(b"\r\n")
460+
self._headers_buffer.append(b"\r\n")
461+
self.wfile.write(b"".join(self._headers_buffer))
462+
self._headers_buffer = []
458463

459464
def log_request(self, code='-', size='-'):
460465
"""Log an accepted request.

Lib/test/test_httpservers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,49 @@ def test_with_continue_1_1(self):
511511
self.verify_get_called()
512512
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
513513

514+
def test_header_buffering(self):
515+
516+
def _readAndReseek(f):
517+
pos = f.tell()
518+
f.seek(0)
519+
data = f.read()
520+
f.seek(pos)
521+
return data
522+
523+
input = BytesIO(b'GET / HTTP/1.1\r\n\r\n')
524+
output = BytesIO()
525+
self.handler.rfile = input
526+
self.handler.wfile = output
527+
self.handler.request_version = 'HTTP/1.1'
528+
529+
self.handler.send_header('Foo', 'foo')
530+
self.handler.send_header('bar', 'bar')
531+
self.assertEqual(_readAndReseek(output), b'')
532+
self.handler.end_headers()
533+
self.assertEqual(_readAndReseek(output),
534+
b'Foo: foo\r\nbar: bar\r\n\r\n')
535+
536+
def test_header_unbuffered_when_continue(self):
537+
538+
def _readAndReseek(f):
539+
pos = f.tell()
540+
f.seek(0)
541+
data = f.read()
542+
f.seek(pos)
543+
return data
544+
545+
input = BytesIO(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n')
546+
output = BytesIO()
547+
self.handler.rfile = input
548+
self.handler.wfile = output
549+
self.handler.request_version = 'HTTP/1.1'
550+
551+
self.handler.handle_one_request()
552+
self.assertNotEqual(_readAndReseek(output), b'')
553+
result = _readAndReseek(output).split(b'\r\n')
554+
self.assertEqual(result[0], b'HTTP/1.1 100 Continue')
555+
self.assertEqual(result[1], b'HTTP/1.1 200 OK')
556+
514557
def test_with_continue_rejected(self):
515558
usual_handler = self.handler # Save to avoid breaking any subsequent tests.
516559
self.handler = RejectingSocketlessRequestHandler()

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- Issue #3709: BaseHTTPRequestHandler will buffer the headers and write to
36+
output stream only when end_headers is invoked. This is a speedup and an
37+
internal optimization. Patch by endian.
38+
3539
- Issue #10220: Added inspect.getgeneratorstate. Initial patch by
3640
Rodolpho Eckhardt.
3741

0 commit comments

Comments
 (0)