diff --git a/AUTHORS.rst b/AUTHORS.rst index f434910468..c4e554d478 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -191,3 +191,4 @@ Patches and Suggestions - "Dull Bananas" (`@dullbananas `_) - Alessio Izzo (`@aless10 `_) - Sylvain MariƩ (`@smarie `_) +- Hod Bin Noon (`@hodbn `_) diff --git a/requests/adapters.py b/requests/adapters.py index 9b7f92af66..fe22ff450e 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -458,9 +458,11 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) try: + skip_host = 'Host' in request.headers low_conn.putrequest(request.method, url, - skip_accept_encoding=True) + skip_accept_encoding=True, + skip_host=skip_host) for header, value in request.headers.items(): low_conn.putheader(header, value) diff --git a/tests/test_lowlevel.py b/tests/test_lowlevel.py index 2ada004a74..7256eda943 100644 --- a/tests/test_lowlevel.py +++ b/tests/test_lowlevel.py @@ -9,6 +9,18 @@ from .utils import override_environ +def echo_response_handler(sock): + """Simple handler that will take request and echo it back to requester.""" + request_content = consume_socket_content(sock, timeout=0.5) + + text_200 = ( + b'HTTP/1.1 200 OK\r\n' + b'Content-Length: %d\r\n\r\n' + b'%s' + ) % (len(request_content), request_content) + sock.send(text_200) + + def test_chunked_upload(): """can safely send generators""" close_server = threading.Event() @@ -46,6 +58,42 @@ def incomplete_chunked_response_handler(sock): close_server.set() # release server block +def test_chunked_upload_uses_only_specified_host_header(): + """Ensure we use only the specified Host header for chunked requests.""" + close_server = threading.Event() + server = Server(echo_response_handler, wait_to_close_event=close_server) + + data = iter([b'a', b'b', b'c']) + custom_host = 'sample-host' + + with server as (host, port): + url = 'http://{}:{}/'.format(host, port) + r = requests.post(url, data=data, headers={'Host': custom_host}, stream=True) + close_server.set() # release server block + + expected_header = b'Host: %s\r\n' % custom_host.encode('utf-8') + assert expected_header in r.content + assert r.content.count(b'Host: ') == 1 + + +def test_chunked_upload_doesnt_skip_host_header(): + """Ensure we don't omit all Host headers with chunked requests.""" + close_server = threading.Event() + server = Server(echo_response_handler, wait_to_close_event=close_server) + + data = iter([b'a', b'b', b'c']) + + with server as (host, port): + expected_host = '{}:{}'.format(host, port) + url = 'http://{}:{}/'.format(host, port) + r = requests.post(url, data=data, stream=True) + close_server.set() # release server block + + expected_header = b'Host: %s\r\n' % expected_host.encode('utf-8') + assert expected_header in r.content + assert r.content.count(b'Host: ') == 1 + + def test_conflicting_content_lengths(): """Ensure we correctly throw an InvalidHeader error if multiple conflicting Content-Length headers are returned.