test_events.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from http import HTTPStatus
  2. import pytest
  3. from .. import _events
  4. from .._events import (
  5. ConnectionClosed,
  6. Data,
  7. EndOfMessage,
  8. Event,
  9. InformationalResponse,
  10. Request,
  11. Response,
  12. )
  13. from .._util import LocalProtocolError
  14. def test_events() -> None:
  15. with pytest.raises(LocalProtocolError):
  16. # Missing Host:
  17. req = Request(
  18. method="GET", target="/", headers=[("a", "b")], http_version="1.1"
  19. )
  20. # But this is okay (HTTP/1.0)
  21. req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0")
  22. # fields are normalized
  23. assert req.method == b"GET"
  24. assert req.target == b"/"
  25. assert req.headers == [(b"a", b"b")]
  26. assert req.http_version == b"1.0"
  27. # This is also okay -- has a Host (with weird capitalization, which is ok)
  28. req = Request(
  29. method="GET",
  30. target="/",
  31. headers=[("a", "b"), ("hOSt", "example.com")],
  32. http_version="1.1",
  33. )
  34. # we normalize header capitalization
  35. assert req.headers == [(b"a", b"b"), (b"host", b"example.com")]
  36. # Multiple host is bad too
  37. with pytest.raises(LocalProtocolError):
  38. req = Request(
  39. method="GET",
  40. target="/",
  41. headers=[("Host", "a"), ("Host", "a")],
  42. http_version="1.1",
  43. )
  44. # Even for HTTP/1.0
  45. with pytest.raises(LocalProtocolError):
  46. req = Request(
  47. method="GET",
  48. target="/",
  49. headers=[("Host", "a"), ("Host", "a")],
  50. http_version="1.0",
  51. )
  52. # Header values are validated
  53. for bad_char in "\x00\r\n\f\v":
  54. with pytest.raises(LocalProtocolError):
  55. req = Request(
  56. method="GET",
  57. target="/",
  58. headers=[("Host", "a"), ("Foo", "asd" + bad_char)],
  59. http_version="1.0",
  60. )
  61. # But for compatibility we allow non-whitespace control characters, even
  62. # though they're forbidden by the spec.
  63. Request(
  64. method="GET",
  65. target="/",
  66. headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")],
  67. http_version="1.0",
  68. )
  69. # Request target is validated
  70. for bad_byte in b"\x00\x20\x7f\xee":
  71. target = bytearray(b"/")
  72. target.append(bad_byte)
  73. with pytest.raises(LocalProtocolError):
  74. Request(
  75. method="GET", target=target, headers=[("Host", "a")], http_version="1.1"
  76. )
  77. # Request method is validated
  78. with pytest.raises(LocalProtocolError):
  79. Request(
  80. method="GET / HTTP/1.1",
  81. target=target,
  82. headers=[("Host", "a")],
  83. http_version="1.1",
  84. )
  85. ir = InformationalResponse(status_code=100, headers=[("Host", "a")])
  86. assert ir.status_code == 100
  87. assert ir.headers == [(b"host", b"a")]
  88. assert ir.http_version == b"1.1"
  89. with pytest.raises(LocalProtocolError):
  90. InformationalResponse(status_code=200, headers=[("Host", "a")])
  91. resp = Response(status_code=204, headers=[], http_version="1.0") # type: ignore[arg-type]
  92. assert resp.status_code == 204
  93. assert resp.headers == []
  94. assert resp.http_version == b"1.0"
  95. with pytest.raises(LocalProtocolError):
  96. resp = Response(status_code=100, headers=[], http_version="1.0") # type: ignore[arg-type]
  97. with pytest.raises(LocalProtocolError):
  98. Response(status_code="100", headers=[], http_version="1.0") # type: ignore[arg-type]
  99. with pytest.raises(LocalProtocolError):
  100. InformationalResponse(status_code=b"100", headers=[], http_version="1.0") # type: ignore[arg-type]
  101. d = Data(data=b"asdf")
  102. assert d.data == b"asdf"
  103. eom = EndOfMessage()
  104. assert eom.headers == []
  105. cc = ConnectionClosed()
  106. assert repr(cc) == "ConnectionClosed()"
  107. def test_intenum_status_code() -> None:
  108. # https://github.com/python-hyper/h11/issues/72
  109. r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0") # type: ignore[arg-type]
  110. assert r.status_code == HTTPStatus.OK
  111. assert type(r.status_code) is not type(HTTPStatus.OK)
  112. assert type(r.status_code) is int
  113. def test_header_casing() -> None:
  114. r = Request(
  115. method="GET",
  116. target="/",
  117. headers=[("Host", "example.org"), ("Connection", "keep-alive")],
  118. http_version="1.1",
  119. )
  120. assert len(r.headers) == 2
  121. assert r.headers[0] == (b"host", b"example.org")
  122. assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")]
  123. assert r.headers.raw_items() == [
  124. (b"Host", b"example.org"),
  125. (b"Connection", b"keep-alive"),
  126. ]