Skip to content

Commit ff3fca1

Browse files
miss-islingtonDas-Chinmayorsenthil
authored
[3.13] gh-47005: fix do_open() to let regular headers override unredirected … (GH-146506) (#153379)
gh-47005: fix do_open() to let regular headers override unredirected … (GH-146506) AbstractHTTPHandler.do_open() was building the request header dict by starting with unredirected_hdrs and only inserting regular headers that were not already present, giving unredirected headers priority. This contradicts get_header() and header_items(), both of which give regular headers the higher priority. Fix by unconditionally updating with req.headers so that a header set via add_header() always overrides one set via add_unredirected_header(). --------- (cherry picked from commit 6d38668) Co-authored-by: CHINMAY <89741289+Das-Chinmay@users.noreply.github.com> Co-authored-by: Senthil Kumaran <senthil@python.org>
1 parent ea616e3 commit ff3fca1

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

Lib/test/test_urllib2.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,35 @@ def test_http(self):
950950
self.assertEqual(req.unredirected_hdrs["Host"], "baz")
951951
self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
952952

953+
def test_http_header_priority(self):
954+
# gh-47005: regular headers set via add_header() must override
955+
# unredirected headers with the same name in do_open(), consistent
956+
# with get_header() and header_items().
957+
cases = [
958+
("Content-Type", "application/json", "application/x-www-form-urlencoded"),
959+
("Content-Length", "99", "0"),
960+
("Host", "override.example.com", "internal.example.com"),
961+
("Authorization", "Bearer user-token", "Basic stale="),
962+
("Cookie", "a=1", "b=2"),
963+
("User-Agent", "MyApp/1.0", "Python-urllib/test"),
964+
]
965+
h = urllib.request.AbstractHTTPHandler()
966+
h.parent = MockOpener()
967+
968+
for key, regular, unredirected in cases:
969+
req = Request("http://example.com/", headers={key: regular})
970+
req.timeout = None
971+
req.add_unredirected_header(key, unredirected)
972+
973+
http = MockHTTPClass()
974+
h.do_open(http, req)
975+
976+
sent_headers = dict(http.req_headers)
977+
self.assertEqual(sent_headers[key], regular)
978+
# key is capitalized by add_header() and add_unredirected_header() calls
979+
self.assertEqual(req.get_header(key.capitalize()), regular)
980+
self.assertEqual(dict(req.header_items())[key.capitalize()], regular)
981+
953982
def test_http_body_file(self):
954983
# A regular file - chunked encoding is used unless Content Length is
955984
# already set.

Lib/urllib/request.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,8 +1289,7 @@ def do_open(self, http_class, req, **http_conn_args):
12891289
h.set_debuglevel(self._debuglevel)
12901290

12911291
headers = dict(req.unredirected_hdrs)
1292-
headers.update({k: v for k, v in req.headers.items()
1293-
if k not in headers})
1292+
headers.update(req.headers)
12941293

12951294
# TODO(jhylton): Should this be redesigned to handle
12961295
# persistent connections?
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :meth:`!urllib.request.AbstractHTTPHandler.do_open` to give regular
2+
headers set via :meth:`~urllib.request.Request.add_header` priority over
3+
unredirected headers, consistent with :meth:`~urllib.request.Request.get_header`
4+
and :meth:`~urllib.request.Request.header_items`.

0 commit comments

Comments
 (0)