# HG changeset patch # Parent 96cdd2532034211fa14f444c159801d349067d73 Issue #2275: Make Request header lookup methods case-insensitive Based on patch by John J Lee. diff -r 96cdd2532034 Doc/library/urllib.request.rst --- a/Doc/library/urllib.request.rst Sun Nov 08 11:09:37 2015 +0000 +++ b/Doc/library/urllib.request.rst Mon Nov 09 06:02:21 2015 +0000 @@ -503,27 +503,36 @@ name, and later calls will overwrite previous calls in case the *key* collides. Currently, this is no loss of HTTP functionality, since all headers which have meaning when used more than once have a (header-specific) way of gaining the - same functionality using only one header. + same functionality using only one header. The header field name *key* is + case-insensitive -.. method:: Request.add_unredirected_header(key, header) +.. method:: Request.add_unredirected_header(key, val) Add a header that will not be added to a redirected request. -.. method:: Request.has_header(header) +.. method:: Request.has_header(header_name) Return whether the instance has the named header (checks both regular and unredirected). + .. versionchanged:: 3.6 + The header field name is now case-insensitive, rather than having to be + in the form produced by :meth:`str.capitalize`. -.. method:: Request.remove_header(header) + +.. method:: Request.remove_header(header_name) Remove named header from the request instance (both from regular and unredirected headers). .. versionadded:: 3.4 + .. versionchanged:: 3.6 + The header field name is now case-insensitive, rather than having to be + in the form produced by :meth:`str.capitalize`. + .. method:: Request.get_full_url() @@ -546,10 +555,16 @@ Return the value of the given header. If the header is not present, return the default value. + .. versionchanged:: 3.6 + The header field name is now case-insensitive, rather than having to be + in the form produced by :meth:`str.capitalize`. + .. method:: Request.header_items() Return a list of tuples (header_name, header_value) of the Request headers. + The returned header field names are transformed as if by + :meth:`str.capitalize`. .. versionchanged:: 3.4 The request methods add_data, has_data, get_data, get_type, get_host, diff -r 96cdd2532034 Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Sun Nov 08 11:09:37 2015 +0000 +++ b/Doc/whatsnew/3.6.rst Mon Nov 09 06:02:21 2015 +0000 @@ -121,6 +121,16 @@ (Contributed by Serhiy Storchaka in :issue:`25011` and :issue:`25209`.) +urllib.request +-------------- + +In the :class:`Request.has_header() `, +:meth:`~urllib.request.Request.get_header` and +:meth:`~urllib.request.Request.remove_header` methods, the header field name +is now case-insensitive. +(Contributed by John J Lee and Martin Panter in :issue:`2275`.) + + urllib.robotparser ------------------ diff -r 96cdd2532034 Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py Sun Nov 08 11:09:37 2015 +0000 +++ b/Lib/test/test_urllib2.py Mon Nov 09 06:02:21 2015 +0000 @@ -99,34 +99,39 @@ ).headers["Spam-eggs"], "blah") def test_request_headers_methods(self): - """ - Note the case normalization of header names here, to - .capitalize()-case. This should be preserved for - backwards-compatibility. (In the HTTP case, normalization to - .title()-case is done by urllib2 before sending headers to - http.client). - - Note that e.g. r.has_header("spam-EggS") is currently False, and - r.get_header("spam-EggS") returns None, but that could be changed in - future. - - Method r.remove_header should remove items both from r.headers and - r.unredirected_hdrs dictionaries - """ url = "http://example.com" req = Request(url, headers={"Spam-eggs": "blah"}) + + # has_header() and get_header() are case-insensitive self.assertTrue(req.has_header("Spam-eggs")) + self.assertTrue(req.has_header("SPAM-EGGS")) + self.assertEqual(req.get_header("Spam-eggs"), "blah") + self.assertEqual(req.get_header("SPAM-EGGS"), "blah") + + # header_items() returns headers in capitalize()-case. This should + # be preserved for backwards compatibility. (In the HTTP case, + # normalization to title()-case is done by urllib2 before sending + # headers to http.client). + self.assertEqual(req.header_items(), [('Spam-eggs', 'blah')]) req.add_header("Foo-Bar", "baz") self.assertEqual(sorted(req.header_items()), [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')]) + + # Missing key behaviour self.assertFalse(req.has_header("Not-there")) self.assertIsNone(req.get_header("Not-there")) self.assertEqual(req.get_header("Not-there", "default"), "default") + # Method r.remove_header should remove items both from r.headers and + # r.unredirected_hdrs dictionaries. It is also case-insensitive. + req.remove_header("Spam-eggs") self.assertFalse(req.has_header("Spam-eggs")) + req.add_header("Spam-eggs", "blah") + req.remove_header("SPAM-EGGS") + self.assertFalse(req.has_header("Spam-eggs")) req.add_unredirected_header("Unredirected-spam", "Eggs") self.assertTrue(req.has_header("Unredirected-spam")) diff -r 96cdd2532034 Lib/urllib/request.py --- a/Lib/urllib/request.py Sun Nov 08 11:09:37 2015 +0000 +++ b/Lib/urllib/request.py Mon Nov 09 06:02:21 2015 +0000 @@ -418,15 +418,18 @@ self.unredirected_hdrs[key.capitalize()] = val def has_header(self, header_name): + header_name = header_name.capitalize() return (header_name in self.headers or header_name in self.unredirected_hdrs) def get_header(self, header_name, default=None): + header_name = header_name.capitalize() return self.headers.get( header_name, self.unredirected_hdrs.get(header_name, default)) def remove_header(self, header_name): + header_name = header_name.capitalize() self.headers.pop(header_name, None) self.unredirected_hdrs.pop(header_name, None) diff -r 96cdd2532034 Misc/NEWS --- a/Misc/NEWS Sun Nov 08 11:09:37 2015 +0000 +++ b/Misc/NEWS Mon Nov 09 06:02:21 2015 +0000 @@ -79,6 +79,9 @@ Library ------- +- Issue #2275: Header field names are now case-insensitive in + urllib.request.Request. Based on patch by John J Lee. + - Issue #25446: Fix regression in smtplib's AUTH LOGIN support. - Issue #18010: Fix the pydoc web server's module search function to handle