Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
events.py680 linesDownload Raw Back to h2
1"""2h2/events3~~~~~~~~~4 5Defines Event types for HTTP/2.6 7Events are returned by the H2 state machine to allow implementations to keep8track of events triggered by receiving data. Each time data is provided to the9H2 state machine it processes the data and returns a list of Event objects.10"""11from __future__ import annotations12 13import binascii14import sys15from dataclasses import dataclass16from typing import TYPE_CHECKING, Any17 18from .settings import ChangedSetting, SettingCodes, Settings, _setting_code_from_int19 20if TYPE_CHECKING:  # pragma: no cover21    from hpack.struct import Header22    from hyperframe.frame import Frame23 24    from .errors import ErrorCodes25 26 27if sys.version_info < (3, 10):  # pragma: no cover28    kw_only: dict[str, bool] = {}29else:  # pragma: no cover30    kw_only = {"kw_only": True}31 32 33_LAZY_INIT: Any = object()34"""35Some h2 events are instantiated by the state machine, but its attributes are36subsequently populated by H2Stream. To make this work with strict type annotations37on the events, they are temporarily set to this placeholder value.38This value should never be exposed to users.39"""40 41 42class Event:43    """44    Base class for h2 events.45    """46 47 48@dataclass(**kw_only)49class RequestReceived(Event):50    """51    The RequestReceived event is fired whenever all of a request's headers52    are received. This event carries the HTTP headers for the given request53    and the stream ID of the new stream.54 55    In HTTP/2, headers may be sent as a HEADERS frame followed by zero or more56    CONTINUATION frames with the final frame setting the END_HEADERS flag.57    This event is fired after the entire sequence is received.58 59    .. versionchanged:: 2.3.060       Changed the type of ``headers`` to :class:`HeaderTuple61       <hpack:hpack.HeaderTuple>`. This has no effect on current users.62 63    .. versionchanged:: 2.4.064       Added ``stream_ended`` and ``priority_updated`` properties.65    """66 67    stream_id: int68    """The Stream ID for the stream this request was made on."""69 70    headers: list[Header] = _LAZY_INIT71    """The request headers."""72 73    stream_ended: StreamEnded | None = None74    """75    If this request also ended the stream, the associated76    :class:`StreamEnded <h2.events.StreamEnded>` event will be available77    here.78 79    .. versionadded:: 2.4.080    """81 82    priority_updated: PriorityUpdated | None = None83    """84    If this request also had associated priority information, the85    associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`86    event will be available here.87 88    .. versionadded:: 2.4.089    """90 91    def __repr__(self) -> str:92        return f"<RequestReceived stream_id:{self.stream_id}, headers:{self.headers}>"93 94 95@dataclass(**kw_only)96class ResponseReceived(Event):97    """98    The ResponseReceived event is fired whenever response headers are received.99    This event carries the HTTP headers for the given response and the stream100    ID of the new stream.101 102    .. versionchanged:: 2.3.0103       Changed the type of ``headers`` to :class:`HeaderTuple104       <hpack:hpack.HeaderTuple>`. This has no effect on current users.105 106    .. versionchanged:: 2.4.0107      Added ``stream_ended`` and ``priority_updated`` properties.108    """109 110    stream_id: int111    """The Stream ID for the stream this response was made on."""112 113    headers: list[Header] = _LAZY_INIT114    """The response headers."""115 116    stream_ended: StreamEnded | None = None117    """118    If this response also ended the stream, the associated119    :class:`StreamEnded <h2.events.StreamEnded>` event will be available120    here.121 122    .. versionadded:: 2.4.0123    """124 125    priority_updated: PriorityUpdated | None = None126    """127    If this response also had associated priority information, the128    associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`129    event will be available here.130 131    .. versionadded:: 2.4.0132    """133 134    def __repr__(self) -> str:135        return f"<ResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>"136 137 138@dataclass(**kw_only)139class TrailersReceived(Event):140    """141    The TrailersReceived event is fired whenever trailers are received on a142    stream. Trailers are a set of headers sent after the body of the143    request/response, and are used to provide information that wasn't known144    ahead of time (e.g. content-length). This event carries the HTTP header145    fields that form the trailers and the stream ID of the stream on which they146    were received.147 148    .. versionchanged:: 2.3.0149       Changed the type of ``headers`` to :class:`HeaderTuple150       <hpack:hpack.HeaderTuple>`. This has no effect on current users.151 152    .. versionchanged:: 2.4.0153       Added ``stream_ended`` and ``priority_updated`` properties.154    """155 156    stream_id: int157    """The Stream ID for the stream on which these trailers were received."""158 159    headers: list[Header] = _LAZY_INIT160    """The trailers themselves."""161 162    stream_ended: StreamEnded | None = None163    """164    Trailers always end streams. This property has the associated165    :class:`StreamEnded <h2.events.StreamEnded>` in it.166 167    .. versionadded:: 2.4.0168    """169 170    priority_updated: PriorityUpdated | None = None171    """172    If the trailers also set associated priority information, the173    associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`174    event will be available here.175 176    .. versionadded:: 2.4.0177    """178 179    def __repr__(self) -> str:180        return f"<TrailersReceived stream_id:{self.stream_id}, headers:{self.headers}>"181 182 183class _HeadersSent(Event):184    """185    The _HeadersSent event is fired whenever headers are sent.186 187    This is an internal event, used to determine validation steps on188    outgoing header blocks.189    """190 191 192 193class _ResponseSent(_HeadersSent):194    """195    The _ResponseSent event is fired whenever response headers are sent196    on a stream.197 198    This is an internal event, used to determine validation steps on199    outgoing header blocks.200    """201 202 203 204class _RequestSent(_HeadersSent):205    """206    The _RequestSent event is fired whenever request headers are sent207    on a stream.208 209    This is an internal event, used to determine validation steps on210    outgoing header blocks.211    """212 213 214 215class _TrailersSent(_HeadersSent):216    """217    The _TrailersSent event is fired whenever trailers are sent on a218    stream. Trailers are a set of headers sent after the body of the219    request/response, and are used to provide information that wasn't known220    ahead of time (e.g. content-length).221 222    This is an internal event, used to determine validation steps on223    outgoing header blocks.224    """225 226 227 228class _PushedRequestSent(_HeadersSent):229    """230    The _PushedRequestSent event is fired whenever pushed request headers are231    sent.232 233    This is an internal event, used to determine validation steps on outgoing234    header blocks.235    """236 237 238@dataclass(**kw_only)239class InformationalResponseReceived(Event):240    """241    The InformationalResponseReceived event is fired when an informational242    response (that is, one whose status code is a 1XX code) is received from243    the remote peer.244 245    The remote peer may send any number of these, from zero upwards. These246    responses are most commonly sent in response to requests that have the247    ``expect: 100-continue`` header field present. Most users can safely248    ignore this event unless you are intending to use the249    ``expect: 100-continue`` flow, or are for any reason expecting a different250    1XX status code.251 252    .. versionadded:: 2.2.0253 254    .. versionchanged:: 2.3.0255       Changed the type of ``headers`` to :class:`HeaderTuple256       <hpack:hpack.HeaderTuple>`. This has no effect on current users.257 258    .. versionchanged:: 2.4.0259       Added ``priority_updated`` property.260    """261 262    stream_id: int263    """The Stream ID for the stream this informational response was made on."""264 265    headers: list[Header] = _LAZY_INIT266    """The headers for this informational response."""267 268    priority_updated: PriorityUpdated | None = None269    """270    If this response also had associated priority information, the271    associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`272    event will be available here.273 274    .. versionadded:: 2.4.0275    """276 277    def __repr__(self) -> str:278        return f"<InformationalResponseReceived stream_id:{self.stream_id}, headers:{self.headers}>"279 280 281@dataclass(**kw_only)282class DataReceived(Event):283    """284    The DataReceived event is fired whenever data is received on a stream from285    the remote peer. The event carries the data itself, and the stream ID on286    which the data was received.287 288    .. versionchanged:: 2.4.0289       Added ``stream_ended`` property.290    """291 292    stream_id: int293    """The Stream ID for the stream this data was received on."""294 295    data: bytes = _LAZY_INIT296    """The data itself."""297 298    flow_controlled_length: int = _LAZY_INIT299    """300    The amount of data received that counts against the flow control301    window. Note that padding counts against the flow control window, so302    when adjusting flow control you should always use this field rather303    than ``len(data)``.304    """305 306    stream_ended: StreamEnded | None = None307    """308    If this data chunk also completed the stream, the associated309    :class:`StreamEnded <h2.events.StreamEnded>` event will be available310    here.311 312    .. versionadded:: 2.4.0313    """314 315    def __repr__(self) -> str:316        return (317            "<DataReceived stream_id:{}, "318            "flow_controlled_length:{}, "319            "data:{}>".format(320                self.stream_id,321                self.flow_controlled_length,322                _bytes_representation(self.data[:20]) if self.data else "",323            )324        )325 326 327@dataclass(**kw_only)328class WindowUpdated(Event):329    """330    The WindowUpdated event is fired whenever a flow control window changes331    size. HTTP/2 defines flow control windows for connections and streams: this332    event fires for both connections and streams. The event carries the ID of333    the stream to which it applies (set to zero if the window update applies to334    the connection), and the delta in the window size.335    """336 337    stream_id: int338    """339    The Stream ID of the stream whose flow control window was changed.340    May be ``0`` if the connection window was changed.341    """342 343    delta: int = _LAZY_INIT344    """345    The window delta.346    """347 348    def __repr__(self) -> str:349        return f"<WindowUpdated stream_id:{self.stream_id}, delta:{self.delta}>"350 351 352class RemoteSettingsChanged(Event):353    """354    The RemoteSettingsChanged event is fired whenever the remote peer changes355    its settings. It contains a complete inventory of changed settings,356    including their previous values.357 358    In HTTP/2, settings changes need to be acknowledged. h2 automatically359    acknowledges settings changes for efficiency. However, it is possible that360    the caller may not be happy with the changed setting.361 362    When this event is received, the caller should confirm that the new363    settings are acceptable. If they are not acceptable, the user should close364    the connection with the error code :data:`PROTOCOL_ERROR365    <h2.errors.ErrorCodes.PROTOCOL_ERROR>`.366 367    .. versionchanged:: 2.0.0368       Prior to this version the user needed to acknowledge settings changes.369       This is no longer the case: h2 now automatically acknowledges370       them.371    """372 373    def __init__(self) -> None:374        #: A dictionary of setting byte to375        #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing376        #: the changed settings.377        self.changed_settings: dict[int, ChangedSetting] = {}378 379    @classmethod380    def from_settings(cls,381                      old_settings: Settings | dict[int, int],382                      new_settings: dict[int, int]) -> RemoteSettingsChanged:383        """384        Build a RemoteSettingsChanged event from a set of changed settings.385 386        :param old_settings: A complete collection of old settings, in the form387                             of a dictionary of ``{setting: value}``.388        :param new_settings: All the changed settings and their new values, in389                             the form of a dictionary of ``{setting: value}``.390        """391        e = cls()392        for setting, new_value in new_settings.items():393            s = _setting_code_from_int(setting)394            original_value = old_settings.get(s)395            change = ChangedSetting(s, original_value, new_value)396            e.changed_settings[s] = change397 398        return e399 400    def __repr__(self) -> str:401        return "<RemoteSettingsChanged changed_settings:{{{}}}>".format(402            ", ".join(repr(cs) for cs in self.changed_settings.values()),403        )404 405 406@dataclass(**kw_only)407class PingReceived(Event):408    """409    The PingReceived event is fired whenever a PING is received. It contains410    the 'opaque data' of the PING frame. A ping acknowledgment with the same411    'opaque data' is automatically emitted after receiving a ping.412 413    .. versionadded:: 3.1.0414    """415 416    ping_data: bytes417    """The data included on the ping."""418 419    def __repr__(self) -> str:420        return f"<PingReceived ping_data:{_bytes_representation(self.ping_data)}>"421 422 423@dataclass(**kw_only)424class PingAckReceived(Event):425    """426    The PingAckReceived event is fired whenever a PING acknowledgment is427    received. It contains the 'opaque data' of the PING+ACK frame, allowing the428    user to correlate PINGs and calculate RTT.429 430    .. versionadded:: 3.1.0431 432    .. versionchanged:: 4.0.0433       Removed deprecated but equivalent ``PingAcknowledged``.434    """435 436    ping_data: bytes437    """The data included on the ping."""438 439    def __repr__(self) -> str:440        return f"<PingAckReceived ping_data:{_bytes_representation(self.ping_data)}>"441 442 443@dataclass(**kw_only)444class StreamEnded(Event):445    """446    The StreamEnded event is fired whenever a stream is ended by a remote447    party. The stream may not be fully closed if it has not been closed448    locally, but no further data or headers should be expected on that stream.449    """450 451    stream_id: int452    """The Stream ID of the stream that was closed."""453 454    def __repr__(self) -> str:455        return f"<StreamEnded stream_id:{self.stream_id}>"456 457 458@dataclass(**kw_only)459class StreamReset(Event):460    """461    The StreamReset event is fired in two situations. The first is when the462    remote party forcefully resets the stream. The second is when the remote463    party has made a protocol error which only affects a single stream. In this464    case, h2 will terminate the stream early and return this event.465 466    .. versionchanged:: 2.0.0467       This event is now fired when h2 automatically resets a stream.468    """469 470    stream_id: int471    """472    The Stream ID of the stream that was reset.473    """474 475    error_code: ErrorCodes | int = _LAZY_INIT476    """477    The error code given.478    """479 480    remote_reset: bool = True481    """482    Whether the remote peer sent a RST_STREAM or we did.483    """484 485    def __repr__(self) -> str:486        return f"<StreamReset stream_id:{self.stream_id}, error_code:{self.error_code!s}, remote_reset:{self.remote_reset}>"487 488 489class PushedStreamReceived(Event):490    """491    The PushedStreamReceived event is fired whenever a pushed stream has been492    received from a remote peer. The event carries on it the new stream ID, the493    ID of the parent stream, and the request headers pushed by the remote peer.494    """495 496    def __init__(self) -> None:497        #: The Stream ID of the stream created by the push.498        self.pushed_stream_id: int | None = None499 500        #: The Stream ID of the stream that the push is related to.501        self.parent_stream_id: int | None = None502 503        #: The request headers, sent by the remote party in the push.504        self.headers: list[Header] | None = None505 506    def __repr__(self) -> str:507        return (508            f"<PushedStreamReceived pushed_stream_id:{self.pushed_stream_id}, parent_stream_id:{self.parent_stream_id}, "509            f"headers:{self.headers}>"510        )511 512 513class SettingsAcknowledged(Event):514    """515    The SettingsAcknowledged event is fired whenever a settings ACK is received516    from the remote peer. The event carries on it the settings that were517    acknowedged, in the same format as518    :class:`h2.events.RemoteSettingsChanged`.519    """520 521    def __init__(self) -> None:522        #: A dictionary of setting byte to523        #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing524        #: the changed settings.525        self.changed_settings: dict[SettingCodes | int, ChangedSetting] = {}526 527    def __repr__(self) -> str:528        s = ", ".join(repr(cs) for cs in self.changed_settings.values())529        return f"<SettingsAcknowledged changed_settings:{{{s}}}>"530 531 532class PriorityUpdated(Event):533    """534    The PriorityUpdated event is fired whenever a stream sends updated priority535    information. This can occur when the stream is opened, or at any time536    during the stream lifetime.537 538    This event is purely advisory, and does not need to be acted on.539 540    .. versionadded:: 2.0.0541    """542 543    def __init__(self) -> None:544        #: The ID of the stream whose priority information is being updated.545        self.stream_id: int | None = None546 547        #: The new stream weight. May be the same as the original stream548        #: weight. An integer between 1 and 256.549        self.weight: int | None = None550 551        #: The stream ID this stream now depends on. May be ``0``.552        self.depends_on: int | None = None553 554        #: Whether the stream *exclusively* depends on the parent stream. If it555        #: does, this stream should inherit the current children of its new556        #: parent.557        self.exclusive: bool | None = None558 559    def __repr__(self) -> str:560        return (561            f"<PriorityUpdated stream_id:{self.stream_id}, weight:{self.weight}, depends_on:{self.depends_on}, "562            f"exclusive:{self.exclusive}>"563        )564 565 566class ConnectionTerminated(Event):567    """568    The ConnectionTerminated event is fired when a connection is torn down by569    the remote peer using a GOAWAY frame. Once received, no further action may570    be taken on the connection: a new connection must be established.571    """572 573    def __init__(self) -> None:574        #: The error code cited when tearing down the connection. Should be575        #: one of :class:`ErrorCodes <h2.errors.ErrorCodes>`, but may not be if576        #: unknown HTTP/2 extensions are being used.577        self.error_code: ErrorCodes | int | None = None578 579        #: The stream ID of the last stream the remote peer saw. This can580        #: provide an indication of what data, if any, never reached the remote581        #: peer and so can safely be resent.582        self.last_stream_id: int | None = None583 584        #: Additional debug data that can be appended to GOAWAY frame.585        self.additional_data: bytes | None = None586 587    def __repr__(self) -> str:588        return (589            "<ConnectionTerminated error_code:{!s}, last_stream_id:{}, "590            "additional_data:{}>".format(591                self.error_code,592                self.last_stream_id,593                _bytes_representation(594                    self.additional_data[:20]595                    if self.additional_data else None),596            )597        )598 599 600class AlternativeServiceAvailable(Event):601    """602    The AlternativeServiceAvailable event is fired when the remote peer603    advertises an `RFC 7838 <https://tools.ietf.org/html/rfc7838>`_ Alternative604    Service using an ALTSVC frame.605 606    This event always carries the origin to which the ALTSVC information607    applies. That origin is either supplied by the server directly, or inferred608    by h2 from the ``:authority`` pseudo-header field that was sent by609    the user when initiating a given stream.610 611    This event also carries what RFC 7838 calls the "Alternative Service Field612    Value", which is formatted like a HTTP header field and contains the613    relevant alternative service information. h2 does not parse or in any614    way modify that information: the user is required to do that.615 616    This event can only be fired on the client end of a connection.617 618    .. versionadded:: 2.3.0619    """620 621    def __init__(self) -> None:622        #: The origin to which the alternative service field value applies.623        #: This field is either supplied by the server directly, or inferred by624        #: h2 from the ``:authority`` pseudo-header field that was sent625        #: by the user when initiating the stream on which the frame was626        #: received.627        self.origin: bytes | None = None628 629        #: The ALTSVC field value. This contains information about the HTTP630        #: alternative service being advertised by the server. h2 does631        #: not parse this field: it is left exactly as sent by the server. The632        #: structure of the data in this field is given by `RFC 7838 Section 3633        #: <https://tools.ietf.org/html/rfc7838#section-3>`_.634        self.field_value: bytes | None = None635 636    def __repr__(self) -> str:637        return (638            "<AlternativeServiceAvailable origin:{}, field_value:{}>".format(639                (self.origin or b"").decode("utf-8", "ignore"),640                (self.field_value or b"").decode("utf-8", "ignore"),641            )642        )643 644 645@dataclass(**kw_only)646class UnknownFrameReceived(Event):647    """648    The UnknownFrameReceived event is fired when the remote peer sends a frame649    that h2 does not understand. This occurs primarily when the remote650    peer is employing HTTP/2 extensions that h2 doesn't know anything651    about.652 653    RFC 7540 requires that HTTP/2 implementations ignore these frames. h2654    does so. However, this event is fired to allow implementations to perform655    special processing on those frames if needed (e.g. if the implementation656    is capable of handling the frame itself).657 658    .. versionadded:: 2.7.0659    """660 661    frame: Frame662 663    def __repr__(self) -> str:664        return "<UnknownFrameReceived>"665 666 667def _bytes_representation(data: bytes | None) -> str | None:668    """669    Converts a bytestring into something that is safe to print on all Python670    platforms.671 672    This function is relatively expensive, so it should not be called on the673    mainline of the code. It's safe to use in things like object repr methods674    though.675    """676    if data is None:677        return None678 679    return binascii.hexlify(data).decode("ascii")680 
codekingpro/portable-devtools · Team Ai