Skip to content

Commit 9fb25cb

Browse files
miss-islingtonDas-Chinmayorsenthil
authored
[3.15] gh-47005: fix do_open() to let regular headers override unredirected … (GH-146506) (#153377)
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 c512e8f commit 9fb25cb

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
@@ -980,6 +980,35 @@ def test_http(self):
980980
self.assertEqual(req.unredirected_hdrs["Host"], "baz")
981981
self.assertEqual(req.unredirected_hdrs["Spam"], "foo")
982982

983+
def test_http_header_priority(self):
984+
# gh-47005: regular headers set via add_header() must override
985+
# unredirected headers with the same name in do_open(), consistent
986+
# with get_header() and header_items().
987+
cases = [
988+
("Content-Type", "application/json", "application/x-www-form-urlencoded"),
989+
("Content-Length", "99", "0"),
990+
("Host", "override.example.com", "internal.example.com"),
991+
("Authorization", "Bearer user-token", "Basic stale="),
992+
("Cookie", "a=1", "b=2"),
993+
("User-Agent", "MyApp/1.0", "Python-urllib/test"),
994+
]
995+
h = urllib.request.AbstractHTTPHandler()
996+
h.parent = MockOpener()
997+
998+
for key, regular, unredirected in cases:
999+
req = Request("http://example.com/", headers={key: regular})
1000+
req.timeout = None
1001+
req.add_unredirected_header(key, unredirected)
1002+
1003+
http = MockHTTPClass()
1004+
h.do_open(http, req)
1005+
1006+
sent_headers = dict(http.req_headers)
1007+
self.assertEqual(sent_headers[key], regular)
1008+
# key is capitalized by add_header() and add_unredirected_header() calls
1009+
self.assertEqual(req.get_header(key.capitalize()), regular)
1010+
self.assertEqual(dict(req.header_items())[key.capitalize()], regular)
1011+
9831012
def test_http_body_file(self):
9841013
# A regular file - chunked encoding is used unless Content Length is
9851014
# already set.

Lib/urllib/request.py

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

12951295
headers = dict(req.unredirected_hdrs)
1296-
headers.update({k: v for k, v in req.headers.items()
1297-
if k not in headers})
1296+
headers.update(req.headers)
12981297

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