You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
5.5 KiB
157 lines
5.5 KiB
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", |
|
) |
|
)
|
|
|