mitmproxy.websocket
Mitmproxy used to have its own WebSocketFlow type until mitmproxy 6, but now WebSocket connections now are represented
as HTTP flows as well. They can be distinguished from regular HTTP requests by having the
mitmproxy.http.HTTPFlow.websocket
attribute set.
This module only defines the classes for individual WebSocketMessage
s and the WebSocketData
container.
1""" 2Mitmproxy used to have its own WebSocketFlow type until mitmproxy 6, but now WebSocket connections now are represented 3as HTTP flows as well. They can be distinguished from regular HTTP requests by having the 4`mitmproxy.http.HTTPFlow.websocket` attribute set. 5 6This module only defines the classes for individual `WebSocketMessage`s and the `WebSocketData` container. 7""" 8import time 9import warnings 10from typing import Union 11from typing import Optional 12 13from mitmproxy import stateobject 14from mitmproxy.coretypes import serializable 15from wsproto.frame_protocol import Opcode 16 17WebSocketMessageState = tuple[int, bool, bytes, float, bool, bool] 18 19 20class WebSocketMessage(serializable.Serializable): 21 """ 22 A single WebSocket message sent from one peer to the other. 23 24 Fragmented WebSocket messages are reassembled by mitmproxy and then 25 represented as a single instance of this class. 26 27 The [WebSocket RFC](https://tools.ietf.org/html/rfc6455) specifies both 28 text and binary messages. To avoid a whole class of nasty type confusion bugs, 29 mitmproxy stores all message contents as `bytes`. If you need a `str`, you can access the `text` property 30 on text messages: 31 32 >>> if message.is_text: 33 >>> text = message.text 34 """ 35 36 from_client: bool 37 """True if this messages was sent by the client.""" 38 type: Opcode 39 """ 40 The message type, as per RFC 6455's [opcode](https://tools.ietf.org/html/rfc6455#section-5.2). 41 42 Mitmproxy currently only exposes messages assembled from `TEXT` and `BINARY` frames. 43 """ 44 content: bytes 45 """A byte-string representing the content of this message.""" 46 timestamp: float 47 """Timestamp of when this message was received or created.""" 48 dropped: bool 49 """True if the message has not been forwarded by mitmproxy, False otherwise.""" 50 injected: bool 51 """True if the message was injected and did not originate from a client/server, False otherwise""" 52 53 def __init__( 54 self, 55 type: Union[int, Opcode], 56 from_client: bool, 57 content: bytes, 58 timestamp: Optional[float] = None, 59 dropped: bool = False, 60 injected: bool = False, 61 ) -> None: 62 self.from_client = from_client 63 self.type = Opcode(type) 64 self.content = content 65 self.timestamp: float = timestamp or time.time() 66 self.dropped = dropped 67 self.injected = injected 68 69 @classmethod 70 def from_state(cls, state: WebSocketMessageState): 71 return cls(*state) 72 73 def get_state(self) -> WebSocketMessageState: 74 return ( 75 int(self.type), 76 self.from_client, 77 self.content, 78 self.timestamp, 79 self.dropped, 80 self.injected, 81 ) 82 83 def set_state(self, state: WebSocketMessageState) -> None: 84 ( 85 typ, 86 self.from_client, 87 self.content, 88 self.timestamp, 89 self.dropped, 90 self.injected, 91 ) = state 92 self.type = Opcode(typ) 93 94 def __repr__(self): 95 if self.type == Opcode.TEXT: 96 return repr(self.content.decode(errors="replace")) 97 else: 98 return repr(self.content) 99 100 @property 101 def is_text(self) -> bool: 102 """ 103 `True` if this message is assembled from WebSocket `TEXT` frames, 104 `False` if it is assembled from `BINARY` frames. 105 """ 106 return self.type == Opcode.TEXT 107 108 def drop(self): 109 """Drop this message, i.e. don't forward it to the other peer.""" 110 self.dropped = True 111 112 def kill(self): # pragma: no cover 113 """A deprecated alias for `.drop()`.""" 114 warnings.warn( 115 "WebSocketMessage.kill() is deprecated, use .drop() instead.", 116 DeprecationWarning, 117 stacklevel=2, 118 ) 119 self.drop() 120 121 @property 122 def text(self) -> str: 123 """ 124 The message content as text. 125 126 This attribute is only available if `WebSocketMessage.is_text` is `True`. 127 128 *See also:* `WebSocketMessage.content` 129 """ 130 if self.type != Opcode.TEXT: 131 raise AttributeError( 132 f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute." 133 ) 134 135 return self.content.decode() 136 137 @text.setter 138 def text(self, value: str) -> None: 139 if self.type != Opcode.TEXT: 140 raise AttributeError( 141 f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute." 142 ) 143 144 self.content = value.encode() 145 146 147class WebSocketData(stateobject.StateObject): 148 """ 149 A data container for everything related to a single WebSocket connection. 150 This is typically accessed as `mitmproxy.http.HTTPFlow.websocket`. 151 """ 152 153 messages: list[WebSocketMessage] 154 """All `WebSocketMessage`s transferred over this connection.""" 155 156 closed_by_client: Optional[bool] = None 157 """ 158 `True` if the client closed the connection, 159 `False` if the server closed the connection, 160 `None` if the connection is active. 161 """ 162 close_code: Optional[int] = None 163 """[Close Code](https://tools.ietf.org/html/rfc6455#section-7.1.5)""" 164 close_reason: Optional[str] = None 165 """[Close Reason](https://tools.ietf.org/html/rfc6455#section-7.1.6)""" 166 167 timestamp_end: Optional[float] = None 168 """*Timestamp:* WebSocket connection closed.""" 169 170 _stateobject_attributes = dict( 171 messages=list[WebSocketMessage], 172 closed_by_client=bool, 173 close_code=int, 174 close_reason=str, 175 timestamp_end=float, 176 ) 177 178 def __init__(self): 179 self.messages = [] 180 181 def __repr__(self): 182 return f"<WebSocketData ({len(self.messages)} messages)>" 183 184 @classmethod 185 def from_state(cls, state): 186 d = WebSocketData() 187 d.set_state(state) 188 return d
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 then 26 represented as a single instance of this class. 27 28 The [WebSocket RFC](https://tools.ietf.org/html/rfc6455) specifies both 29 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` property 31 on text messages: 32 33 >>> if message.is_text: 34 >>> text = message.text 35 """ 36 37 from_client: bool 38 """True if this messages was sent by the client.""" 39 type: Opcode 40 """ 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: bytes 46 """A byte-string representing the content of this message.""" 47 timestamp: float 48 """Timestamp of when this message was received or created.""" 49 dropped: bool 50 """True if the message has not been forwarded by mitmproxy, False otherwise.""" 51 injected: bool 52 """True if the message was injected and did not originate from a client/server, False otherwise""" 53 54 def __init__( 55 self, 56 type: Union[int, Opcode], 57 from_client: bool, 58 content: bytes, 59 timestamp: Optional[float] = None, 60 dropped: bool = False, 61 injected: bool = False, 62 ) -> None: 63 self.from_client = from_client 64 self.type = Opcode(type) 65 self.content = content 66 self.timestamp: float = timestamp or time.time() 67 self.dropped = dropped 68 self.injected = injected 69 70 @classmethod 71 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 ) = state 93 self.type = Opcode(typ) 94 95 def __repr__(self): 96 if self.type == Opcode.TEXT: 97 return repr(self.content.decode(errors="replace")) 98 else: 99 return repr(self.content) 100 101 @property 102 def is_text(self) -> bool: 103 """ 104 `True` if this message is assembled from WebSocket `TEXT` frames, 105 `False` if it is assembled from `BINARY` frames. 106 """ 107 return self.type == Opcode.TEXT 108 109 def drop(self): 110 """Drop this message, i.e. don't forward it to the other peer.""" 111 self.dropped = True 112 113 def kill(self): # pragma: no cover 114 """A deprecated alias for `.drop()`.""" 115 warnings.warn( 116 "WebSocketMessage.kill() is deprecated, use .drop() instead.", 117 DeprecationWarning, 118 stacklevel=2, 119 ) 120 self.drop() 121 122 @property 123 def text(self) -> str: 124 """ 125 The message content as text. 126 127 This attribute is only available if `WebSocketMessage.is_text` is `True`. 128 129 *See also:* `WebSocketMessage.content` 130 """ 131 if self.type != Opcode.TEXT: 132 raise AttributeError( 133 f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute." 134 ) 135 136 return self.content.decode() 137 138 @text.setter 139 def text(self, value: str) -> None: 140 if self.type != Opcode.TEXT: 141 raise AttributeError( 142 f"{self.type.name.title()} WebSocket frames do not have a 'text' attribute." 143 ) 144 145 self.content = value.encode()
A single WebSocket message sent from one peer to the other.
Fragmented WebSocket messages are reassembled by mitmproxy and then represented as a single instance of this class.
The WebSocket RFC specifies both
text and binary messages. To avoid a whole class of nasty type confusion bugs,
mitmproxy stores all message contents as bytes
. If you need a str
, you can access the text
property
on text messages:
>>> if message.is_text:
>>> text = message.text
54 def __init__( 55 self, 56 type: Union[int, Opcode], 57 from_client: bool, 58 content: bytes, 59 timestamp: Optional[float] = None, 60 dropped: bool = False, 61 injected: bool = False, 62 ) -> None: 63 self.from_client = from_client 64 self.type = Opcode(type) 65 self.content = content 66 self.timestamp: float = timestamp or time.time() 67 self.dropped = dropped 68 self.injected = injected
True if the message was injected and did not originate from a client/server, False otherwise
True
if this message is assembled from WebSocket TEXT
frames,
False
if it is assembled from BINARY
frames.
109 def drop(self): 110 """Drop this message, i.e. don't forward it to the other peer.""" 111 self.dropped = True
Drop this message, i.e. don't forward it to the other peer.
113 def kill(self): # pragma: no cover 114 """A deprecated alias for `.drop()`.""" 115 warnings.warn( 116 "WebSocketMessage.kill() is deprecated, use .drop() instead.", 117 DeprecationWarning, 118 stacklevel=2, 119 ) 120 self.drop()
A deprecated alias for .drop()
.
The message content as text.
This attribute is only available if WebSocketMessage.is_text
is True
.
See also: WebSocketMessage.content
Inherited Members
- mitmproxy.coretypes.serializable.Serializable
- copy
148class WebSocketData(stateobject.StateObject): 149 """ 150 A data container for everything related to a single WebSocket connection. 151 This is typically accessed as `mitmproxy.http.HTTPFlow.websocket`. 152 """ 153 154 messages: list[WebSocketMessage] 155 """All `WebSocketMessage`s transferred over this connection.""" 156 157 closed_by_client: Optional[bool] = None 158 """ 159 `True` if the client closed the connection, 160 `False` if the server closed the connection, 161 `None` if the connection is active. 162 """ 163 close_code: Optional[int] = None 164 """[Close Code](https://tools.ietf.org/html/rfc6455#section-7.1.5)""" 165 close_reason: Optional[str] = None 166 """[Close Reason](https://tools.ietf.org/html/rfc6455#section-7.1.6)""" 167 168 timestamp_end: Optional[float] = None 169 """*Timestamp:* WebSocket connection closed.""" 170 171 _stateobject_attributes = dict( 172 messages=list[WebSocketMessage], 173 closed_by_client=bool, 174 close_code=int, 175 close_reason=str, 176 timestamp_end=float, 177 ) 178 179 def __init__(self): 180 self.messages = [] 181 182 def __repr__(self): 183 return f"<WebSocketData ({len(self.messages)} messages)>" 184 185 @classmethod 186 def from_state(cls, state): 187 d = WebSocketData() 188 d.set_state(state) 189 return d
A data container for everything related to a single WebSocket connection.
This is typically accessed as mitmproxy.http.HTTPFlow.websocket
.
All WebSocketMessage
s transferred over this connection.
True
if the client closed the connection,
False
if the server closed the connection,
None
if the connection is active.
Inherited Members
- mitmproxy.coretypes.serializable.Serializable
- copy