diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 4002fc697ceaa56..ab557f1805e1049 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -370,7 +370,8 @@ HTTPConnection Objects proxy server). The *headers* argument should be a mapping of extra HTTP headers to send with - the CONNECT request. + the CONNECT request. Non-string header values are converted to strings via + :func:`str`. As HTTP/1.1 is used for HTTP CONNECT tunnelling request, `as per the RFC `_, a HTTP ``Host:`` diff --git a/Lib/http/client.py b/Lib/http/client.py index 4b6fc549a9b5179..f13eb6d91b77f6f 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1015,7 +1015,7 @@ def _tunnel(self): headers = [connect] for header, value in self._tunnel_headers.items(): header_bytes = header.encode("latin-1") - value_bytes = value.encode("latin-1") + value_bytes = str(value).encode("latin-1") if not _is_legal_header_name(header_bytes): raise ValueError('Invalid header name %r' % (header_bytes,)) if _is_illegal_header_value(value_bytes): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index ad28e921978e98f..5fd4e8fca30fcf5 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2793,6 +2793,23 @@ def _create_connection(address, timeout=None, source_address=None): self.assertIsNotNone(exc) self.assertTrue(sock.file_closed) + def test_tunnel_nonstr_headers(self): + class CustomObject: + def __str__(self): + return 'custom-str' + + self.conn.set_tunnel('destination.com', headers={ + 'None-Value': None, + 'Integer-Value': 9, + 'Custom-Value': CustomObject(), + }) + + self.conn.request('HEAD', '/', '') + + self.assertIn(b'None-Value: None\r\n', self.conn.sock.data) + self.assertIn(b'Integer-Value: 9\r\n', self.conn.sock.data) + self.assertIn(b'Custom-Value: custom-str\r\n', self.conn.sock.data) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Misc/NEWS.d/next/Library/2026-10-12-00-00-00.gh-issue-159044.Tr8kXv.rst b/Misc/NEWS.d/next/Library/2026-10-12-00-00-00.gh-issue-159044.Tr8kXv.rst new file mode 100644 index 000000000000000..ef51dc8eca1066c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-12-00-00-00.gh-issue-159044.Tr8kXv.rst @@ -0,0 +1,4 @@ +Restore the previous behaviour of :meth:`http.client.HTTPConnection.set_tunnel` +where header values that are not strings (such as :const:`None`, :class:`int` +or other objects) are converted with :class:`str` instead of raising +:exc:`AttributeError` when the request is sent.