Edit on GitHub

mitmproxy.connection

  1import dataclasses
  2import time
  3import uuid
  4import warnings
  5from abc import ABCMeta
  6from collections.abc import Sequence
  7from dataclasses import dataclass
  8from dataclasses import field
  9from enum import Flag
 10from typing import Literal
 11
 12from mitmproxy import certs
 13from mitmproxy.coretypes import serializable
 14from mitmproxy.net import server_spec
 15from mitmproxy.proxy import mode_specs
 16from mitmproxy.utils import human
 17
 18
 19class ConnectionState(Flag):
 20    """The current state of the underlying socket."""
 21
 22    CLOSED = 0
 23    CAN_READ = 1
 24    CAN_WRITE = 2
 25    OPEN = CAN_READ | CAN_WRITE
 26
 27
 28TransportProtocol = Literal["tcp", "udp"]
 29
 30# https://docs.openssl.org/master/man3/SSL_get_version/#return-values
 31TlsVersion = Literal[
 32    "SSLv3",
 33    "TLSv1",
 34    "TLSv1.1",
 35    "TLSv1.2",
 36    "TLSv1.3",
 37    "DTLSv0.9",
 38    "DTLSv1",
 39    "DTLSv1.2",
 40    "QUICv1",
 41]
 42
 43# practically speaking we may have IPv6 addresses with flowinfo and scope_id,
 44# but type checking isn't good enough to properly handle tuple unions.
 45# this version at least provides useful type checking messages.
 46Address = tuple[str, int]
 47
 48kw_only = {"kw_only": True}
 49
 50
 51# noinspection PyDataclass
 52@dataclass(**kw_only)
 53class Connection(serializable.SerializableDataclass, metaclass=ABCMeta):
 54    """
 55    Base class for client and server connections.
 56
 57    The connection object only exposes metadata about the connection, but not the underlying socket object.
 58    This is intentional, all I/O should be handled by `mitmproxy.proxy.server` exclusively.
 59    """
 60
 61    peername: Address | None
 62    """The remote's `(ip, port)` tuple for this connection."""
 63    sockname: Address | None
 64    """Our local `(ip, port)` tuple for this connection."""
 65
 66    state: ConnectionState = field(
 67        default=ConnectionState.CLOSED, metadata={"serialize": False}
 68    )
 69    """The current connection state."""
 70
 71    # all connections have a unique id. While
 72    # f.client_conn == f2.client_conn already holds true for live flows (where we have object identity),
 73    # we also want these semantics for recorded flows.
 74    id: str = field(default_factory=lambda: str(uuid.uuid4()))
 75    """A unique UUID to identify the connection."""
 76    transport_protocol: TransportProtocol = field(default="tcp")
 77    """The connection protocol in use."""
 78    error: str | None = None
 79    """
 80    A string describing a general error with connections to this address.
 81
 82    The purpose of this property is to signal that new connections to the particular endpoint should not be attempted,
 83    for example because it uses an untrusted TLS certificate. Regular (unexpected) disconnects do not set the error
 84    property. This property is only reused per client connection.
 85    """
 86
 87    tls: bool = False
 88    """
 89    `True` if TLS should be established, `False` otherwise.
 90    Note that this property only describes if a connection should eventually be protected using TLS.
 91    To check if TLS has already been established, use `Connection.tls_established`.
 92    """
 93    certificate_list: Sequence[certs.Cert] = ()
 94    """
 95    The TLS certificate list as sent by the peer.
 96    The first certificate is the end-entity certificate.
 97
 98    > [RFC 8446] Prior to TLS 1.3, "certificate_list" ordering required each
 99    > certificate to certify the one immediately preceding it; however,
100    > some implementations allowed some flexibility.  Servers sometimes
101    > send both a current and deprecated intermediate for transitional
102    > purposes, and others are simply configured incorrectly, but these
103    > cases can nonetheless be validated properly.  For maximum
104    > compatibility, all implementations SHOULD be prepared to handle
105    > potentially extraneous certificates and arbitrary orderings from any
106    > TLS version, with the exception of the end-entity certificate which
107    > MUST be first.
108    """
109    alpn: bytes | None = None
110    """The application-layer protocol as negotiated using
111    [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation)."""
112    alpn_offers: Sequence[bytes] = ()
113    """The ALPN offers as sent in the ClientHello."""
114    # we may want to add SSL_CIPHER_description here, but that's currently not exposed by cryptography
115    cipher: str | None = None
116    """The active cipher name as returned by OpenSSL's `SSL_CIPHER_get_name`."""
117    cipher_list: Sequence[str] = ()
118    """Ciphers accepted by the proxy server on this connection."""
119    tls_version: TlsVersion | None = None
120    """The active TLS version."""
121    sni: str | None = None
122    """
123    The [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) sent in the ClientHello.
124    """
125
126    timestamp_start: float | None = None
127    timestamp_end: float | None = None
128    """*Timestamp:* Connection has been closed."""
129    timestamp_tls_setup: float | None = None
130    """*Timestamp:* TLS handshake has been completed successfully."""
131
132    @property
133    def connected(self) -> bool:
134        """*Read-only:* `True` if Connection.state is ConnectionState.OPEN, `False` otherwise."""
135        return self.state is ConnectionState.OPEN
136
137    @property
138    def tls_established(self) -> bool:
139        """*Read-only:* `True` if TLS has been established, `False` otherwise."""
140        return self.timestamp_tls_setup is not None
141
142    def __eq__(self, other):
143        if isinstance(other, Connection):
144            return self.id == other.id
145        return False
146
147    def __hash__(self):
148        return hash(self.id)
149
150    def __repr__(self):
151        attrs = {
152            # ensure these come first.
153            "id": None,
154            "address": None,
155        }
156        for f in dataclasses.fields(self):
157            val = getattr(self, f.name)
158            if val != f.default:
159                if f.name == "cipher_list":
160                    val = f"<{len(val)} ciphers>"
161                elif f.name == "id":
162                    val = f"…{val[-6:]}"
163                attrs[f.name] = val
164        return f"{type(self).__name__}({attrs!r})"
165
166    @property
167    def alpn_proto_negotiated(self) -> bytes | None:  # pragma: no cover
168        """*Deprecated:* An outdated alias for Connection.alpn."""
169        warnings.warn(
170            "Connection.alpn_proto_negotiated is deprecated, use Connection.alpn instead.",
171            DeprecationWarning,
172            stacklevel=2,
173        )
174        return self.alpn
175
176
177# noinspection PyDataclass
178@dataclass(eq=False, repr=False, **kw_only)
179class Client(Connection):
180    """A connection between a client and mitmproxy."""
181
182    peername: Address
183    """The client's address."""
184    sockname: Address
185    """The local address we received this connection on."""
186
187    mitmcert: certs.Cert | None = None
188    """
189    The certificate used by mitmproxy to establish TLS with the client.
190    """
191
192    proxy_mode: mode_specs.ProxyMode = field(
193        default=mode_specs.ProxyMode.parse("regular")
194    )
195    """The proxy server type this client has been connecting to."""
196
197    timestamp_start: float = field(default_factory=time.time)
198    """*Timestamp:* TCP SYN received"""
199
200    def __str__(self):
201        if self.alpn:
202            tls_state = f", alpn={self.alpn.decode(errors='replace')}"
203        elif self.tls_established:
204            tls_state = ", tls"
205        else:
206            tls_state = ""
207        state = self.state.name
208        assert state
209        return f"Client({human.format_address(self.peername)}, state={state.lower()}{tls_state})"
210
211    @property
212    def address(self):  # pragma: no cover
213        """*Deprecated:* An outdated alias for Client.peername."""
214        warnings.warn(
215            "Client.address is deprecated, use Client.peername instead.",
216            DeprecationWarning,
217            stacklevel=2,
218        )
219        return self.peername
220
221    @address.setter
222    def address(self, x):  # pragma: no cover
223        warnings.warn(
224            "Client.address is deprecated, use Client.peername instead.",
225            DeprecationWarning,
226            stacklevel=2,
227        )
228        self.peername = x
229
230    @property
231    def cipher_name(self) -> str | None:  # pragma: no cover
232        """*Deprecated:* An outdated alias for Connection.cipher."""
233        warnings.warn(
234            "Client.cipher_name is deprecated, use Client.cipher instead.",
235            DeprecationWarning,
236            stacklevel=2,
237        )
238        return self.cipher
239
240    @property
241    def clientcert(self) -> certs.Cert | None:  # pragma: no cover
242        """*Deprecated:* An outdated alias for Connection.certificate_list[0]."""
243        warnings.warn(
244            "Client.clientcert is deprecated, use Client.certificate_list instead.",
245            DeprecationWarning,
246            stacklevel=2,
247        )
248        if self.certificate_list:
249            return self.certificate_list[0]
250        else:
251            return None
252
253    @clientcert.setter
254    def clientcert(self, val):  # pragma: no cover
255        warnings.warn(
256            "Client.clientcert is deprecated, use Client.certificate_list instead.",
257            DeprecationWarning,
258            stacklevel=2,
259        )
260        if val:
261            self.certificate_list = [val]
262        else:
263            self.certificate_list = []
264
265
266# noinspection PyDataclass
267@dataclass(eq=False, repr=False, **kw_only)
268class Server(Connection):
269    """A connection between mitmproxy and an upstream server."""
270
271    address: Address | None  # type: ignore
272    """
273    The server's `(host, port)` address tuple.
274
275    The host can either be a domain or a plain IP address.
276    Which of those two will be present depends on the proxy mode and the client.
277    For explicit proxies, this value will reflect what the client instructs mitmproxy to connect to.
278    For example, if the client starts off a connection with `CONNECT example.com HTTP/1.1`, it will be `example.com`.
279    For transparent proxies such as WireGuard mode, this value will be an IP address.
280    """
281
282    peername: Address | None = None
283    """
284    The server's resolved `(ip, port)` tuple. Will be set during connection establishment.
285    May be `None` in upstream proxy mode when the address is resolved by the upstream proxy only.
286    """
287    sockname: Address | None = None
288
289    timestamp_start: float | None = None
290    """
291    *Timestamp:* Connection establishment started.
292
293    For IP addresses, this corresponds to sending a TCP SYN; for domains, this corresponds to starting a DNS lookup.
294    """
295    timestamp_tcp_setup: float | None = None
296    """*Timestamp:* TCP ACK received."""
297
298    via: server_spec.ServerSpec | None = None
299    """An optional proxy server specification via which the connection should be established."""
300
301    def __str__(self):
302        if self.alpn:
303            tls_state = f", alpn={self.alpn.decode(errors='replace')}"
304        elif self.tls_established:
305            tls_state = ", tls"
306        else:
307            tls_state = ""
308        if self.sockname:
309            local_port = f", src_port={self.sockname[1]}"
310        else:
311            local_port = ""
312        state = self.state.name
313        assert state
314        return f"Server({human.format_address(self.address)}, state={state.lower()}{tls_state}{local_port})"
315
316    def __setattr__(self, name, value):
317        if name in ("address", "via"):
318            connection_open = (
319                self.__dict__.get("state", ConnectionState.CLOSED)
320                is ConnectionState.OPEN
321            )
322            # assigning the current value is okay, that may be an artifact of calling .set_state().
323            attr_changed = self.__dict__.get(name) != value
324            if connection_open and attr_changed:
325                raise RuntimeError(f"Cannot change server.{name} on open connection.")
326        return super().__setattr__(name, value)
327
328    @property
329    def ip_address(self) -> Address | None:  # pragma: no cover
330        """*Deprecated:* An outdated alias for `Server.peername`."""
331        warnings.warn(
332            "Server.ip_address is deprecated, use Server.peername instead.",
333            DeprecationWarning,
334            stacklevel=2,
335        )
336        return self.peername
337
338    @property
339    def cert(self) -> certs.Cert | None:  # pragma: no cover
340        """*Deprecated:* An outdated alias for `Connection.certificate_list[0]`."""
341        warnings.warn(
342            "Server.cert is deprecated, use Server.certificate_list instead.",
343            DeprecationWarning,
344            stacklevel=2,
345        )
346        if self.certificate_list:
347            return self.certificate_list[0]
348        else:
349            return None
350
351    @cert.setter
352    def cert(self, val):  # pragma: no cover
353        warnings.warn(
354            "Server.cert is deprecated, use Server.certificate_list instead.",
355            DeprecationWarning,
356            stacklevel=2,
357        )
358        if val:
359            self.certificate_list = [val]
360        else:
361            self.certificate_list = []
362
363
364__all__ = ["Connection", "Client", "Server", "ConnectionState"]
@dataclass(**kw_only)
class Connection(mitmproxy.coretypes.serializable.SerializableDataclass):
 53@dataclass(**kw_only)
 54class Connection(serializable.SerializableDataclass, metaclass=ABCMeta):
 55    """
 56    Base class for client and server connections.
 57
 58    The connection object only exposes metadata about the connection, but not the underlying socket object.
 59    This is intentional, all I/O should be handled by `mitmproxy.proxy.server` exclusively.
 60    """
 61
 62    peername: Address | None
 63    """The remote's `(ip, port)` tuple for this connection."""
 64    sockname: Address | None
 65    """Our local `(ip, port)` tuple for this connection."""
 66
 67    state: ConnectionState = field(
 68        default=ConnectionState.CLOSED, metadata={"serialize": False}
 69    )
 70    """The current connection state."""
 71
 72    # all connections have a unique id. While
 73    # f.client_conn == f2.client_conn already holds true for live flows (where we have object identity),
 74    # we also want these semantics for recorded flows.
 75    id: str = field(default_factory=lambda: str(uuid.uuid4()))
 76    """A unique UUID to identify the connection."""
 77    transport_protocol: TransportProtocol = field(default="tcp")
 78    """The connection protocol in use."""
 79    error: str | None = None
 80    """
 81    A string describing a general error with connections to this address.
 82
 83    The purpose of this property is to signal that new connections to the particular endpoint should not be attempted,
 84    for example because it uses an untrusted TLS certificate. Regular (unexpected) disconnects do not set the error
 85    property. This property is only reused per client connection.
 86    """
 87
 88    tls: bool = False
 89    """
 90    `True` if TLS should be established, `False` otherwise.
 91    Note that this property only describes if a connection should eventually be protected using TLS.
 92    To check if TLS has already been established, use `Connection.tls_established`.
 93    """
 94    certificate_list: Sequence[certs.Cert] = ()
 95    """
 96    The TLS certificate list as sent by the peer.
 97    The first certificate is the end-entity certificate.
 98
 99    > [RFC 8446] Prior to TLS 1.3, "certificate_list" ordering required each
100    > certificate to certify the one immediately preceding it; however,
101    > some implementations allowed some flexibility.  Servers sometimes
102    > send both a current and deprecated intermediate for transitional
103    > purposes, and others are simply configured incorrectly, but these
104    > cases can nonetheless be validated properly.  For maximum
105    > compatibility, all implementations SHOULD be prepared to handle
106    > potentially extraneous certificates and arbitrary orderings from any
107    > TLS version, with the exception of the end-entity certificate which
108    > MUST be first.
109    """
110    alpn: bytes | None = None
111    """The application-layer protocol as negotiated using
112    [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation)."""
113    alpn_offers: Sequence[bytes] = ()
114    """The ALPN offers as sent in the ClientHello."""
115    # we may want to add SSL_CIPHER_description here, but that's currently not exposed by cryptography
116    cipher: str | None = None
117    """The active cipher name as returned by OpenSSL's `SSL_CIPHER_get_name`."""
118    cipher_list: Sequence[str] = ()
119    """Ciphers accepted by the proxy server on this connection."""
120    tls_version: TlsVersion | None = None
121    """The active TLS version."""
122    sni: str | None = None
123    """
124    The [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) sent in the ClientHello.
125    """
126
127    timestamp_start: float | None = None
128    timestamp_end: float | None = None
129    """*Timestamp:* Connection has been closed."""
130    timestamp_tls_setup: float | None = None
131    """*Timestamp:* TLS handshake has been completed successfully."""
132
133    @property
134    def connected(self) -> bool:
135        """*Read-only:* `True` if Connection.state is ConnectionState.OPEN, `False` otherwise."""
136        return self.state is ConnectionState.OPEN
137
138    @property
139    def tls_established(self) -> bool:
140        """*Read-only:* `True` if TLS has been established, `False` otherwise."""
141        return self.timestamp_tls_setup is not None
142
143    def __eq__(self, other):
144        if isinstance(other, Connection):
145            return self.id == other.id
146        return False
147
148    def __hash__(self):
149        return hash(self.id)
150
151    def __repr__(self):
152        attrs = {
153            # ensure these come first.
154            "id": None,
155            "address": None,
156        }
157        for f in dataclasses.fields(self):
158            val = getattr(self, f.name)
159            if val != f.default:
160                if f.name == "cipher_list":
161                    val = f"<{len(val)} ciphers>"
162                elif f.name == "id":
163                    val = f"…{val[-6:]}"
164                attrs[f.name] = val
165        return f"{type(self).__name__}({attrs!r})"
166
167    @property
168    def alpn_proto_negotiated(self) -> bytes | None:  # pragma: no cover
169        """*Deprecated:* An outdated alias for Connection.alpn."""
170        warnings.warn(
171            "Connection.alpn_proto_negotiated is deprecated, use Connection.alpn instead.",
172            DeprecationWarning,
173            stacklevel=2,
174        )
175        return self.alpn

Base class for client and server connections.

The connection object only exposes metadata about the connection, but not the underlying socket object. This is intentional, all I/O should be handled by mitmproxy.proxy.server exclusively.

Connection( *, peername: tuple[str, int] | None, sockname: tuple[str, int] | None, state: ConnectionState = <ConnectionState.CLOSED: 0>, id: str = <factory>, transport_protocol: Literal['tcp', 'udp'] = 'tcp', error: str | None = None, tls: bool = False, certificate_list: Sequence[mitmproxy.certs.Cert] = (), alpn: bytes | None = None, alpn_offers: Sequence[bytes] = (), cipher: str | None = None, cipher_list: Sequence[str] = (), tls_version: Optional[Literal['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3', 'DTLSv0.9', 'DTLSv1', 'DTLSv1.2', 'QUICv1']] = None, sni: str | None = None, timestamp_start: float | None = None, timestamp_end: float | None = None, timestamp_tls_setup: float | None = None)
peername: tuple[str, int] | None

The remote's (ip, port) tuple for this connection.

sockname: tuple[str, int] | None

Our local (ip, port) tuple for this connection.

The current connection state.

id: str

A unique UUID to identify the connection.

transport_protocol: Literal['tcp', 'udp'] = 'tcp'

The connection protocol in use.

error: str | None = None

A string describing a general error with connections to this address.

The purpose of this property is to signal that new connections to the particular endpoint should not be attempted, for example because it uses an untrusted TLS certificate. Regular (unexpected) disconnects do not set the error property. This property is only reused per client connection.

tls: bool = False

True if TLS should be established, False otherwise. Note that this property only describes if a connection should eventually be protected using TLS. To check if TLS has already been established, use Connection.tls_established.

certificate_list: Sequence[mitmproxy.certs.Cert] = ()

The TLS certificate list as sent by the peer. The first certificate is the end-entity certificate.

[RFC 8446] Prior to TLS 1.3, "certificate_list" ordering required each certificate to certify the one immediately preceding it; however, some implementations allowed some flexibility. Servers sometimes send both a current and deprecated intermediate for transitional purposes, and others are simply configured incorrectly, but these cases can nonetheless be validated properly. For maximum compatibility, all implementations SHOULD be prepared to handle potentially extraneous certificates and arbitrary orderings from any TLS version, with the exception of the end-entity certificate which MUST be first.

alpn: bytes | None = None

The application-layer protocol as negotiated using ALPN.

alpn_offers: Sequence[bytes] = ()

The ALPN offers as sent in the ClientHello.

cipher: str | None = None

The active cipher name as returned by OpenSSL's SSL_CIPHER_get_name.

cipher_list: Sequence[str] = ()

Ciphers accepted by the proxy server on this connection.

tls_version: Optional[Literal['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3', 'DTLSv0.9', 'DTLSv1', 'DTLSv1.2', 'QUICv1']] = None

The active TLS version.

sni: str | None = None

The Server Name Indication (SNI) sent in the ClientHello.

timestamp_start: float | None = None
timestamp_end: float | None = None

Timestamp: Connection has been closed.

timestamp_tls_setup: float | None = None

Timestamp: TLS handshake has been completed successfully.

connected: bool
133    @property
134    def connected(self) -> bool:
135        """*Read-only:* `True` if Connection.state is ConnectionState.OPEN, `False` otherwise."""
136        return self.state is ConnectionState.OPEN

Read-only: True if Connection.state is ConnectionState.OPEN, False otherwise.

tls_established: bool
138    @property
139    def tls_established(self) -> bool:
140        """*Read-only:* `True` if TLS has been established, `False` otherwise."""
141        return self.timestamp_tls_setup is not None

Read-only: True if TLS has been established, False otherwise.

alpn_proto_negotiated: bytes | None
167    @property
168    def alpn_proto_negotiated(self) -> bytes | None:  # pragma: no cover
169        """*Deprecated:* An outdated alias for Connection.alpn."""
170        warnings.warn(
171            "Connection.alpn_proto_negotiated is deprecated, use Connection.alpn instead.",
172            DeprecationWarning,
173            stacklevel=2,
174        )
175        return self.alpn

Deprecated: An outdated alias for Connection.alpn.

@dataclass(eq=False, repr=False, **kw_only)
class Client(Connection):
179@dataclass(eq=False, repr=False, **kw_only)
180class Client(Connection):
181    """A connection between a client and mitmproxy."""
182
183    peername: Address
184    """The client's address."""
185    sockname: Address
186    """The local address we received this connection on."""
187
188    mitmcert: certs.Cert | None = None
189    """
190    The certificate used by mitmproxy to establish TLS with the client.
191    """
192
193    proxy_mode: mode_specs.ProxyMode = field(
194        default=mode_specs.ProxyMode.parse("regular")
195    )
196    """The proxy server type this client has been connecting to."""
197
198    timestamp_start: float = field(default_factory=time.time)
199    """*Timestamp:* TCP SYN received"""
200
201    def __str__(self):
202        if self.alpn:
203            tls_state = f", alpn={self.alpn.decode(errors='replace')}"
204        elif self.tls_established:
205            tls_state = ", tls"
206        else:
207            tls_state = ""
208        state = self.state.name
209        assert state
210        return f"Client({human.format_address(self.peername)}, state={state.lower()}{tls_state})"
211
212    @property
213    def address(self):  # pragma: no cover
214        """*Deprecated:* An outdated alias for Client.peername."""
215        warnings.warn(
216            "Client.address is deprecated, use Client.peername instead.",
217            DeprecationWarning,
218            stacklevel=2,
219        )
220        return self.peername
221
222    @address.setter
223    def address(self, x):  # pragma: no cover
224        warnings.warn(
225            "Client.address is deprecated, use Client.peername instead.",
226            DeprecationWarning,
227            stacklevel=2,
228        )
229        self.peername = x
230
231    @property
232    def cipher_name(self) -> str | None:  # pragma: no cover
233        """*Deprecated:* An outdated alias for Connection.cipher."""
234        warnings.warn(
235            "Client.cipher_name is deprecated, use Client.cipher instead.",
236            DeprecationWarning,
237            stacklevel=2,
238        )
239        return self.cipher
240
241    @property
242    def clientcert(self) -> certs.Cert | None:  # pragma: no cover
243        """*Deprecated:* An outdated alias for Connection.certificate_list[0]."""
244        warnings.warn(
245            "Client.clientcert is deprecated, use Client.certificate_list instead.",
246            DeprecationWarning,
247            stacklevel=2,
248        )
249        if self.certificate_list:
250            return self.certificate_list[0]
251        else:
252            return None
253
254    @clientcert.setter
255    def clientcert(self, val):  # pragma: no cover
256        warnings.warn(
257            "Client.clientcert is deprecated, use Client.certificate_list instead.",
258            DeprecationWarning,
259            stacklevel=2,
260        )
261        if val:
262            self.certificate_list = [val]
263        else:
264            self.certificate_list = []

A connection between a client and mitmproxy.

Client( *, peername: tuple[str, int], sockname: tuple[str, int], state: ConnectionState = <ConnectionState.CLOSED: 0>, id: str = <factory>, transport_protocol: Literal['tcp', 'udp'] = 'tcp', error: str | None = None, tls: bool = False, certificate_list: Sequence[mitmproxy.certs.Cert] = (), alpn: bytes | None = None, alpn_offers: Sequence[bytes] = (), cipher: str | None = None, cipher_list: Sequence[str] = (), tls_version: Optional[Literal['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3', 'DTLSv0.9', 'DTLSv1', 'DTLSv1.2', 'QUICv1']] = None, sni: str | None = None, timestamp_start: float = <factory>, timestamp_end: float | None = None, timestamp_tls_setup: float | None = None, mitmcert: mitmproxy.certs.Cert | None = None, proxy_mode: mitmproxy.proxy.mode_specs.ProxyMode = ProxyMode.parse('regular'))
peername: tuple[str, int]

The client's address.

sockname: tuple[str, int]

The local address we received this connection on.

mitmcert: mitmproxy.certs.Cert | None = None

The certificate used by mitmproxy to establish TLS with the client.

proxy_mode: mitmproxy.proxy.mode_specs.ProxyMode = ProxyMode.parse('regular')

The proxy server type this client has been connecting to.

timestamp_start: float = None

Timestamp: TCP SYN received

address
212    @property
213    def address(self):  # pragma: no cover
214        """*Deprecated:* An outdated alias for Client.peername."""
215        warnings.warn(
216            "Client.address is deprecated, use Client.peername instead.",
217            DeprecationWarning,
218            stacklevel=2,
219        )
220        return self.peername

Deprecated: An outdated alias for Client.peername.

cipher_name: str | None
231    @property
232    def cipher_name(self) -> str | None:  # pragma: no cover
233        """*Deprecated:* An outdated alias for Connection.cipher."""
234        warnings.warn(
235            "Client.cipher_name is deprecated, use Client.cipher instead.",
236            DeprecationWarning,
237            stacklevel=2,
238        )
239        return self.cipher

Deprecated: An outdated alias for Connection.cipher.

clientcert: mitmproxy.certs.Cert | None
241    @property
242    def clientcert(self) -> certs.Cert | None:  # pragma: no cover
243        """*Deprecated:* An outdated alias for Connection.certificate_list[0]."""
244        warnings.warn(
245            "Client.clientcert is deprecated, use Client.certificate_list instead.",
246            DeprecationWarning,
247            stacklevel=2,
248        )
249        if self.certificate_list:
250            return self.certificate_list[0]
251        else:
252            return None

Deprecated: An outdated alias for Connection.certificate_list[0].

@dataclass(eq=False, repr=False, **kw_only)
class Server(Connection):
268@dataclass(eq=False, repr=False, **kw_only)
269class Server(Connection):
270    """A connection between mitmproxy and an upstream server."""
271
272    address: Address | None  # type: ignore
273    """
274    The server's `(host, port)` address tuple.
275
276    The host can either be a domain or a plain IP address.
277    Which of those two will be present depends on the proxy mode and the client.
278    For explicit proxies, this value will reflect what the client instructs mitmproxy to connect to.
279    For example, if the client starts off a connection with `CONNECT example.com HTTP/1.1`, it will be `example.com`.
280    For transparent proxies such as WireGuard mode, this value will be an IP address.
281    """
282
283    peername: Address | None = None
284    """
285    The server's resolved `(ip, port)` tuple. Will be set during connection establishment.
286    May be `None` in upstream proxy mode when the address is resolved by the upstream proxy only.
287    """
288    sockname: Address | None = None
289
290    timestamp_start: float | None = None
291    """
292    *Timestamp:* Connection establishment started.
293
294    For IP addresses, this corresponds to sending a TCP SYN; for domains, this corresponds to starting a DNS lookup.
295    """
296    timestamp_tcp_setup: float | None = None
297    """*Timestamp:* TCP ACK received."""
298
299    via: server_spec.ServerSpec | None = None
300    """An optional proxy server specification via which the connection should be established."""
301
302    def __str__(self):
303        if self.alpn:
304            tls_state = f", alpn={self.alpn.decode(errors='replace')}"
305        elif self.tls_established:
306            tls_state = ", tls"
307        else:
308            tls_state = ""
309        if self.sockname:
310            local_port = f", src_port={self.sockname[1]}"
311        else:
312            local_port = ""
313        state = self.state.name
314        assert state
315        return f"Server({human.format_address(self.address)}, state={state.lower()}{tls_state}{local_port})"
316
317    def __setattr__(self, name, value):
318        if name in ("address", "via"):
319            connection_open = (
320                self.__dict__.get("state", ConnectionState.CLOSED)
321                is ConnectionState.OPEN
322            )
323            # assigning the current value is okay, that may be an artifact of calling .set_state().
324            attr_changed = self.__dict__.get(name) != value
325            if connection_open and attr_changed:
326                raise RuntimeError(f"Cannot change server.{name} on open connection.")
327        return super().__setattr__(name, value)
328
329    @property
330    def ip_address(self) -> Address | None:  # pragma: no cover
331        """*Deprecated:* An outdated alias for `Server.peername`."""
332        warnings.warn(
333            "Server.ip_address is deprecated, use Server.peername instead.",
334            DeprecationWarning,
335            stacklevel=2,
336        )
337        return self.peername
338
339    @property
340    def cert(self) -> certs.Cert | None:  # pragma: no cover
341        """*Deprecated:* An outdated alias for `Connection.certificate_list[0]`."""
342        warnings.warn(
343            "Server.cert is deprecated, use Server.certificate_list instead.",
344            DeprecationWarning,
345            stacklevel=2,
346        )
347        if self.certificate_list:
348            return self.certificate_list[0]
349        else:
350            return None
351
352    @cert.setter
353    def cert(self, val):  # pragma: no cover
354        warnings.warn(
355            "Server.cert is deprecated, use Server.certificate_list instead.",
356            DeprecationWarning,
357            stacklevel=2,
358        )
359        if val:
360            self.certificate_list = [val]
361        else:
362            self.certificate_list = []

A connection between mitmproxy and an upstream server.

Server( *, peername: tuple[str, int] | None = None, sockname: tuple[str, int] | None = None, state: ConnectionState = <ConnectionState.CLOSED: 0>, id: str = <factory>, transport_protocol: Literal['tcp', 'udp'] = 'tcp', error: str | None = None, tls: bool = False, certificate_list: Sequence[mitmproxy.certs.Cert] = (), alpn: bytes | None = None, alpn_offers: Sequence[bytes] = (), cipher: str | None = None, cipher_list: Sequence[str] = (), tls_version: Optional[Literal['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3', 'DTLSv0.9', 'DTLSv1', 'DTLSv1.2', 'QUICv1']] = None, sni: str | None = None, timestamp_start: float | None = None, timestamp_end: float | None = None, timestamp_tls_setup: float | None = None, address: tuple[str, int] | None, timestamp_tcp_setup: float | None = None, via: tuple[typing.Literal['http', 'https', 'http3', 'tls', 'dtls', 'tcp', 'udp', 'dns', 'quic'], tuple[str, int]] | None = None)
address: tuple[str, int] | None

The server's (host, port) address tuple.

The host can either be a domain or a plain IP address. Which of those two will be present depends on the proxy mode and the client. For explicit proxies, this value will reflect what the client instructs mitmproxy to connect to. For example, if the client starts off a connection with CONNECT example.com HTTP/1.1, it will be example.com. For transparent proxies such as WireGuard mode, this value will be an IP address.

peername: tuple[str, int] | None = None

The server's resolved (ip, port) tuple. Will be set during connection establishment. May be None in upstream proxy mode when the address is resolved by the upstream proxy only.

sockname: tuple[str, int] | None = None

Our local (ip, port) tuple for this connection.

timestamp_start: float | None = None

Timestamp: Connection establishment started.

For IP addresses, this corresponds to sending a TCP SYN; for domains, this corresponds to starting a DNS lookup.

timestamp_tcp_setup: float | None = None

Timestamp: TCP ACK received.

via: tuple[typing.Literal['http', 'https', 'http3', 'tls', 'dtls', 'tcp', 'udp', 'dns', 'quic'], tuple[str, int]] | None = None

An optional proxy server specification via which the connection should be established.

ip_address: tuple[str, int] | None
329    @property
330    def ip_address(self) -> Address | None:  # pragma: no cover
331        """*Deprecated:* An outdated alias for `Server.peername`."""
332        warnings.warn(
333            "Server.ip_address is deprecated, use Server.peername instead.",
334            DeprecationWarning,
335            stacklevel=2,
336        )
337        return self.peername

Deprecated: An outdated alias for Server.peername.

cert: mitmproxy.certs.Cert | None
339    @property
340    def cert(self) -> certs.Cert | None:  # pragma: no cover
341        """*Deprecated:* An outdated alias for `Connection.certificate_list[0]`."""
342        warnings.warn(
343            "Server.cert is deprecated, use Server.certificate_list instead.",
344            DeprecationWarning,
345            stacklevel=2,
346        )
347        if self.certificate_list:
348            return self.certificate_list[0]
349        else:
350            return None

Deprecated: An outdated alias for Connection.certificate_list[0].

class ConnectionState(enum.Flag):
20class ConnectionState(Flag):
21    """The current state of the underlying socket."""
22
23    CLOSED = 0
24    CAN_READ = 1
25    CAN_WRITE = 2
26    OPEN = CAN_READ | CAN_WRITE

The current state of the underlying socket.

CLOSED = <ConnectionState.CLOSED: 0>
CAN_READ = <ConnectionState.CAN_READ: 1>
CAN_WRITE = <ConnectionState.CAN_WRITE: 2>
OPEN = <ConnectionState.OPEN: 3>