diff --git a/h11/_headers.py b/h11/_headers.py index 878f63c..9747f31 100644 --- a/h11/_headers.py +++ b/h11/_headers.py @@ -64,7 +64,7 @@ def normalize_and_validate(headers, _parsed=False): new_headers = [] - saw_content_length = False + saw_content_length = None saw_transfer_encoding = False for name, value in headers: # For headers coming out of the parser, we can safely skip some steps, @@ -77,10 +77,13 @@ def normalize_and_validate(headers, _parsed=False): validate(_field_value_re, value, "Illegal header value {!r}", value) name = name.lower() if name == b"content-length": - if saw_content_length: - raise LocalProtocolError("multiple Content-Length headers") validate(_content_length_re, value, "bad Content-Length") - saw_content_length = True + if saw_content_length: + if saw_content_length != value: + raise LocalProtocolError("conflicting Content-Length headers") + # We have a duplicate Content-Length, so collapse it + new_headers.remove((name, saw_content_length)) + saw_content_length = value if name == b"transfer-encoding": # "A server that receives a request message with a transfer coding # it does not understand SHOULD respond with 501 (Not diff --git a/h11/tests/test_headers.py b/h11/tests/test_headers.py index 67bcd7b..562baa3 100644 --- a/h11/tests/test_headers.py +++ b/h11/tests/test_headers.py @@ -54,6 +54,13 @@ def test_normalize_and_validate(): normalize_and_validate([("Content-Length", "1x")]) with pytest.raises(LocalProtocolError): normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")]) + assert normalize_and_validate( + [("Content-Length", "1"), ("Content-Length", "1")] + ) == [(b"content-length", b"1")] + with pytest.raises(LocalProtocolError): + normalize_and_validate( + [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")] + ) # transfer-encoding assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [