test_headers.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import pytest
  2. from .._events import Request
  3. from .._headers import (
  4. get_comma_header,
  5. has_expect_100_continue,
  6. Headers,
  7. normalize_and_validate,
  8. set_comma_header,
  9. )
  10. from .._util import LocalProtocolError
  11. def test_normalize_and_validate() -> None:
  12. assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")]
  13. assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")]
  14. # no leading/trailing whitespace in names
  15. with pytest.raises(LocalProtocolError):
  16. normalize_and_validate([(b"foo ", "bar")])
  17. with pytest.raises(LocalProtocolError):
  18. normalize_and_validate([(b" foo", "bar")])
  19. # no weird characters in names
  20. with pytest.raises(LocalProtocolError) as excinfo:
  21. normalize_and_validate([(b"foo bar", b"baz")])
  22. assert "foo bar" in str(excinfo.value)
  23. with pytest.raises(LocalProtocolError):
  24. normalize_and_validate([(b"foo\x00bar", b"baz")])
  25. # Not even 8-bit characters:
  26. with pytest.raises(LocalProtocolError):
  27. normalize_and_validate([(b"foo\xffbar", b"baz")])
  28. # And not even the control characters we allow in values:
  29. with pytest.raises(LocalProtocolError):
  30. normalize_and_validate([(b"foo\x01bar", b"baz")])
  31. # no return or NUL characters in values
  32. with pytest.raises(LocalProtocolError) as excinfo:
  33. normalize_and_validate([("foo", "bar\rbaz")])
  34. assert "bar\\rbaz" in str(excinfo.value)
  35. with pytest.raises(LocalProtocolError):
  36. normalize_and_validate([("foo", "bar\nbaz")])
  37. with pytest.raises(LocalProtocolError):
  38. normalize_and_validate([("foo", "bar\x00baz")])
  39. # no leading/trailing whitespace
  40. with pytest.raises(LocalProtocolError):
  41. normalize_and_validate([("foo", "barbaz ")])
  42. with pytest.raises(LocalProtocolError):
  43. normalize_and_validate([("foo", " barbaz")])
  44. with pytest.raises(LocalProtocolError):
  45. normalize_and_validate([("foo", "barbaz\t")])
  46. with pytest.raises(LocalProtocolError):
  47. normalize_and_validate([("foo", "\tbarbaz")])
  48. # content-length
  49. assert normalize_and_validate([("Content-Length", "1")]) == [
  50. (b"content-length", b"1")
  51. ]
  52. with pytest.raises(LocalProtocolError):
  53. normalize_and_validate([("Content-Length", "asdf")])
  54. with pytest.raises(LocalProtocolError):
  55. normalize_and_validate([("Content-Length", "1x")])
  56. with pytest.raises(LocalProtocolError):
  57. normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")])
  58. assert normalize_and_validate(
  59. [("Content-Length", "0"), ("Content-Length", "0")]
  60. ) == [(b"content-length", b"0")]
  61. assert normalize_and_validate([("Content-Length", "0 , 0")]) == [
  62. (b"content-length", b"0")
  63. ]
  64. with pytest.raises(LocalProtocolError):
  65. normalize_and_validate(
  66. [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")]
  67. )
  68. with pytest.raises(LocalProtocolError):
  69. normalize_and_validate([("Content-Length", "1 , 1,2")])
  70. # transfer-encoding
  71. assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [
  72. (b"transfer-encoding", b"chunked")
  73. ]
  74. assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [
  75. (b"transfer-encoding", b"chunked")
  76. ]
  77. with pytest.raises(LocalProtocolError) as excinfo:
  78. normalize_and_validate([("Transfer-Encoding", "gzip")])
  79. assert excinfo.value.error_status_hint == 501 # Not Implemented
  80. with pytest.raises(LocalProtocolError) as excinfo:
  81. normalize_and_validate(
  82. [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")]
  83. )
  84. assert excinfo.value.error_status_hint == 501 # Not Implemented
  85. def test_get_set_comma_header() -> None:
  86. headers = normalize_and_validate(
  87. [
  88. ("Connection", "close"),
  89. ("whatever", "something"),
  90. ("connectiON", "fOo,, , BAR"),
  91. ]
  92. )
  93. assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"]
  94. headers = set_comma_header(headers, b"newthing", ["a", "b"]) # type: ignore
  95. with pytest.raises(LocalProtocolError):
  96. set_comma_header(headers, b"newthing", [" a", "b"]) # type: ignore
  97. assert headers == [
  98. (b"connection", b"close"),
  99. (b"whatever", b"something"),
  100. (b"connection", b"fOo,, , BAR"),
  101. (b"newthing", b"a"),
  102. (b"newthing", b"b"),
  103. ]
  104. headers = set_comma_header(headers, b"whatever", ["different thing"]) # type: ignore
  105. assert headers == [
  106. (b"connection", b"close"),
  107. (b"connection", b"fOo,, , BAR"),
  108. (b"newthing", b"a"),
  109. (b"newthing", b"b"),
  110. (b"whatever", b"different thing"),
  111. ]
  112. def test_has_100_continue() -> None:
  113. assert has_expect_100_continue(
  114. Request(
  115. method="GET",
  116. target="/",
  117. headers=[("Host", "example.com"), ("Expect", "100-continue")],
  118. )
  119. )
  120. assert not has_expect_100_continue(
  121. Request(method="GET", target="/", headers=[("Host", "example.com")])
  122. )
  123. # Case insensitive
  124. assert has_expect_100_continue(
  125. Request(
  126. method="GET",
  127. target="/",
  128. headers=[("Host", "example.com"), ("Expect", "100-Continue")],
  129. )
  130. )
  131. # Doesn't work in HTTP/1.0
  132. assert not has_expect_100_continue(
  133. Request(
  134. method="GET",
  135. target="/",
  136. headers=[("Host", "example.com"), ("Expect", "100-continue")],
  137. http_version="1.0",
  138. )
  139. )