events.py 20 KB

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