Skip to content

Commit 32a3e7e

Browse files
Das-Chinmayorsenthil
authored andcommitted
gh-47005: fix do_open() to let regular headers override unredirected … (GH-146506)
* gh-47005: fix do_open() to let regular headers override unredirected headers 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 0f529b1 commit 32a3e7e

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
@@ -963,6 +963,35 @@ def test_http(self):
963963
self.assertEqual(req.unredirected_hdrs["Host"], "baz")
964964
self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
965965

966+
def test_http_header_priority(self):
967+
# gh-47005: regular headers set via add_header() must override
968+
# unredirected headers with the same name in do_open(), consistent
969+
# with get_header() and header_items().
970+
cases = [
971+
("Content-Type", "application/json", "application/x-www-form-urlencoded"),
972+
("Content-Length", "99", "0"),
973+
("Host", "override.example.com", "internal.example.com"),
974+
("Authorization", "Bearer user-token", "Basic stale="),
975+
("Cookie", "a=1", "b=2"),
976+
("User-Agent", "MyApp/1.0", "Python-urllib/test"),
977+
]
978+
h = urllib.request.AbstractHTTPHandler()
979+
h.parent = MockOpener()
980+
981+
for key, regular, unredirected in cases:
982+
req = Request("http://example.com/", headers={key: regular})
983+
req.timeout = None
984+
req.add_unredirected_header(key, unredirected)
985+
986+
http = MockHTTPClass()
987+
h.do_open(http, req)
988+
989+
sent_headers = dict(http.req_headers)
990+
self.assertEqual(sent_headers[key], regular)
991+
# key is capitalized by add_header() and add_unredirected_header() calls
992+
self.assertEqual(req.get_header(key.capitalize()), regular)
993+
self.assertEqual(dict(req.header_items())[key.capitalize()], regular)
994+
966995
def test_http_body_file(self):
967996
# A regular file - chunked encoding is used unless Content Length is
968997
# already set.

Lib/urllib/request.py

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

12931293
headers = dict(req.unredirected_hdrs)
1294-
headers.update({k: v for k, v in req.headers.items()
1295-
if k not in headers})
1294+
headers.update(req.headers)
12961295

12971296
# TODO(jhylton): Should this be redesigned to handle
12981297
# 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)