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.
 
 
 
 

370 lines
12 KiB

  1. # High level events that make up HTTP/1.1 conversations. Loosely inspired by
  2. # the corresponding events in hyper-h2:
  3. #
  4. # http://python-hyper.org/h2/en/stable/api.html#events
  5. #
  6. # Don't subclass these. Stuff will break.
  7. import re
  8. from abc import ABC
  9. from dataclasses import dataclass
  10. from typing import List, Tuple, Union
  11. from ._abnf import method, request_target
  12. from ._headers import Headers, normalize_and_validate
  13. from ._util import bytesify, LocalProtocolError, validate
  14. # Everything in __all__ gets re-exported as part of the h11 public API.
  15. __all__ = [
  16. "Event",
  17. "Request",
  18. "InformationalResponse",
  19. "Response",
  20. "Data",
  21. "EndOfMessage",
  22. "ConnectionClosed",
  23. ]
  24. method_re = re.compile(method.encode("ascii"))
  25. request_target_re = re.compile(request_target.encode("ascii"))
  26. class Event(ABC):
  27. """
  28. Base class for h11 events.
  29. """
  30. __slots__ = ()
  31. @dataclass(init=False, frozen=True)
  32. class Request(Event):
  33. """The beginning of an HTTP request.
  34. Fields:
  35. .. attribute:: method
  36. An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte
  37. string. :term:`Bytes-like objects <bytes-like object>` and native
  38. strings containing only ascii characters will be automatically
  39. converted to byte strings.
  40. .. attribute:: target
  41. The target of an HTTP request, e.g. ``b"/index.html"``, or one of the
  42. more exotic formats described in `RFC 7320, section 5.3
  43. <https://tools.ietf.org/html/rfc7230#section-5.3>`_. Always a byte
  44. string. :term:`Bytes-like objects <bytes-like object>` and native
  45. strings containing only ascii characters will be automatically
  46. converted to byte strings.
  47. .. attribute:: headers
  48. Request headers, represented as a list of (name, value) pairs. See
  49. :ref:`the header normalization rules <headers-format>` for details.
  50. .. attribute:: http_version
  51. The HTTP protocol version, represented as a byte string like
  52. ``b"1.1"``. See :ref:`the HTTP version normalization rules
  53. <http_version-format>` for details.
  54. """
  55. __slots__ = ("method", "headers", "target", "http_version")
  56. method: bytes
  57. headers: Headers
  58. target: bytes
  59. http_version: bytes
  60. def __init__(
  61. self,
  62. *,
  63. method: Union[bytes, str],
  64. headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
  65. target: Union[bytes, str],
  66. http_version: Union[bytes, str] = b"1.1",
  67. _parsed: bool = False,
  68. ) -> None:
  69. super().__init__()
  70. if isinstance(headers, Headers):
  71. object.__setattr__(self, "headers", headers)
  72. else:
  73. object.__setattr__(
  74. self, "headers", normalize_and_validate(headers, _parsed=_parsed)
  75. )
  76. if not _parsed:
  77. object.__setattr__(self, "method", bytesify(method))
  78. object.__setattr__(self, "target", bytesify(target))
  79. object.__setattr__(self, "http_version", bytesify(http_version))
  80. else:
  81. object.__setattr__(self, "method", method)
  82. object.__setattr__(self, "target", target)
  83. object.__setattr__(self, "http_version", http_version)
  84. # "A server MUST respond with a 400 (Bad Request) status code to any
  85. # HTTP/1.1 request message that lacks a Host header field and to any
  86. # request message that contains more than one Host header field or a
  87. # Host header field with an invalid field-value."
  88. # -- https://tools.ietf.org/html/rfc7230#section-5.4
  89. host_count = 0
  90. for name, value in self.headers:
  91. if name == b"host":
  92. host_count += 1
  93. if self.http_version == b"1.1" and host_count == 0:
  94. raise LocalProtocolError("Missing mandatory Host: header")
  95. if host_count > 1:
  96. raise LocalProtocolError("Found multiple Host: headers")
  97. validate(method_re, self.method, "Illegal method characters")
  98. validate(request_target_re, self.target, "Illegal target characters")
  99. # This is an unhashable type.
  100. __hash__ = None # type: ignore
  101. @dataclass(init=False, frozen=True)
  102. class _ResponseBase(Event):
  103. __slots__ = ("headers", "http_version", "reason", "status_code")
  104. headers: Headers
  105. http_version: bytes
  106. reason: bytes
  107. status_code: int
  108. def __init__(
  109. self,
  110. *,
  111. headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
  112. status_code: int,
  113. http_version: Union[bytes, str] = b"1.1",
  114. reason: Union[bytes, str] = b"",
  115. _parsed: bool = False,
  116. ) -> None:
  117. super().__init__()
  118. if isinstance(headers, Headers):
  119. object.__setattr__(self, "headers", headers)
  120. else:
  121. object.__setattr__(
  122. self, "headers", normalize_and_validate(headers, _parsed=_parsed)
  123. )
  124. if not _parsed:
  125. object.__setattr__(self, "reason", bytesify(reason))
  126. object.__setattr__(self, "http_version", bytesify(http_version))
  127. if not isinstance(status_code, int):
  128. raise LocalProtocolError("status code must be integer")
  129. # Because IntEnum objects are instances of int, but aren't
  130. # duck-compatible (sigh), see gh-72.
  131. object.__setattr__(self, "status_code", int(status_code))
  132. else:
  133. object.__setattr__(self, "reason", reason)
  134. object.__setattr__(self, "http_version", http_version)
  135. object.__setattr__(self, "status_code", status_code)
  136. self.__post_init__()
  137. def __post_init__(self) -> None:
  138. pass
  139. # This is an unhashable type.
  140. __hash__ = None # type: ignore
  141. @dataclass(init=False, frozen=True)
  142. class InformationalResponse(_ResponseBase):
  143. """An HTTP informational response.
  144. Fields:
  145. .. attribute:: status_code
  146. The status code of this response, as an integer. For an
  147. :class:`InformationalResponse`, this is always in the range [100,
  148. 200).
  149. .. attribute:: headers
  150. Request headers, represented as a list of (name, value) pairs. See
  151. :ref:`the header normalization rules <headers-format>` for
  152. details.
  153. .. attribute:: http_version
  154. The HTTP protocol version, represented as a byte string like
  155. ``b"1.1"``. See :ref:`the HTTP version normalization rules
  156. <http_version-format>` for details.
  157. .. attribute:: reason
  158. The reason phrase of this response, as a byte string. For example:
  159. ``b"OK"``, or ``b"Not Found"``.
  160. """
  161. def __post_init__(self) -> None:
  162. if not (100 <= self.status_code < 200):
  163. raise LocalProtocolError(
  164. "InformationalResponse status_code should be in range "
  165. "[100, 200), not {}".format(self.status_code)
  166. )
  167. # This is an unhashable type.
  168. __hash__ = None # type: ignore
  169. @dataclass(init=False, frozen=True)
  170. class Response(_ResponseBase):
  171. """The beginning of an HTTP response.
  172. Fields:
  173. .. attribute:: status_code
  174. The status code of this response, as an integer. For an
  175. :class:`Response`, this is always in the range [200,
  176. 1000).
  177. .. attribute:: headers
  178. Request headers, represented as a list of (name, value) pairs. See
  179. :ref:`the header normalization rules <headers-format>` for details.
  180. .. attribute:: http_version
  181. The HTTP protocol version, represented as a byte string like
  182. ``b"1.1"``. See :ref:`the HTTP version normalization rules
  183. <http_version-format>` for details.
  184. .. attribute:: reason
  185. The reason phrase of this response, as a byte string. For example:
  186. ``b"OK"``, or ``b"Not Found"``.
  187. """
  188. def __post_init__(self) -> None:
  189. if not (200 <= self.status_code < 1000):
  190. raise LocalProtocolError(
  191. "Response status_code should be in range [200, 1000), not {}".format(
  192. self.status_code
  193. )
  194. )
  195. # This is an unhashable type.
  196. __hash__ = None # type: ignore
  197. @dataclass(init=False, frozen=True)
  198. class Data(Event):
  199. """Part of an HTTP message body.
  200. Fields:
  201. .. attribute:: data
  202. A :term:`bytes-like object` containing part of a message body. Or, if
  203. using the ``combine=False`` argument to :meth:`Connection.send`, then
  204. any object that your socket writing code knows what to do with, and for
  205. which calling :func:`len` returns the number of bytes that will be
  206. written -- see :ref:`sendfile` for details.
  207. .. attribute:: chunk_start
  208. A marker that indicates whether this data object is from the start of a
  209. chunked transfer encoding chunk. This field is ignored when when a Data
  210. event is provided to :meth:`Connection.send`: it is only valid on
  211. events emitted from :meth:`Connection.next_event`. You probably
  212. shouldn't use this attribute at all; see
  213. :ref:`chunk-delimiters-are-bad` for details.
  214. .. attribute:: chunk_end
  215. A marker that indicates whether this data object is the last for a
  216. given chunked transfer encoding chunk. This field is ignored when when
  217. a Data event is provided to :meth:`Connection.send`: it is only valid
  218. on events emitted from :meth:`Connection.next_event`. You probably
  219. shouldn't use this attribute at all; see
  220. :ref:`chunk-delimiters-are-bad` for details.
  221. """
  222. __slots__ = ("data", "chunk_start", "chunk_end")
  223. data: bytes
  224. chunk_start: bool
  225. chunk_end: bool
  226. def __init__(
  227. self, data: bytes, chunk_start: bool = False, chunk_end: bool = False
  228. ) -> None:
  229. object.__setattr__(self, "data", data)
  230. object.__setattr__(self, "chunk_start", chunk_start)
  231. object.__setattr__(self, "chunk_end", chunk_end)
  232. # This is an unhashable type.
  233. __hash__ = None # type: ignore
  234. # XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that
  235. # are forbidden to be sent in a trailer, since processing them as if they were
  236. # present in the header section might bypass external security filters."
  237. # https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part
  238. # Unfortunately, the list of forbidden fields is long and vague :-/
  239. @dataclass(init=False, frozen=True)
  240. class EndOfMessage(Event):
  241. """The end of an HTTP message.
  242. Fields:
  243. .. attribute:: headers
  244. Default value: ``[]``
  245. Any trailing headers attached to this message, represented as a list of
  246. (name, value) pairs. See :ref:`the header normalization rules
  247. <headers-format>` for details.
  248. Must be empty unless ``Transfer-Encoding: chunked`` is in use.
  249. """
  250. __slots__ = ("headers",)
  251. headers: Headers
  252. def __init__(
  253. self,
  254. *,
  255. headers: Union[
  256. Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None
  257. ] = None,
  258. _parsed: bool = False,
  259. ) -> None:
  260. super().__init__()
  261. if headers is None:
  262. headers = Headers([])
  263. elif not isinstance(headers, Headers):
  264. headers = normalize_and_validate(headers, _parsed=_parsed)
  265. object.__setattr__(self, "headers", headers)
  266. # This is an unhashable type.
  267. __hash__ = None # type: ignore
  268. @dataclass(frozen=True)
  269. class ConnectionClosed(Event):
  270. """This event indicates that the sender has closed their outgoing
  271. connection.
  272. Note that this does not necessarily mean that they can't *receive* further
  273. data, because TCP connections are composed to two one-way channels which
  274. can be closed independently. See :ref:`closing` for details.
  275. No fields.
  276. """
  277. pass