Skip to content

Commit 43b0d51

Browse files
committed
gh-135056: Don't let extra headers overwrite default headers
1 parent c1fee3b commit 43b0d51

File tree

3 files changed

+32
-7
lines changed

3 files changed

+32
-7
lines changed

Doc/library/http.server.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,12 @@ instantiation, of which this module provides three different variants:
406406

407407
.. attribute:: extra_response_headers
408408

409-
A sequence of ``(name, value)`` pairs containing user-defined extra
410-
HTTP response headers to add to each successful HTTP status 200 response.
411-
These headers are not included in other status code responses.
409+
A sequence of ``(name, value)`` pairs containing user-defined extra HTTP
410+
response headers to add to each successful HTTP status 200 response. These
411+
headers are not included in other status code responses.
412+
413+
Headers that the server sends automatically (for instance Content-Type)
414+
will not be overwritten by extra_response_headers.
412415

413416
The :class:`SimpleHTTPRequestHandler` class defines the following methods:
414417

@@ -564,7 +567,8 @@ The following options are accepted:
564567

565568
Specify an additional extra HTTP Response Header to send on successful HTTP
566569
200 responses. Can be used multiple times to send additional custom response
567-
headers.
570+
headers. Headers that are sent automatically by the server (for instance
571+
Content-Type) will not be overwriten by the server.
568572

569573
.. versionadded:: next
570574

Lib/http/server.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ def handle_one_request(self):
466466
def handle(self):
467467
"""Handle multiple requests if necessary."""
468468
self.close_connection = True
469+
self.default_response_headers = []
469470

470471
self.handle_one_request()
471472
while not self.close_connection:
@@ -551,13 +552,15 @@ def send_response_only(self, code, message=None):
551552
(self.protocol_version, code, message)).encode(
552553
'latin-1', 'strict'))
553554

554-
def send_header(self, keyword, value):
555+
def send_header(self, keyword, value, is_extra=False):
555556
"""Send a MIME header to the headers buffer."""
556557
if self.request_version != 'HTTP/0.9':
557558
if not hasattr(self, '_headers_buffer'):
558559
self._headers_buffer = []
559560
self._headers_buffer.append(
560561
("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
562+
if not is_extra:
563+
self.default_response_headers.append((keyword, value))
561564

562565
if keyword.lower() == 'connection':
563566
if value.lower() == 'close':
@@ -758,10 +761,12 @@ def do_HEAD(self):
758761
f.close()
759762

760763
def _send_extra_response_headers(self):
761-
"""Send the headers stored in self.extra_response_headers."""
764+
"""Send the headers stored in self.extra_response_headers"""
762765
if self.extra_response_headers is not None:
763766
for header, value in self.extra_response_headers:
764-
self.send_header(header, value)
767+
# Don't send the header if it's already sent as part of the default response headers
768+
if header.lower() not in (h.lower() for h, _ in self.default_response_headers):
769+
self.send_header(header, value, is_extra=True)
765770

766771
def send_head(self):
767772
"""Common code for GET and HEAD commands.

Lib/test/test_httpservers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,22 @@ def test_extra_response_headers_missing_on_404(self):
939939
self.assertEqual(response.status, 404)
940940
self.assertEqual(response.getheader("X-Test1"), None)
941941

942+
def test_extra_response_headers_dont_overwrite_default_headers(self):
943+
with mock.patch.object(self.request_handler, 'extra_response_headers', [
944+
('Content-Type', 'test/not_allowed'),
945+
('Server', 'not_allowed'),
946+
('Set-Cookie', 'test=allowed'),
947+
]):
948+
# The Content-Type header should not be overwritten by the extra_response_headers
949+
# But cookies in the extra_allowed_duplicate_headers are allowed,
950+
# including Set-Cookie
951+
response = self.request(self.base_url + '/')
952+
self.assertEqual(response.status, 200)
953+
self.assertNotEqual(response.getheader("Content-Type"), 'test/not_allowed')
954+
self.assertNotEqual(response.getheader("Server"), 'not_allowed')
955+
self.assertEqual(response.getheader("Set-Cookie"), 'test=allowed')
956+
957+
942958

943959
class SocketlessRequestHandler(SimpleHTTPRequestHandler):
944960
def __init__(self, directory=None):

0 commit comments

Comments
 (0)