Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
websocket.py183 linesDownload Raw Back to mitmproxy
1"""2Mitmproxy used to have its own WebSocketFlow type until mitmproxy 6, but now WebSocket connections now are represented3as HTTP flows as well. They can be distinguished from regular HTTP requests by having the4`mitmproxy.http.HTTPFlow.websocket` attribute set.5 6This module only defines the classes for individual `WebSocketMessage`s and the `WebSocketData` container.7"""8 9import time10import warnings11from dataclasses import dataclass12from dataclasses import field13 14from wsproto.frame_protocol import Opcode15 16from mitmproxy.coretypes import serializable17 18WebSocketMessageState = tuple[int, bool, bytes, float, bool, bool]19 20 21class WebSocketMessage(serializable.Serializable):22    """23    A single WebSocket message sent from one peer to the other.24 25    Fragmented WebSocket messages are reassembled by mitmproxy and then26    represented as a single instance of this class.27 28    The [WebSocket RFC](https://tools.ietf.org/html/rfc6455) specifies both29    text and binary messages. To avoid a whole class of nasty type confusion bugs,30    mitmproxy stores all message contents as `bytes`. If you need a `str`, you can access the `text` property31    on text messages:32 33    >>> if message.is_text:34    >>>     text = message.text35    """36 37    from_client: bool38    """True if this messages was sent by the client."""39    type: Opcode40    """41    The message type, as per RFC 6455's [opcode](https://tools.ietf.org/html/rfc6455#section-5.2).42 43    Mitmproxy currently only exposes messages assembled from `TEXT` and `BINARY` frames.44    """45    content: bytes46    """A byte-string representing the content of this message."""47    timestamp: float48    """Timestamp of when this message was received or created."""49    dropped: bool50    """True if the message has not been forwarded by mitmproxy, False otherwise."""51    injected: bool52    """True if the message was injected and did not originate from a client/server, False otherwise"""53 54    def __init__(55        self,56        type: int | Opcode,57        from_client: bool,58        content: bytes,59        timestamp: float | None = None,60        dropped: bool = False,61        injected: bool = False,62    ) -> None:63        self.from_client = from_client64        self.type = Opcode(type)65        self.content = content66        self.timestamp: float = timestamp or time.time()67        self.dropped = dropped68        self.injected = injected69 70    @classmethod71    def from_state(cls, state: WebSocketMessageState):72        return cls(*state)73 74    def get_state(self) -> WebSocketMessageState:75        return (76            int(self.type),77            self.from_client,78            self.content,79            self.timestamp,80            self.dropped,81            self.injected,82        )83 84    def set_state(self, state: WebSocketMessageState) -> None:85        (86            typ,87            self.from_client,88            self.content,89            self.timestamp,90            self.dropped,91            self.injected,92        ) = state93        self.type = Opcode(typ)94 95    def _format_ws_message(self) -> bytes:96        if self.from_client:97            return b"[OUTGOING] " + self.content98        else:99            return b"[INCOMING] " + self.content100 101    def __repr__(self):102        if self.type == Opcode.TEXT:103            return repr(self.content.decode(errors="replace"))104        else:105            return repr(self.content)106 107    @property108    def is_text(self) -> bool:109        """110        `True` if this message is assembled from WebSocket `TEXT` frames,111        `False` if it is assembled from `BINARY` frames.112        """113        return self.type == Opcode.TEXT114 115    def drop(self):116        """Drop this message, i.e. don't forward it to the other peer."""117        self.dropped = True118 119    def kill(self):  # pragma: no cover120        """A deprecated alias for `.drop()`."""121        warnings.warn(122            "WebSocketMessage.kill() is deprecated, use .drop() instead.",123            DeprecationWarning,124            stacklevel=2,125        )126        self.drop()127 128    @property129    def text(self) -> str:130        """131        The message content as text.132 133        This attribute is only available if `WebSocketMessage.is_text` is `True`.134 135        *See also:* `WebSocketMessage.content`136        """137        if self.type != Opcode.TEXT:138            raise AttributeError(139                f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute."140            )141 142        return self.content.decode()143 144    @text.setter145    def text(self, value: str) -> None:146        if self.type != Opcode.TEXT:147            raise AttributeError(148                f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute."149            )150 151        self.content = value.encode()152 153 154@dataclass155class WebSocketData(serializable.SerializableDataclass):156    """157    A data container for everything related to a single WebSocket connection.158    This is typically accessed as `mitmproxy.http.HTTPFlow.websocket`.159    """160 161    messages: list[WebSocketMessage] = field(default_factory=list)162    """All `WebSocketMessage`s transferred over this connection."""163 164    closed_by_client: bool | None = None165    """166    `True` if the client closed the connection,167    `False` if the server closed the connection,168    `None` if the connection is active.169    """170    close_code: int | None = None171    """[Close Code](https://tools.ietf.org/html/rfc6455#section-7.1.5)"""172    close_reason: str | None = None173    """[Close Reason](https://tools.ietf.org/html/rfc6455#section-7.1.6)"""174 175    timestamp_end: float | None = None176    """*Timestamp:* WebSocket connection closed."""177 178    def __repr__(self):179        return f"<WebSocketData ({len(self.messages)} messages)>"180 181    def _get_formatted_messages(self) -> bytes:182        return b"\n".join(m._format_ws_message() for m in self.messages)183 
codekingpro/portable-devtools · Team Ai