File size: 4,657 Bytes
870ab6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from http import HTTPStatus

import pytest

from .. import _events
from .._events import (
    ConnectionClosed,
    Data,
    EndOfMessage,
    Event,
    InformationalResponse,
    Request,
    Response,
)
from .._util import LocalProtocolError


def test_events() -> None:
    with pytest.raises(LocalProtocolError):
        # Missing Host:
        req = Request(
            method="GET", target="/", headers=[("a", "b")], http_version="1.1"
        )
    # But this is okay (HTTP/1.0)
    req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0")
    # fields are normalized
    assert req.method == b"GET"
    assert req.target == b"/"
    assert req.headers == [(b"a", b"b")]
    assert req.http_version == b"1.0"

    # This is also okay -- has a Host (with weird capitalization, which is ok)
    req = Request(
        method="GET",
        target="/",
        headers=[("a", "b"), ("hOSt", "example.com")],
        http_version="1.1",
    )
    # we normalize header capitalization
    assert req.headers == [(b"a", b"b"), (b"host", b"example.com")]

    # Multiple host is bad too
    with pytest.raises(LocalProtocolError):
        req = Request(
            method="GET",
            target="/",
            headers=[("Host", "a"), ("Host", "a")],
            http_version="1.1",
        )
    # Even for HTTP/1.0
    with pytest.raises(LocalProtocolError):
        req = Request(
            method="GET",
            target="/",
            headers=[("Host", "a"), ("Host", "a")],
            http_version="1.0",
        )

    # Header values are validated
    for bad_char in "\x00\r\n\f\v":
        with pytest.raises(LocalProtocolError):
            req = Request(
                method="GET",
                target="/",
                headers=[("Host", "a"), ("Foo", "asd" + bad_char)],
                http_version="1.0",
            )

    # But for compatibility we allow non-whitespace control characters, even
    # though they're forbidden by the spec.
    Request(
        method="GET",
        target="/",
        headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")],
        http_version="1.0",
    )

    # Request target is validated
    for bad_byte in b"\x00\x20\x7f\xee":
        target = bytearray(b"/")
        target.append(bad_byte)
        with pytest.raises(LocalProtocolError):
            Request(
                method="GET", target=target, headers=[("Host", "a")], http_version="1.1"
            )

    # Request method is validated
    with pytest.raises(LocalProtocolError):
        Request(
            method="GET / HTTP/1.1",
            target=target,
            headers=[("Host", "a")],
            http_version="1.1",
        )

    ir = InformationalResponse(status_code=100, headers=[("Host", "a")])
    assert ir.status_code == 100
    assert ir.headers == [(b"host", b"a")]
    assert ir.http_version == b"1.1"

    with pytest.raises(LocalProtocolError):
        InformationalResponse(status_code=200, headers=[("Host", "a")])

    resp = Response(status_code=204, headers=[], http_version="1.0")  # type: ignore[arg-type]
    assert resp.status_code == 204
    assert resp.headers == []
    assert resp.http_version == b"1.0"

    with pytest.raises(LocalProtocolError):
        resp = Response(status_code=100, headers=[], http_version="1.0")  # type: ignore[arg-type]

    with pytest.raises(LocalProtocolError):
        Response(status_code="100", headers=[], http_version="1.0")  # type: ignore[arg-type]

    with pytest.raises(LocalProtocolError):
        InformationalResponse(status_code=b"100", headers=[], http_version="1.0")  # type: ignore[arg-type]

    d = Data(data=b"asdf")
    assert d.data == b"asdf"

    eom = EndOfMessage()
    assert eom.headers == []

    cc = ConnectionClosed()
    assert repr(cc) == "ConnectionClosed()"


def test_intenum_status_code() -> None:
    # https://github.com/python-hyper/h11/issues/72

    r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0")  # type: ignore[arg-type]
    assert r.status_code == HTTPStatus.OK
    assert type(r.status_code) is not type(HTTPStatus.OK)
    assert type(r.status_code) is int


def test_header_casing() -> None:
    r = Request(
        method="GET",
        target="/",
        headers=[("Host", "example.org"), ("Connection", "keep-alive")],
        http_version="1.1",
    )
    assert len(r.headers) == 2
    assert r.headers[0] == (b"host", b"example.org")
    assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")]
    assert r.headers.raw_items() == [
        (b"Host", b"example.org"),
        (b"Connection", b"keep-alive"),
    ]