events.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. """
  2. h2/events
  3. ~~~~~~~~~
  4. Defines Event types for HTTP/2.
  5. Events are returned by the H2 state machine to allow implementations to keep
  6. track of events triggered by receiving data. Each time data is provided to the
  7. H2 state machine it processes the data and returns a list of Event objects.
  8. """
  9. from __future__ import annotations
  10. import binascii
  11. import sys
  12. from dataclasses import dataclass
  13. from typing import TYPE_CHECKING, Any
  14. from .settings import ChangedSetting, SettingCodes, Settings, _setting_code_from_int
  15. if TYPE_CHECKING: # pragma: no cover
  16. from hpack.struct import Header
  17. from hyperframe.frame import Frame
  18. from .errors import ErrorCodes
  19. if sys.version_info < (3, 10): # pragma: no cover
  20. kw_only: dict[str, bool] = {}
  21. else: # pragma: no cover
  22. kw_only = {"kw_only": True}
  23. _LAZY_INIT: Any = object()
  24. """
  25. Some h2 events are instantiated by the state machine, but its attributes are
  26. subsequently populated by H2Stream. To make this work with strict type annotations
  27. on the events, they are temporarily set to this placeholder value.
  28. This value should never be exposed to users.
  29. """
  30. class Event:
  31. """
  32. Base class for h2 events.
  33. """
  34. @dataclass(**kw_only)
  35. class RequestReceived(Event):
  36. """
  37. The RequestReceived event is fired whenever all of a request's headers
  38. are received. This event carries the HTTP headers for the given request
  39. and the stream ID of the new stream.
  40. In HTTP/2, headers may be sent as a HEADERS frame followed by zero or more
  41. CONTINUATION frames with the final frame setting the END_HEADERS flag.
  42. This event is fired after the entire sequence is received.
  43. .. versionchanged:: 2.3.0
  44. Changed the type of ``headers`` to :class:`HeaderTuple
  45. <hpack:hpack.HeaderTuple>`. This has no effect on current users.
  46. .. versionchanged:: 2.4.0
  47. Added ``stream_ended`` and ``priority_updated`` properties.
  48. """
  49. stream_id: int
  50. """The Stream ID for the stream this request was made on."""
  51. headers: list[Header] = _LAZY_INIT
  52. """The request headers."""
  53. stream_ended: StreamEnded | None = None
  54. """
  55. If this request also ended the stream, the associated
  56. :class:`StreamEnded <h2.events.StreamEnded>` event will be available
  57. here.
  58. .. versionadded:: 2.4.0
  59. """
  60. priority_updated: PriorityUpdated | None = None
  61. """
  62. If this request also had associated priority information, the
  63. associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
  64. event will be available here.
  65. .. versionadded:: 2.4.0
  66. """
  67. def __repr__(self) -> str:
  68. return f"<RequestReceived stream_id:{self.stream_id}, headers:{self.headers}>"
  69. @dataclass(**kw_only)
  70. class ResponseReceived(Event):
  71. """
  72. The ResponseReceived event is fired whenever response headers are received.
  73. This event carries the HTTP headers for the given response and the stream
  74. ID of the new stream.
  75. .. versionchanged:: 2.3.0
  76. Changed the type of ``headers`` to :class:`HeaderTuple
  77. <hpack:hpack.HeaderTuple>`. This has no effect on current users.
  78. .. versionchanged:: 2.4.0
  79. Added ``stream_ended`` and ``priority_updated`` properties.
  80. """
  81. stream_id: int
  82. """The Stream ID for the stream this response was made on."""
  83. headers: list[Header] = _LAZY_INIT
  84. """The response headers."""
  85. stream_ended: StreamEnded | None = None
  86. """
  87. If this response also ended the stream, the associated
  88. :class:`StreamEnded <h2.events.StreamEnded>` event will be available
  89. here.
  90. .. versionadded:: 2.4.0
  91. """
  92. priority_updated: PriorityUpdated | None = None
  93. """
  94. If this response also had associated priority information, the
  95. associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
  96. event will be available here.
  97. .. versionadded:: 2.4.0
  98. """
  99. def __repr__(self) -> str:
  100. return f"<ResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>"
  101. @dataclass(**kw_only)
  102. class TrailersReceived(Event):
  103. """
  104. The TrailersReceived event is fired whenever trailers are received on a
  105. stream. Trailers are a set of headers sent after the body of the
  106. request/response, and are used to provide information that wasn't known
  107. ahead of time (e.g. content-length). This event carries the HTTP header
  108. fields that form the trailers and the stream ID of the stream on which they
  109. were received.
  110. .. versionchanged:: 2.3.0
  111. Changed the type of ``headers`` to :class:`HeaderTuple
  112. <hpack:hpack.HeaderTuple>`. This has no effect on current users.
  113. .. versionchanged:: 2.4.0
  114. Added ``stream_ended`` and ``priority_updated`` properties.
  115. """
  116. stream_id: int
  117. """The Stream ID for the stream on which these trailers were received."""
  118. headers: list[Header] = _LAZY_INIT
  119. """The trailers themselves."""
  120. stream_ended: StreamEnded | None = None
  121. """
  122. Trailers always end streams. This property has the associated
  123. :class:`StreamEnded <h2.events.StreamEnded>` in it.
  124. .. versionadded:: 2.4.0
  125. """
  126. priority_updated: PriorityUpdated | None = None
  127. """
  128. If the trailers also set associated priority information, the
  129. associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
  130. event will be available here.
  131. .. versionadded:: 2.4.0
  132. """
  133. def __repr__(self) -> str:
  134. return f"<TrailersReceived stream_id:{self.stream_id}, headers:{self.headers}>"
  135. class _HeadersSent(Event):
  136. """
  137. The _HeadersSent event is fired whenever headers are sent.
  138. This is an internal event, used to determine validation steps on
  139. outgoing header blocks.
  140. """
  141. class _ResponseSent(_HeadersSent):
  142. """
  143. The _ResponseSent event is fired whenever response headers are sent
  144. on a stream.
  145. This is an internal event, used to determine validation steps on
  146. outgoing header blocks.
  147. """
  148. class _RequestSent(_HeadersSent):
  149. """
  150. The _RequestSent event is fired whenever request headers are sent
  151. on a stream.
  152. This is an internal event, used to determine validation steps on
  153. outgoing header blocks.
  154. """
  155. class _TrailersSent(_HeadersSent):
  156. """
  157. The _TrailersSent event is fired whenever trailers are sent on a
  158. stream. Trailers are a set of headers sent after the body of the
  159. request/response, and are used to provide information that wasn't known
  160. ahead of time (e.g. content-length).
  161. This is an internal event, used to determine validation steps on
  162. outgoing header blocks.
  163. """
  164. class _PushedRequestSent(_HeadersSent):
  165. """
  166. The _PushedRequestSent event is fired whenever pushed request headers are
  167. sent.
  168. This is an internal event, used to determine validation steps on outgoing
  169. header blocks.
  170. """
  171. @dataclass(**kw_only)
  172. class InformationalResponseReceived(Event):
  173. """
  174. The InformationalResponseReceived event is fired when an informational
  175. response (that is, one whose status code is a 1XX code) is received from
  176. the remote peer.
  177. The remote peer may send any number of these, from zero upwards. These
  178. responses are most commonly sent in response to requests that have the
  179. ``expect: 100-continue`` header field present. Most users can safely
  180. ignore this event unless you are intending to use the
  181. ``expect: 100-continue`` flow, or are for any reason expecting a different
  182. 1XX status code.
  183. .. versionadded:: 2.2.0
  184. .. versionchanged:: 2.3.0
  185. Changed the type of ``headers`` to :class:`HeaderTuple
  186. <hpack:hpack.HeaderTuple>`. This has no effect on current users.
  187. .. versionchanged:: 2.4.0
  188. Added ``priority_updated`` property.
  189. """
  190. stream_id: int
  191. """The Stream ID for the stream this informational response was made on."""
  192. headers: list[Header] = _LAZY_INIT
  193. """The headers for this informational response."""
  194. priority_updated: PriorityUpdated | None = None
  195. """
  196. If this response also had associated priority information, the
  197. associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
  198. event will be available here.
  199. .. versionadded:: 2.4.0
  200. """
  201. def __repr__(self) -> str:
  202. return f"<InformationalResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>"
  203. @dataclass(**kw_only)
  204. class DataReceived(Event):
  205. """
  206. The DataReceived event is fired whenever data is received on a stream from
  207. the remote peer. The event carries the data itself, and the stream ID on
  208. which the data was received.
  209. .. versionchanged:: 2.4.0
  210. Added ``stream_ended`` property.
  211. """
  212. stream_id: int
  213. """The Stream ID for the stream this data was received on."""
  214. data: bytes = _LAZY_INIT
  215. """The data itself."""
  216. flow_controlled_length: int = _LAZY_INIT
  217. """
  218. The amount of data received that counts against the flow control
  219. window. Note that padding counts against the flow control window, so
  220. when adjusting flow control you should always use this field rather
  221. than ``len(data)``.
  222. """
  223. stream_ended: StreamEnded | None = None
  224. """
  225. If this data chunk also completed the stream, the associated
  226. :class:`StreamEnded <h2.events.StreamEnded>` event will be available
  227. here.
  228. .. versionadded:: 2.4.0
  229. """
  230. def __repr__(self) -> str:
  231. return (
  232. "<DataReceived stream_id:{}, "
  233. "flow_controlled_length:{}, "
  234. "data:{}>".format(
  235. self.stream_id,
  236. self.flow_controlled_length,
  237. _bytes_representation(self.data[:20]) if self.data else "",
  238. )
  239. )
  240. @dataclass(**kw_only)
  241. class WindowUpdated(Event):
  242. """
  243. The WindowUpdated event is fired whenever a flow control window changes
  244. size. HTTP/2 defines flow control windows for connections and streams: this
  245. event fires for both connections and streams. The event carries the ID of
  246. the stream to which it applies (set to zero if the window update applies to
  247. the connection), and the delta in the window size.
  248. """
  249. stream_id: int
  250. """
  251. The Stream ID of the stream whose flow control window was changed.
  252. May be ``0`` if the connection window was changed.
  253. """
  254. delta: int = _LAZY_INIT
  255. """
  256. The window delta.
  257. """
  258. def __repr__(self) -> str:
  259. return f"<WindowUpdated stream_id:{self.stream_id}, delta:{self.delta}>"
  260. class RemoteSettingsChanged(Event):
  261. """
  262. The RemoteSettingsChanged event is fired whenever the remote peer changes
  263. its settings. It contains a complete inventory of changed settings,
  264. including their previous values.
  265. In HTTP/2, settings changes need to be acknowledged. h2 automatically
  266. acknowledges settings changes for efficiency. However, it is possible that
  267. the caller may not be happy with the changed setting.
  268. When this event is received, the caller should confirm that the new
  269. settings are acceptable. If they are not acceptable, the user should close
  270. the connection with the error code :data:`PROTOCOL_ERROR
  271. <h2.errors.ErrorCodes.PROTOCOL_ERROR>`.
  272. .. versionchanged:: 2.0.0
  273. Prior to this version the user needed to acknowledge settings changes.
  274. This is no longer the case: h2 now automatically acknowledges
  275. them.
  276. """
  277. def __init__(self) -> None:
  278. #: A dictionary of setting byte to
  279. #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing
  280. #: the changed settings.
  281. self.changed_settings: dict[int, ChangedSetting] = {}
  282. @classmethod
  283. def from_settings(cls,
  284. old_settings: Settings | dict[int, int],
  285. new_settings: dict[int, int]) -> RemoteSettingsChanged:
  286. """
  287. Build a RemoteSettingsChanged event from a set of changed settings.
  288. :param old_settings: A complete collection of old settings, in the form
  289. of a dictionary of ``{setting: value}``.
  290. :param new_settings: All the changed settings and their new values, in
  291. the form of a dictionary of ``{setting: value}``.
  292. """
  293. e = cls()
  294. for setting, new_value in new_settings.items():
  295. s = _setting_code_from_int(setting)
  296. original_value = old_settings.get(s)
  297. change = ChangedSetting(s, original_value, new_value)
  298. e.changed_settings[s] = change
  299. return e
  300. def __repr__(self) -> str:
  301. return "<RemoteSettingsChanged changed_settings:{{{}}}>".format(
  302. ", ".join(repr(cs) for cs in self.changed_settings.values()),
  303. )
  304. @dataclass(**kw_only)
  305. class PingReceived(Event):
  306. """
  307. The PingReceived event is fired whenever a PING is received. It contains
  308. the 'opaque data' of the PING frame. A ping acknowledgment with the same
  309. 'opaque data' is automatically emitted after receiving a ping.
  310. .. versionadded:: 3.1.0
  311. """
  312. ping_data: bytes
  313. """The data included on the ping."""
  314. def __repr__(self) -> str:
  315. return f"<PingReceived ping_data:{_bytes_representation(self.ping_data)}>"
  316. @dataclass(**kw_only)
  317. class PingAckReceived(Event):
  318. """
  319. The PingAckReceived event is fired whenever a PING acknowledgment is
  320. received. It contains the 'opaque data' of the PING+ACK frame, allowing the
  321. user to correlate PINGs and calculate RTT.
  322. .. versionadded:: 3.1.0
  323. .. versionchanged:: 4.0.0
  324. Removed deprecated but equivalent ``PingAcknowledged``.
  325. """
  326. ping_data: bytes
  327. """The data included on the ping."""
  328. def __repr__(self) -> str:
  329. return f"<PingAckReceived ping_data:{_bytes_representation(self.ping_data)}>"
  330. @dataclass(**kw_only)
  331. class StreamEnded(Event):
  332. """
  333. The StreamEnded event is fired whenever a stream is ended by a remote
  334. party. The stream may not be fully closed if it has not been closed
  335. locally, but no further data or headers should be expected on that stream.
  336. """
  337. stream_id: int
  338. """The Stream ID of the stream that was closed."""
  339. def __repr__(self) -> str:
  340. return f"<StreamEnded stream_id:{self.stream_id}>"
  341. @dataclass(**kw_only)
  342. class StreamReset(Event):
  343. """
  344. The StreamReset event is fired in two situations. The first is when the
  345. remote party forcefully resets the stream. The second is when the remote
  346. party has made a protocol error which only affects a single stream. In this
  347. case, h2 will terminate the stream early and return this event.
  348. .. versionchanged:: 2.0.0
  349. This event is now fired when h2 automatically resets a stream.
  350. """
  351. stream_id: int
  352. """
  353. The Stream ID of the stream that was reset.
  354. """
  355. error_code: ErrorCodes | int = _LAZY_INIT
  356. """
  357. The error code given.
  358. """
  359. remote_reset: bool = True
  360. """
  361. Whether the remote peer sent a RST_STREAM or we did.
  362. """
  363. def __repr__(self) -> str:
  364. return f"<StreamReset stream_id:{self.stream_id}, error_code:{self.error_code!s}, remote_reset:{self.remote_reset}>"
  365. class PushedStreamReceived(Event):
  366. """
  367. The PushedStreamReceived event is fired whenever a pushed stream has been
  368. received from a remote peer. The event carries on it the new stream ID, the
  369. ID of the parent stream, and the request headers pushed by the remote peer.
  370. """
  371. def __init__(self) -> None:
  372. #: The Stream ID of the stream created by the push.
  373. self.pushed_stream_id: int | None = None
  374. #: The Stream ID of the stream that the push is related to.
  375. self.parent_stream_id: int | None = None
  376. #: The request headers, sent by the remote party in the push.
  377. self.headers: list[Header] | None = None
  378. def __repr__(self) -> str:
  379. return (
  380. f"<PushedStreamReceived pushed_stream_id:{self.pushed_stream_id}, parent_stream_id:{self.parent_stream_id}, "
  381. f"headers:{self.headers}>"
  382. )
  383. class SettingsAcknowledged(Event):
  384. """
  385. The SettingsAcknowledged event is fired whenever a settings ACK is received
  386. from the remote peer. The event carries on it the settings that were
  387. acknowedged, in the same format as
  388. :class:`h2.events.RemoteSettingsChanged`.
  389. """
  390. def __init__(self) -> None:
  391. #: A dictionary of setting byte to
  392. #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing
  393. #: the changed settings.
  394. self.changed_settings: dict[SettingCodes | int, ChangedSetting] = {}
  395. def __repr__(self) -> str:
  396. s = ", ".join(repr(cs) for cs in self.changed_settings.values())
  397. return f"<SettingsAcknowledged changed_settings:{{{s}}}>"
  398. class PriorityUpdated(Event):
  399. """
  400. The PriorityUpdated event is fired whenever a stream sends updated priority
  401. information. This can occur when the stream is opened, or at any time
  402. during the stream lifetime.
  403. This event is purely advisory, and does not need to be acted on.
  404. .. versionadded:: 2.0.0
  405. """
  406. def __init__(self) -> None:
  407. #: The ID of the stream whose priority information is being updated.
  408. self.stream_id: int | None = None
  409. #: The new stream weight. May be the same as the original stream
  410. #: weight. An integer between 1 and 256.
  411. self.weight: int | None = None
  412. #: The stream ID this stream now depends on. May be ``0``.
  413. self.depends_on: int | None = None
  414. #: Whether the stream *exclusively* depends on the parent stream. If it
  415. #: does, this stream should inherit the current children of its new
  416. #: parent.
  417. self.exclusive: bool | None = None
  418. def __repr__(self) -> str:
  419. return (
  420. f"<PriorityUpdated stream_id:{self.stream_id}, weight:{self.weight}, depends_on:{self.depends_on}, "
  421. f"exclusive:{self.exclusive}>"
  422. )
  423. class ConnectionTerminated(Event):
  424. """
  425. The ConnectionTerminated event is fired when a connection is torn down by
  426. the remote peer using a GOAWAY frame. Once received, no further action may
  427. be taken on the connection: a new connection must be established.
  428. """
  429. def __init__(self) -> None:
  430. #: The error code cited when tearing down the connection. Should be
  431. #: one of :class:`ErrorCodes <h2.errors.ErrorCodes>`, but may not be if
  432. #: unknown HTTP/2 extensions are being used.
  433. self.error_code: ErrorCodes | int | None = None
  434. #: The stream ID of the last stream the remote peer saw. This can
  435. #: provide an indication of what data, if any, never reached the remote
  436. #: peer and so can safely be resent.
  437. self.last_stream_id: int | None = None
  438. #: Additional debug data that can be appended to GOAWAY frame.
  439. self.additional_data: bytes | None = None
  440. def __repr__(self) -> str:
  441. return (
  442. "<ConnectionTerminated error_code:{!s}, last_stream_id:{}, "
  443. "additional_data:{}>".format(
  444. self.error_code,
  445. self.last_stream_id,
  446. _bytes_representation(
  447. self.additional_data[:20]
  448. if self.additional_data else None),
  449. )
  450. )
  451. class AlternativeServiceAvailable(Event):
  452. """
  453. The AlternativeServiceAvailable event is fired when the remote peer
  454. advertises an `RFC 7838 <https://tools.ietf.org/html/rfc7838>`_ Alternative
  455. Service using an ALTSVC frame.
  456. This event always carries the origin to which the ALTSVC information
  457. applies. That origin is either supplied by the server directly, or inferred
  458. by h2 from the ``:authority`` pseudo-header field that was sent by
  459. the user when initiating a given stream.
  460. This event also carries what RFC 7838 calls the "Alternative Service Field
  461. Value", which is formatted like a HTTP header field and contains the
  462. relevant alternative service information. h2 does not parse or in any
  463. way modify that information: the user is required to do that.
  464. This event can only be fired on the client end of a connection.
  465. .. versionadded:: 2.3.0
  466. """
  467. def __init__(self) -> None:
  468. #: The origin to which the alternative service field value applies.
  469. #: This field is either supplied by the server directly, or inferred by
  470. #: h2 from the ``:authority`` pseudo-header field that was sent
  471. #: by the user when initiating the stream on which the frame was
  472. #: received.
  473. self.origin: bytes | None = None
  474. #: The ALTSVC field value. This contains information about the HTTP
  475. #: alternative service being advertised by the server. h2 does
  476. #: not parse this field: it is left exactly as sent by the server. The
  477. #: structure of the data in this field is given by `RFC 7838 Section 3
  478. #: <https://tools.ietf.org/html/rfc7838#section-3>`_.
  479. self.field_value: bytes | None = None
  480. def __repr__(self) -> str:
  481. return (
  482. "<AlternativeServiceAvailable origin:{}, field_value:{}>".format(
  483. (self.origin or b"").decode("utf-8", "ignore"),
  484. (self.field_value or b"").decode("utf-8", "ignore"),
  485. )
  486. )
  487. @dataclass(**kw_only)
  488. class UnknownFrameReceived(Event):
  489. """
  490. The UnknownFrameReceived event is fired when the remote peer sends a frame
  491. that h2 does not understand. This occurs primarily when the remote
  492. peer is employing HTTP/2 extensions that h2 doesn't know anything
  493. about.
  494. RFC 7540 requires that HTTP/2 implementations ignore these frames. h2
  495. does so. However, this event is fired to allow implementations to perform
  496. special processing on those frames if needed (e.g. if the implementation
  497. is capable of handling the frame itself).
  498. .. versionadded:: 2.7.0
  499. """
  500. frame: Frame
  501. def __repr__(self) -> str:
  502. return "<UnknownFrameReceived>"
  503. def _bytes_representation(data: bytes | None) -> str | None:
  504. """
  505. Converts a bytestring into something that is safe to print on all Python
  506. platforms.
  507. This function is relatively expensive, so it should not be called on the
  508. mainline of the code. It's safe to use in things like object repr methods
  509. though.
  510. """
  511. if data is None:
  512. return None
  513. return binascii.hexlify(data).decode("ascii")