diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/h2/events.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/h2/events.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/h2/events.py | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/h2/events.py b/.venv/lib/python3.12/site-packages/h2/events.py new file mode 100644 index 00000000..b81fd1a6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h2/events.py @@ -0,0 +1,639 @@ +""" +h2/events +~~~~~~~~~ + +Defines Event types for HTTP/2. + +Events are returned by the H2 state machine to allow implementations to keep +track of events triggered by receiving data. Each time data is provided to the +H2 state machine it processes the data and returns a list of Event objects. +""" +from __future__ import annotations + +import binascii +from typing import TYPE_CHECKING + +from .settings import ChangedSetting, SettingCodes, Settings, _setting_code_from_int + +if TYPE_CHECKING: # pragma: no cover + from hpack import HeaderTuple + from hyperframe.frame import Frame + + from .errors import ErrorCodes + + +class Event: + """ + Base class for h2 events. + """ + + + +class RequestReceived(Event): + """ + The RequestReceived event is fired whenever all of a request's headers + are received. This event carries the HTTP headers for the given request + and the stream ID of the new stream. + + In HTTP/2, headers may be sent as a HEADERS frame followed by zero or more + CONTINUATION frames with the final frame setting the END_HEADERS flag. + This event is fired after the entire sequence is received. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + <hpack:hpack.HeaderTuple>`. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self) -> None: + #: The Stream ID for the stream this request was made on. + self.stream_id: int | None = None + + #: The request headers. + self.headers: list[HeaderTuple] | None = None + + #: If this request also ended the stream, the associated + #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended: StreamEnded | None = None + + #: If this request also had associated priority information, the + #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated: PriorityUpdated | None = None + + def __repr__(self) -> str: + return f"<RequestReceived stream_id:{self.stream_id}, headers:{self.headers}>" + + +class ResponseReceived(Event): + """ + The ResponseReceived event is fired whenever response headers are received. + This event carries the HTTP headers for the given response and the stream + ID of the new stream. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + <hpack:hpack.HeaderTuple>`. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self) -> None: + #: The Stream ID for the stream this response was made on. + self.stream_id: int | None = None + + #: The response headers. + self.headers: list[HeaderTuple] | None = None + + #: If this response also ended the stream, the associated + #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended: StreamEnded | None = None + + #: If this response also had associated priority information, the + #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated: PriorityUpdated | None = None + + def __repr__(self) -> str: + return f"<ResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>" + + +class TrailersReceived(Event): + """ + The TrailersReceived event is fired whenever trailers are received on a + stream. Trailers are a set of headers sent after the body of the + request/response, and are used to provide information that wasn't known + ahead of time (e.g. content-length). This event carries the HTTP header + fields that form the trailers and the stream ID of the stream on which they + were received. + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + <hpack:hpack.HeaderTuple>`. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` and ``priority_updated`` properties. + """ + + def __init__(self) -> None: + #: The Stream ID for the stream on which these trailers were received. + self.stream_id: int | None = None + + #: The trailers themselves. + self.headers: list[HeaderTuple] | None = None + + #: Trailers always end streams. This property has the associated + #: :class:`StreamEnded <h2.events.StreamEnded>` in it. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended: StreamEnded | None = None + + #: If the trailers also set associated priority information, the + #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated: PriorityUpdated | None = None + + def __repr__(self) -> str: + return f"<TrailersReceived stream_id:{self.stream_id}, headers:{self.headers}>" + + +class _HeadersSent(Event): + """ + The _HeadersSent event is fired whenever headers are sent. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + + +class _ResponseSent(_HeadersSent): + """ + The _ResponseSent event is fired whenever response headers are sent + on a stream. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + + +class _RequestSent(_HeadersSent): + """ + The _RequestSent event is fired whenever request headers are sent + on a stream. + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + + +class _TrailersSent(_HeadersSent): + """ + The _TrailersSent event is fired whenever trailers are sent on a + stream. Trailers are a set of headers sent after the body of the + request/response, and are used to provide information that wasn't known + ahead of time (e.g. content-length). + + This is an internal event, used to determine validation steps on + outgoing header blocks. + """ + + + +class _PushedRequestSent(_HeadersSent): + """ + The _PushedRequestSent event is fired whenever pushed request headers are + sent. + + This is an internal event, used to determine validation steps on outgoing + header blocks. + """ + + + +class InformationalResponseReceived(Event): + """ + The InformationalResponseReceived event is fired when an informational + response (that is, one whose status code is a 1XX code) is received from + the remote peer. + + The remote peer may send any number of these, from zero upwards. These + responses are most commonly sent in response to requests that have the + ``expect: 100-continue`` header field present. Most users can safely + ignore this event unless you are intending to use the + ``expect: 100-continue`` flow, or are for any reason expecting a different + 1XX status code. + + .. versionadded:: 2.2.0 + + .. versionchanged:: 2.3.0 + Changed the type of ``headers`` to :class:`HeaderTuple + <hpack:hpack.HeaderTuple>`. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``priority_updated`` property. + """ + + def __init__(self) -> None: + #: The Stream ID for the stream this informational response was made + #: on. + self.stream_id: int | None = None + + #: The headers for this informational response. + self.headers: list[HeaderTuple] | None = None + + #: If this response also had associated priority information, the + #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>` + #: event will be available here. + #: + #: .. versionadded:: 2.4.0 + self.priority_updated: PriorityUpdated | None = None + + def __repr__(self) -> str: + return f"<InformationalResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>" + + +class DataReceived(Event): + """ + The DataReceived event is fired whenever data is received on a stream from + the remote peer. The event carries the data itself, and the stream ID on + which the data was received. + + .. versionchanged:: 2.4.0 + Added ``stream_ended`` property. + """ + + def __init__(self) -> None: + #: The Stream ID for the stream this data was received on. + self.stream_id: int | None = None + + #: The data itself. + self.data: bytes | None = None + + #: The amount of data received that counts against the flow control + #: window. Note that padding counts against the flow control window, so + #: when adjusting flow control you should always use this field rather + #: than ``len(data)``. + self.flow_controlled_length: int | None = None + + #: If this data chunk also completed the stream, the associated + #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available + #: here. + #: + #: .. versionadded:: 2.4.0 + self.stream_ended: StreamEnded | None = None + + def __repr__(self) -> str: + return ( + "<DataReceived stream_id:{}, " + "flow_controlled_length:{}, " + "data:{}>".format( + self.stream_id, + self.flow_controlled_length, + _bytes_representation(self.data[:20]) if self.data else "", + ) + ) + + +class WindowUpdated(Event): + """ + The WindowUpdated event is fired whenever a flow control window changes + size. HTTP/2 defines flow control windows for connections and streams: this + event fires for both connections and streams. The event carries the ID of + the stream to which it applies (set to zero if the window update applies to + the connection), and the delta in the window size. + """ + + def __init__(self) -> None: + #: The Stream ID of the stream whose flow control window was changed. + #: May be ``0`` if the connection window was changed. + self.stream_id: int | None = None + + #: The window delta. + self.delta: int | None = None + + def __repr__(self) -> str: + return f"<WindowUpdated stream_id:{self.stream_id}, delta:{self.delta}>" + + +class RemoteSettingsChanged(Event): + """ + The RemoteSettingsChanged event is fired whenever the remote peer changes + its settings. It contains a complete inventory of changed settings, + including their previous values. + + In HTTP/2, settings changes need to be acknowledged. h2 automatically + acknowledges settings changes for efficiency. However, it is possible that + the caller may not be happy with the changed setting. + + When this event is received, the caller should confirm that the new + settings are acceptable. If they are not acceptable, the user should close + the connection with the error code :data:`PROTOCOL_ERROR + <h2.errors.ErrorCodes.PROTOCOL_ERROR>`. + + .. versionchanged:: 2.0.0 + Prior to this version the user needed to acknowledge settings changes. + This is no longer the case: h2 now automatically acknowledges + them. + """ + + def __init__(self) -> None: + #: A dictionary of setting byte to + #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing + #: the changed settings. + self.changed_settings: dict[int, ChangedSetting] = {} + + @classmethod + def from_settings(cls, + old_settings: Settings | dict[int, int], + new_settings: dict[int, int]) -> RemoteSettingsChanged: + """ + Build a RemoteSettingsChanged event from a set of changed settings. + + :param old_settings: A complete collection of old settings, in the form + of a dictionary of ``{setting: value}``. + :param new_settings: All the changed settings and their new values, in + the form of a dictionary of ``{setting: value}``. + """ + e = cls() + for setting, new_value in new_settings.items(): + s = _setting_code_from_int(setting) + original_value = old_settings.get(s) + change = ChangedSetting(s, original_value, new_value) + e.changed_settings[s] = change + + return e + + def __repr__(self) -> str: + return "<RemoteSettingsChanged changed_settings:{{{}}}>".format( + ", ".join(repr(cs) for cs in self.changed_settings.values()), + ) + + +class PingReceived(Event): + """ + The PingReceived event is fired whenever a PING is received. It contains + the 'opaque data' of the PING frame. A ping acknowledgment with the same + 'opaque data' is automatically emitted after receiving a ping. + + .. versionadded:: 3.1.0 + """ + + def __init__(self) -> None: + #: The data included on the ping. + self.ping_data: bytes | None = None + + def __repr__(self) -> str: + return f"<PingReceived ping_data:{_bytes_representation(self.ping_data)}>" + + +class PingAckReceived(Event): + """ + The PingAckReceived event is fired whenever a PING acknowledgment is + received. It contains the 'opaque data' of the PING+ACK frame, allowing the + user to correlate PINGs and calculate RTT. + + .. versionadded:: 3.1.0 + + .. versionchanged:: 4.0.0 + Removed deprecated but equivalent ``PingAcknowledged``. + """ + + def __init__(self) -> None: + #: The data included on the ping. + self.ping_data: bytes | None = None + + def __repr__(self) -> str: + return f"<PingAckReceived ping_data:{_bytes_representation(self.ping_data)}>" + + +class StreamEnded(Event): + """ + The StreamEnded event is fired whenever a stream is ended by a remote + party. The stream may not be fully closed if it has not been closed + locally, but no further data or headers should be expected on that stream. + """ + + def __init__(self) -> None: + #: The Stream ID of the stream that was closed. + self.stream_id: int | None = None + + def __repr__(self) -> str: + return f"<StreamEnded stream_id:{self.stream_id}>" + + +class StreamReset(Event): + """ + The StreamReset event is fired in two situations. The first is when the + remote party forcefully resets the stream. The second is when the remote + party has made a protocol error which only affects a single stream. In this + case, h2 will terminate the stream early and return this event. + + .. versionchanged:: 2.0.0 + This event is now fired when h2 automatically resets a stream. + """ + + def __init__(self) -> None: + #: The Stream ID of the stream that was reset. + self.stream_id: int | None = None + + #: The error code given. Either one of :class:`ErrorCodes + #: <h2.errors.ErrorCodes>` or ``int`` + self.error_code: ErrorCodes | None = None + + #: Whether the remote peer sent a RST_STREAM or we did. + self.remote_reset = True + + def __repr__(self) -> str: + return f"<StreamReset stream_id:{self.stream_id}, error_code:{self.error_code!s}, remote_reset:{self.remote_reset}>" + + +class PushedStreamReceived(Event): + """ + The PushedStreamReceived event is fired whenever a pushed stream has been + received from a remote peer. The event carries on it the new stream ID, the + ID of the parent stream, and the request headers pushed by the remote peer. + """ + + def __init__(self) -> None: + #: The Stream ID of the stream created by the push. + self.pushed_stream_id: int | None = None + + #: The Stream ID of the stream that the push is related to. + self.parent_stream_id: int | None = None + + #: The request headers, sent by the remote party in the push. + self.headers: list[HeaderTuple] | None = None + + def __repr__(self) -> str: + return ( + f"<PushedStreamReceived pushed_stream_id:{self.pushed_stream_id}, parent_stream_id:{self.parent_stream_id}, " + f"headers:{self.headers}>" + ) + + +class SettingsAcknowledged(Event): + """ + The SettingsAcknowledged event is fired whenever a settings ACK is received + from the remote peer. The event carries on it the settings that were + acknowedged, in the same format as + :class:`h2.events.RemoteSettingsChanged`. + """ + + def __init__(self) -> None: + #: A dictionary of setting byte to + #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing + #: the changed settings. + self.changed_settings: dict[SettingCodes | int, ChangedSetting] = {} + + def __repr__(self) -> str: + s = ", ".join(repr(cs) for cs in self.changed_settings.values()) + return f"<SettingsAcknowledged changed_settings:{{{s}}}>" + + +class PriorityUpdated(Event): + """ + The PriorityUpdated event is fired whenever a stream sends updated priority + information. This can occur when the stream is opened, or at any time + during the stream lifetime. + + This event is purely advisory, and does not need to be acted on. + + .. versionadded:: 2.0.0 + """ + + def __init__(self) -> None: + #: The ID of the stream whose priority information is being updated. + self.stream_id: int | None = None + + #: The new stream weight. May be the same as the original stream + #: weight. An integer between 1 and 256. + self.weight: int | None = None + + #: The stream ID this stream now depends on. May be ``0``. + self.depends_on: int | None = None + + #: Whether the stream *exclusively* depends on the parent stream. If it + #: does, this stream should inherit the current children of its new + #: parent. + self.exclusive: bool | None = None + + def __repr__(self) -> str: + return ( + f"<PriorityUpdated stream_id:{self.stream_id}, weight:{self.weight}, depends_on:{self.depends_on}, " + f"exclusive:{self.exclusive}>" + ) + + +class ConnectionTerminated(Event): + """ + The ConnectionTerminated event is fired when a connection is torn down by + the remote peer using a GOAWAY frame. Once received, no further action may + be taken on the connection: a new connection must be established. + """ + + def __init__(self) -> None: + #: The error code cited when tearing down the connection. Should be + #: one of :class:`ErrorCodes <h2.errors.ErrorCodes>`, but may not be if + #: unknown HTTP/2 extensions are being used. + self.error_code: ErrorCodes | int | None = None + + #: The stream ID of the last stream the remote peer saw. This can + #: provide an indication of what data, if any, never reached the remote + #: peer and so can safely be resent. + self.last_stream_id: int | None = None + + #: Additional debug data that can be appended to GOAWAY frame. + self.additional_data: bytes | None = None + + def __repr__(self) -> str: + return ( + "<ConnectionTerminated error_code:{!s}, last_stream_id:{}, " + "additional_data:{}>".format( + self.error_code, + self.last_stream_id, + _bytes_representation( + self.additional_data[:20] + if self.additional_data else None), + ) + ) + + +class AlternativeServiceAvailable(Event): + """ + The AlternativeServiceAvailable event is fired when the remote peer + advertises an `RFC 7838 <https://tools.ietf.org/html/rfc7838>`_ Alternative + Service using an ALTSVC frame. + + This event always carries the origin to which the ALTSVC information + applies. That origin is either supplied by the server directly, or inferred + by h2 from the ``:authority`` pseudo-header field that was sent by + the user when initiating a given stream. + + This event also carries what RFC 7838 calls the "Alternative Service Field + Value", which is formatted like a HTTP header field and contains the + relevant alternative service information. h2 does not parse or in any + way modify that information: the user is required to do that. + + This event can only be fired on the client end of a connection. + + .. versionadded:: 2.3.0 + """ + + def __init__(self) -> None: + #: The origin to which the alternative service field value applies. + #: This field is either supplied by the server directly, or inferred by + #: h2 from the ``:authority`` pseudo-header field that was sent + #: by the user when initiating the stream on which the frame was + #: received. + self.origin: bytes | None = None + + #: The ALTSVC field value. This contains information about the HTTP + #: alternative service being advertised by the server. h2 does + #: not parse this field: it is left exactly as sent by the server. The + #: structure of the data in this field is given by `RFC 7838 Section 3 + #: <https://tools.ietf.org/html/rfc7838#section-3>`_. + self.field_value: bytes | None = None + + def __repr__(self) -> str: + return ( + "<AlternativeServiceAvailable origin:{}, field_value:{}>".format( + (self.origin or b"").decode("utf-8", "ignore"), + (self.field_value or b"").decode("utf-8", "ignore"), + ) + ) + + +class UnknownFrameReceived(Event): + """ + The UnknownFrameReceived event is fired when the remote peer sends a frame + that h2 does not understand. This occurs primarily when the remote + peer is employing HTTP/2 extensions that h2 doesn't know anything + about. + + RFC 7540 requires that HTTP/2 implementations ignore these frames. h2 + does so. However, this event is fired to allow implementations to perform + special processing on those frames if needed (e.g. if the implementation + is capable of handling the frame itself). + + .. versionadded:: 2.7.0 + """ + + def __init__(self) -> None: + #: The hyperframe Frame object that encapsulates the received frame. + self.frame: Frame | None = None + + def __repr__(self) -> str: + return "<UnknownFrameReceived>" + + +def _bytes_representation(data: bytes | None) -> str | None: + """ + Converts a bytestring into something that is safe to print on all Python + platforms. + + This function is relatively expensive, so it should not be called on the + mainline of the code. It's safe to use in things like object repr methods + though. + """ + if data is None: + return None + + return binascii.hexlify(data).decode("ascii") |