import pytest from .._events import Request from .._headers import ( get_comma_header, has_expect_100_continue, Headers, normalize_and_validate, set_comma_header, ) from .._util import LocalProtocolError def test_normalize_and_validate() -> None: assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")] assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")] # no leading/trailing whitespace in names with pytest.raises(LocalProtocolError): normalize_and_validate([(b"foo ", "bar")]) with pytest.raises(LocalProtocolError): normalize_and_validate([(b" foo", "bar")]) # no weird characters in names with pytest.raises(LocalProtocolError) as excinfo: normalize_and_validate([(b"foo bar", b"baz")]) assert "foo bar" in str(excinfo.value) with pytest.raises(LocalProtocolError): normalize_and_validate([(b"foo\x00bar", b"baz")]) # Not even 8-bit characters: with pytest.raises(LocalProtocolError): normalize_and_validate([(b"foo\xffbar", b"baz")]) # And not even the control characters we allow in values: with pytest.raises(LocalProtocolError): normalize_and_validate([(b"foo\x01bar", b"baz")]) # no return or NUL characters in values with pytest.raises(LocalProtocolError) as excinfo: normalize_and_validate([("foo", "bar\rbaz")]) assert "bar\\rbaz" in str(excinfo.value) with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", "bar\nbaz")]) with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", "bar\x00baz")]) # no leading/trailing whitespace with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", "barbaz ")]) with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", " barbaz")]) with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", "barbaz\t")]) with pytest.raises(LocalProtocolError): normalize_and_validate([("foo", "\tbarbaz")]) # content-length assert normalize_and_validate([("Content-Length", "1")]) == [ (b"content-length", b"1") ] with pytest.raises(LocalProtocolError): normalize_and_validate([("Content-Length", "asdf")]) with pytest.raises(LocalProtocolError): 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", "0"), ("Content-Length", "0")] ) == [(b"content-length", b"0")] assert normalize_and_validate([("Content-Length", "0 , 0")]) == [ (b"content-length", b"0") ] with pytest.raises(LocalProtocolError): normalize_and_validate( [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")] ) with pytest.raises(LocalProtocolError): normalize_and_validate([("Content-Length", "1 , 1,2")]) # transfer-encoding assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [ (b"transfer-encoding", b"chunked") ] assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [ (b"transfer-encoding", b"chunked") ] with pytest.raises(LocalProtocolError) as excinfo: normalize_and_validate([("Transfer-Encoding", "gzip")]) assert excinfo.value.error_status_hint == 501 # Not Implemented with pytest.raises(LocalProtocolError) as excinfo: normalize_and_validate( [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")] ) assert excinfo.value.error_status_hint == 501 # Not Implemented def test_get_set_comma_header() -> None: headers = normalize_and_validate( [ ("Connection", "close"), ("whatever", "something"), ("connectiON", "fOo,, , BAR"), ] ) assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"] headers = set_comma_header(headers, b"newthing", ["a", "b"]) # type: ignore with pytest.raises(LocalProtocolError): set_comma_header(headers, b"newthing", [" a", "b"]) # type: ignore assert headers == [ (b"connection", b"close"), (b"whatever", b"something"), (b"connection", b"fOo,, , BAR"), (b"newthing", b"a"), (b"newthing", b"b"), ] headers = set_comma_header(headers, b"whatever", ["different thing"]) # type: ignore assert headers == [ (b"connection", b"close"), (b"connection", b"fOo,, , BAR"), (b"newthing", b"a"), (b"newthing", b"b"), (b"whatever", b"different thing"), ] def test_has_100_continue() -> None: assert has_expect_100_continue( Request( method="GET", target="/", headers=[("Host", "example.com"), ("Expect", "100-continue")], ) ) assert not has_expect_100_continue( Request(method="GET", target="/", headers=[("Host", "example.com")]) ) # Case insensitive assert has_expect_100_continue( Request( method="GET", target="/", headers=[("Host", "example.com"), ("Expect", "100-Continue")], ) ) # Doesn't work in HTTP/1.0 assert not has_expect_100_continue( Request( method="GET", target="/", headers=[("Host", "example.com"), ("Expect", "100-continue")], http_version="1.0", ) )