diff --git a/mitmproxy/addons/clientplayback.py b/mitmproxy/addons/clientplayback.py index f65a5115c..ac56fc8a1 100644 --- a/mitmproxy/addons/clientplayback.py +++ b/mitmproxy/addons/clientplayback.py @@ -14,9 +14,10 @@ from mitmproxy.addons.proxyserver import AsyncReply from mitmproxy.hooks import UpdateHook from mitmproxy.net import server_spec from mitmproxy.options import Options +from mitmproxy.proxy.context import Context from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import commands, events, layers, server -from mitmproxy.proxy.context import ConnectionState, Context, Server +from mitmproxy.connection import ConnectionState, Server from mitmproxy.proxy.layer import CommandGenerator from mitmproxy.utils import asyncio_utils diff --git a/mitmproxy/addons/next_layer.py b/mitmproxy/addons/next_layer.py index 16a025de2..453c3b5b1 100644 --- a/mitmproxy/addons/next_layer.py +++ b/mitmproxy/addons/next_layer.py @@ -1,7 +1,7 @@ import re from typing import Type, Sequence, Union, Tuple, Any, Iterable, Optional, List -from mitmproxy import ctx, exceptions +from mitmproxy import ctx, exceptions, connection from mitmproxy.net.tls import is_tls_record_magic from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import context, layer, layers @@ -43,7 +43,7 @@ class NextLayer: re.compile(x, re.IGNORECASE) for x in ctx.options.allow_hosts ] - def ignore_connection(self, server_address: Optional[context.Address], data_client: bytes) -> Optional[bool]: + def ignore_connection(self, server_address: Optional[connection.Address], data_client: bytes) -> Optional[bool]: """ Returns: True, if the connection should be ignored. diff --git a/mitmproxy/addons/proxyauth.py b/mitmproxy/addons/proxyauth.py index e949aa4d8..9d5ca46e0 100644 --- a/mitmproxy/addons/proxyauth.py +++ b/mitmproxy/addons/proxyauth.py @@ -7,11 +7,10 @@ from typing import Tuple import ldap3 import passlib.apache -from mitmproxy import ctx +from mitmproxy import ctx, connection from mitmproxy import exceptions from mitmproxy import http from mitmproxy.net.http import status_codes -from mitmproxy.proxy import context REALM = "mitmproxy" @@ -48,7 +47,7 @@ class ProxyAuth: self.singleuser = None self.ldapconn = None self.ldapserver = None - self.authenticated: MutableMapping[context.Client, Tuple[str, str]] = weakref.WeakKeyDictionary() + self.authenticated: MutableMapping[connection.Client, Tuple[str, str]] = weakref.WeakKeyDictionary() """Contains all connections that are permanently authenticated after an HTTP CONNECT""" def load(self, loader): diff --git a/mitmproxy/addons/tlsconfig.py b/mitmproxy/addons/tlsconfig.py index 766643504..5d0412b3a 100644 --- a/mitmproxy/addons/tlsconfig.py +++ b/mitmproxy/addons/tlsconfig.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import List, Optional, TypedDict, Any from OpenSSL import SSL -from mitmproxy import certs, ctx, exceptions +from mitmproxy import certs, ctx, exceptions, connection from mitmproxy.net import tls as net_tls from mitmproxy.options import CONF_BASENAME from mitmproxy.proxy import context @@ -113,8 +113,8 @@ class TlsConfig: self.create_proxy_server_ssl_conn(tls_start) def create_client_proxy_ssl_conn(self, tls_start: tls.TlsStartData) -> None: - client: context.Client = tls_start.context.client - server: context.Server = tls_start.context.server + client: connection.Client = tls_start.context.client + server: connection.Server = tls_start.context.server entry = self.get_cert(tls_start.context) @@ -149,8 +149,8 @@ class TlsConfig: tls_start.ssl_conn.set_accept_state() def create_proxy_server_ssl_conn(self, tls_start: tls.TlsStartData) -> None: - client: context.Client = tls_start.context.client - server: context.Server = tls_start.context.server + client: connection.Client = tls_start.context.client + server: connection.Server = tls_start.context.server assert server.address if ctx.options.ssl_insecure: diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py index 96549adfc..4c3037ce8 100644 --- a/mitmproxy/addons/view.py +++ b/mitmproxy/addons/view.py @@ -15,14 +15,15 @@ import blinker import sortedcontainers import mitmproxy.flow -from mitmproxy import flowfilter, hooks -from mitmproxy import exceptions from mitmproxy import command from mitmproxy import ctx -from mitmproxy import io +from mitmproxy import exceptions +from mitmproxy import hooks +from mitmproxy import connection +from mitmproxy import flowfilter from mitmproxy import http +from mitmproxy import io from mitmproxy import tcp -from mitmproxy.proxy import context from mitmproxy.utils import human @@ -133,15 +134,15 @@ class View(collections.abc.Sequence): self.default_order = OrderRequestStart(self) self.orders = dict( - time = OrderRequestStart(self), method = OrderRequestMethod(self), - url = OrderRequestURL(self), size = OrderKeySize(self), + time=OrderRequestStart(self), method=OrderRequestMethod(self), + url=OrderRequestURL(self), size=OrderKeySize(self), ) self.order_key = self.default_order self.order_reversed = False self.focus_follow = False self._view = sortedcontainers.SortedListWithKey( - key = self.order_key + key=self.order_key ) # The sig_view* signals broadcast events that affect the view. That is, @@ -461,8 +462,8 @@ class View(collections.abc.Sequence): except ValueError as e: raise exceptions.CommandError("Invalid URL: %s" % e) - c = context.Client(("", 0), ("", 0), req.timestamp_start - 0.0001) - s = context.Server((req.host, req.port)) + c = connection.Client(("", 0), ("", 0), req.timestamp_start - 0.0001) + s = connection.Server((req.host, req.port)) f = http.HTTPFlow(c, s) f.request = req @@ -622,6 +623,7 @@ class Focus: """ Tracks a focus element within a View. """ + def __init__(self, v: View) -> None: self.view = v self._flow: typing.Optional[mitmproxy.flow.Flow] = None diff --git a/mitmproxy/connection.py b/mitmproxy/connection.py new file mode 100644 index 000000000..50c92f5c9 --- /dev/null +++ b/mitmproxy/connection.py @@ -0,0 +1,374 @@ +import uuid +import warnings +from abc import ABCMeta +from enum import Flag +from typing import Literal, Optional, Sequence, Tuple, Union + +from mitmproxy import certs +from mitmproxy.coretypes import serializable +from mitmproxy.net import server_spec +from mitmproxy.utils import human + + +class ConnectionState(Flag): + """The current state of the underlying socket.""" + CLOSED = 0 + CAN_READ = 1 + CAN_WRITE = 2 + OPEN = CAN_READ | CAN_WRITE + + +# practically speaking we may have IPv6 addresses with flowinfo and scope_id, +# but type checking isn't good enough to properly handle tuple unions. +# this version at least provides useful type checking messages. +Address = Tuple[str, int] + + +class Connection(serializable.Serializable, metaclass=ABCMeta): + """ + 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. + """ + # all connections have a unique id. While + # f.client_conn == f2.client_conn already holds true for live flows (where we have object identity), + # we also want these semantics for recorded flows. + id: str + """A unique UUID to identify the connection.""" + state: ConnectionState + """The current connection state.""" + peername: Optional[Address] + """The remote's `(ip, port)` tuple for this connection.""" + sockname: Optional[Address] + """Our local `(ip, port)` tuple for this connection.""" + error: Optional[str] = None + """A string describing the connection error.""" + + 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[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: Optional[bytes] = None + """The application-layer protocol as negotiated using + [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation).""" + alpn_offers: Sequence[bytes] = () + """The ALPN offers as sent in the ClientHello.""" + # we may want to add SSL_CIPHER_description here, but that's currently not exposed by cryptography + cipher: Optional[str] = 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[str] = None + """The active TLS version.""" + sni: Union[str, Literal[True], None] + """ + The [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) sent in the ClientHello. + For server connections, this value may also be set to `True`, which means "use `Server.address`". + """ + + timestamp_end: Optional[float] = None + """*Timestamp:* Connection has been closed.""" + timestamp_tls_setup: Optional[float] = None + """*Timestamp:* TLS handshake has been completed successfully.""" + + @property + def connected(self) -> bool: + """`True` if Connection.state is ConnectionState.OPEN, `False` otherwise. Read-only.""" + return self.state is ConnectionState.OPEN + + @property + def tls_established(self) -> bool: + """`True` if TLS has been established, `False` otherwise. Read-only.""" + return self.timestamp_tls_setup is not None + + def __eq__(self, other): + if isinstance(other, Connection): + return self.id == other.id + return False + + def __hash__(self): + return hash(self.id) + + def __repr__(self): + attrs = repr({ + k: { + "cipher_list": lambda: f"<{len(v)} ciphers>", + "id": lambda: f"…{v[-6:]}" + }.get(k, lambda: v)() + for k, v in self.__dict__.items() + }) + return f"{type(self).__name__}({attrs})" + + @property + def alpn_proto_negotiated(self) -> Optional[bytes]: # pragma: no cover + """*Deprecated:* An outdated alias for Connection.alpn.""" + warnings.warn("Connection.alpn_proto_negotiated is deprecated, use Connection.alpn instead.", + DeprecationWarning) + return self.alpn + + +class Client(Connection): + """A connection between a client and mitmproxy.""" + peername: Address + """The client's address.""" + sockname: Address + """The local address we received this connection on.""" + + mitmcert: Optional[certs.Cert] = None + """ + The certificate used by mitmproxy to establish TLS with the client. + """ + sni: Union[str, None] = None + """The Server Name Indication sent by the client.""" + + timestamp_start: float + """*Timestamp:* TCP SYN received""" + + def __init__(self, peername, sockname, timestamp_start): + self.id = str(uuid.uuid4()) + self.peername = peername + self.sockname = sockname + self.timestamp_start = timestamp_start + self.state = ConnectionState.OPEN + + def __str__(self): + if self.alpn: + tls_state = f", alpn={self.alpn.decode(errors='replace')}" + elif self.tls_established: + tls_state = ", tls" + else: + tls_state = "" + return f"Client({human.format_address(self.peername)}, state={self.state.name.lower()}{tls_state})" + + def get_state(self): + # Important: Retain full compatibility with old proxy core for now! + # This means we need to add all new fields to the old implementation. + return { + 'address': self.peername, + 'alpn': self.alpn, + 'cipher_name': self.cipher, + 'id': self.id, + 'mitmcert': self.mitmcert.get_state() if self.mitmcert is not None else None, + 'sni': self.sni, + 'timestamp_end': self.timestamp_end, + 'timestamp_start': self.timestamp_start, + 'timestamp_tls_setup': self.timestamp_tls_setup, + 'tls_established': self.tls_established, + 'tls_extensions': [], + 'tls_version': self.tls_version, + # only used in sans-io + 'state': self.state.value, + 'sockname': self.sockname, + 'error': self.error, + 'tls': self.tls, + 'certificate_list': [x.get_state() for x in self.certificate_list], + 'alpn_offers': self.alpn_offers, + 'cipher_list': self.cipher_list, + } + + @classmethod + def from_state(cls, state) -> "Client": + client = Client( + state["address"], + ("mitmproxy", 8080), + state["timestamp_start"] + ) + client.set_state(state) + return client + + def set_state(self, state): + self.peername = tuple(state["address"]) if state["address"] else None + self.alpn = state["alpn"] + self.cipher = state["cipher_name"] + self.id = state["id"] + self.sni = state["sni"] + self.timestamp_end = state["timestamp_end"] + self.timestamp_start = state["timestamp_start"] + self.timestamp_tls_setup = state["timestamp_tls_setup"] + self.tls_version = state["tls_version"] + # only used in sans-io + self.state = ConnectionState(state["state"]) + self.sockname = tuple(state["sockname"]) if state["sockname"] else None + self.error = state["error"] + self.tls = state["tls"] + self.certificate_list = [certs.Cert.from_state(x) for x in state["certificate_list"]] + self.mitmcert = certs.Cert.from_state(state["mitmcert"]) if state["mitmcert"] is not None else None + self.alpn_offers = state["alpn_offers"] + self.cipher_list = state["cipher_list"] + + @property + def address(self): # pragma: no cover + """*Deprecated:* An outdated alias for Client.peername.""" + warnings.warn("Client.address is deprecated, use Client.peername instead.", DeprecationWarning, stacklevel=2) + return self.peername + + @address.setter + def address(self, x): # pragma: no cover + warnings.warn("Client.address is deprecated, use Client.peername instead.", DeprecationWarning, stacklevel=2) + self.peername = x + + @property + def cipher_name(self) -> Optional[str]: # pragma: no cover + """*Deprecated:* An outdated alias for Connection.cipher.""" + warnings.warn("Client.cipher_name is deprecated, use Client.cipher instead.", DeprecationWarning, stacklevel=2) + return self.cipher + + @property + def clientcert(self) -> Optional[certs.Cert]: # pragma: no cover + """*Deprecated:* An outdated alias for Connection.certificate_list[0].""" + warnings.warn("Client.clientcert is deprecated, use Client.certificate_list instead.", DeprecationWarning, + stacklevel=2) + if self.certificate_list: + return self.certificate_list[0] + else: + return None + + @clientcert.setter + def clientcert(self, val): # pragma: no cover + warnings.warn("Client.clientcert is deprecated, use Client.certificate_list instead.", DeprecationWarning) + if val: + self.certificate_list = [val] + else: + self.certificate_list = [] + + +class Server(Connection): + """A connection between mitmproxy and an upstream server.""" + + peername: Optional[Address] = None + """The server's resolved `(ip, port)` tuple. Will be set during connection establishment.""" + sockname: Optional[Address] = None + address: Optional[Address] + """The server's `(host, port)` address tuple. The host can either be a domain or a plain IP address.""" + + timestamp_start: Optional[float] = None + """*Timestamp:* TCP SYN sent.""" + timestamp_tcp_setup: Optional[float] = None + """*Timestamp:* TCP ACK received.""" + + sni: Union[str, Literal[True], None] = True + via: Optional[server_spec.ServerSpec] = None + """An optional proxy server specification via which the connection should be established.""" + + def __init__(self, address: Optional[Address]): + self.id = str(uuid.uuid4()) + self.address = address + self.state = ConnectionState.CLOSED + + def __str__(self): + if self.alpn: + tls_state = f", alpn={self.alpn.decode(errors='replace')}" + elif self.tls_established: + tls_state = ", tls" + else: + tls_state = "" + if self.sockname: + local_port = f", src_port={self.sockname[1]}" + else: + local_port = "" + return f"Server({human.format_address(self.address)}, state={self.state.name.lower()}{tls_state}{local_port})" + + def get_state(self): + return { + 'address': self.address, + 'alpn': self.alpn, + 'id': self.id, + 'ip_address': self.peername, + 'sni': self.sni, + 'source_address': self.sockname, + 'timestamp_end': self.timestamp_end, + 'timestamp_start': self.timestamp_start, + 'timestamp_tcp_setup': self.timestamp_tcp_setup, + 'timestamp_tls_setup': self.timestamp_tls_setup, + 'tls_established': self.tls_established, + 'tls_version': self.tls_version, + 'via': None, + # only used in sans-io + 'state': self.state.value, + 'error': self.error, + 'tls': self.tls, + 'certificate_list': [x.get_state() for x in self.certificate_list], + 'alpn_offers': self.alpn_offers, + 'cipher_name': self.cipher, + 'cipher_list': self.cipher_list, + 'via2': self.via, + } + + @classmethod + def from_state(cls, state) -> "Server": + server = Server(None) + server.set_state(state) + return server + + def set_state(self, state): + self.address = tuple(state["address"]) if state["address"] else None + self.alpn = state["alpn"] + self.id = state["id"] + self.peername = tuple(state["ip_address"]) if state["ip_address"] else None + self.sni = state["sni"] + self.sockname = tuple(state["source_address"]) if state["source_address"] else None + self.timestamp_end = state["timestamp_end"] + self.timestamp_start = state["timestamp_start"] + self.timestamp_tcp_setup = state["timestamp_tcp_setup"] + self.timestamp_tls_setup = state["timestamp_tls_setup"] + self.tls_version = state["tls_version"] + self.state = ConnectionState(state["state"]) + self.error = state["error"] + self.tls = state["tls"] + self.certificate_list = [certs.Cert.from_state(x) for x in state["certificate_list"]] + self.alpn_offers = state["alpn_offers"] + self.cipher = state["cipher_name"] + self.cipher_list = state["cipher_list"] + self.via = state["via2"] + + @property + def ip_address(self) -> Optional[Address]: # pragma: no cover + """*Deprecated:* An outdated alias for `Server.peername`.""" + warnings.warn("Server.ip_address is deprecated, use Server.peername instead.", DeprecationWarning, stacklevel=2) + return self.peername + + @property + def cert(self) -> Optional[certs.Cert]: # pragma: no cover + """*Deprecated:* An outdated alias for `Connection.certificate_list[0]`.""" + warnings.warn("Server.cert is deprecated, use Server.certificate_list instead.", DeprecationWarning, + stacklevel=2) + if self.certificate_list: + return self.certificate_list[0] + else: + return None + + @cert.setter + def cert(self, val): # pragma: no cover + warnings.warn("Server.cert is deprecated, use Server.certificate_list instead.", DeprecationWarning, + stacklevel=2) + if val: + self.certificate_list = [val] + else: + self.certificate_list = [] + + +__all__ = [ + "Connection", + "Client", + "Server", + "ConnectionState" +] diff --git a/mitmproxy/flow.py b/mitmproxy/flow.py index 71770e7de..c9da7fde0 100644 --- a/mitmproxy/flow.py +++ b/mitmproxy/flow.py @@ -2,11 +2,10 @@ import time import typing # noqa import uuid -from mitmproxy import controller +from mitmproxy import controller, connection from mitmproxy import exceptions from mitmproxy import stateobject from mitmproxy import version -from mitmproxy.proxy import context class Error(stateobject.StateObject): @@ -55,18 +54,17 @@ class Error(stateobject.StateObject): class Flow(stateobject.StateObject): - """ A Flow is a collection of objects representing a single transaction. This class is usually subclassed for each protocol, e.g. HTTPFlow. """ def __init__( - self, - type: str, - client_conn: context.Client, - server_conn: context.Server, - live: bool=None + self, + type: str, + client_conn: connection.Client, + server_conn: connection.Server, + live: bool = None ) -> None: self.type = type self.id = str(uuid.uuid4()) @@ -85,8 +83,8 @@ class Flow(stateobject.StateObject): _stateobject_attributes = dict( id=str, error=Error, - client_conn=context.Client, - server_conn=context.Server, + client_conn=connection.Client, + server_conn=connection.Server, type=str, intercepted=bool, is_replay=str, diff --git a/mitmproxy/http.py b/mitmproxy/http.py index 577685d0e..098eee6f0 100644 --- a/mitmproxy/http.py +++ b/mitmproxy/http.py @@ -1,5 +1,4 @@ import re -import re import time import urllib.parse from dataclasses import dataclass @@ -13,7 +12,7 @@ from typing import Optional from typing import Tuple from typing import Union -from mitmproxy import flow +from mitmproxy import flow, connection from mitmproxy.coretypes import multidict from mitmproxy.coretypes import serializable from mitmproxy.net import encoding @@ -21,7 +20,6 @@ from mitmproxy.net.http import cookies, multipart from mitmproxy.net.http import status_codes from mitmproxy.net.http import url from mitmproxy.net.http.headers import assemble_content_type, parse_content_type -from mitmproxy.proxy import context from mitmproxy.utils import human from mitmproxy.utils import strutils from mitmproxy.utils import typecheck @@ -1117,8 +1115,8 @@ class HTTPFlow(flow.Flow): object. This might happen, for instance, when a response was received from the server, but there was an error sending it back to the client. """ - server_conn: context.Server - client_conn: context.Client + server_conn: connection.Server + client_conn: connection.Client intercepted: bool = False """ Is this flow currently being intercepted? """ mode: str diff --git a/mitmproxy/proxy/commands.py b/mitmproxy/proxy/commands.py index b4d0bceb1..04429800e 100644 --- a/mitmproxy/proxy/commands.py +++ b/mitmproxy/proxy/commands.py @@ -9,7 +9,7 @@ The counterpart to commands are events. from typing import Literal, Union, TYPE_CHECKING import mitmproxy.hooks -from mitmproxy.proxy.context import Connection, Server +from mitmproxy.connection import Connection, Server if TYPE_CHECKING: import mitmproxy.proxy.layer diff --git a/mitmproxy/proxy/context.py b/mitmproxy/proxy/context.py index 33f9d8c2e..6a21504d9 100644 --- a/mitmproxy/proxy/context.py +++ b/mitmproxy/proxy/context.py @@ -1,394 +1,30 @@ -import uuid -import warnings -from abc import ABCMeta -from enum import Flag -from typing import List, Literal, Optional, Sequence, Tuple, Union, TYPE_CHECKING +from typing import List, TYPE_CHECKING -import mitmproxy -from mitmproxy import certs -from mitmproxy.coretypes import serializable -from mitmproxy.net import server_spec +from mitmproxy import connection from mitmproxy.options import Options -from mitmproxy.utils import human if TYPE_CHECKING: import mitmproxy.proxy.layer -class ConnectionState(Flag): - """The current state of the underlying socket.""" - CLOSED = 0 - CAN_READ = 1 - CAN_WRITE = 2 - OPEN = CAN_READ | CAN_WRITE - - -# practically speaking we may have IPv6 addresses with flowinfo and scope_id, -# but type checking isn't good enough to properly handle tuple unions. -# this version at least provides useful type checking messages. -Address = Tuple[str, int] - - -class Connection(serializable.Serializable, metaclass=ABCMeta): - """ - 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. - """ - # all connections have a unique id. While - # f.client_conn == f2.client_conn already holds true for live flows (where we have object identity), - # we also want these semantics for recorded flows. - id: str - """A unique UUID to identify the connection.""" - state: ConnectionState - """The current connection state.""" - peername: Optional[Address] - """The remote's `(ip, port)` tuple for this connection.""" - sockname: Optional[Address] - """Our local `(ip, port)` tuple for this connection.""" - error: Optional[str] = None - """A string describing the connection error.""" - - 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[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: Optional[bytes] = None - """The application-layer protocol as negotiated using - [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation).""" - alpn_offers: Sequence[bytes] = () - """The ALPN offers as sent in the ClientHello.""" - # we may want to add SSL_CIPHER_description here, but that's currently not exposed by cryptography - cipher: Optional[str] = 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[str] = None - """The active TLS version.""" - sni: Union[str, Literal[True], None] - """ - The [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) sent in the ClientHello. - For server connections, this value may also be set to `True`, which means "use `Server.address`". - """ - - timestamp_end: Optional[float] = None - """*Timestamp:* Connection has been closed.""" - timestamp_tls_setup: Optional[float] = None - """*Timestamp:* TLS handshake has been completed successfully.""" - - @property - def connected(self) -> bool: - """`True` if Connection.state is ConnectionState.OPEN, `False` otherwise. Read-only.""" - return self.state is ConnectionState.OPEN - - @property - def tls_established(self) -> bool: - """`True` if TLS has been established, `False` otherwise. Read-only.""" - return self.timestamp_tls_setup is not None - - def __eq__(self, other): - if isinstance(other, Connection): - return self.id == other.id - return False - - def __hash__(self): - return hash(self.id) - - def __repr__(self): - attrs = repr({ - k: { - "cipher_list": lambda: f"<{len(v)} ciphers>", - "id": lambda: f"…{v[-6:]}" - }.get(k, lambda: v)() - for k, v in self.__dict__.items() - }) - return f"{type(self).__name__}({attrs})" - - @property - def alpn_proto_negotiated(self) -> Optional[bytes]: # pragma: no cover - """*Deprecated:* An outdated alias for Connection.alpn.""" - warnings.warn("Connection.alpn_proto_negotiated is deprecated, use Connection.alpn instead.", - DeprecationWarning) - return self.alpn - - -class Client(Connection): - """A connection between a client and mitmproxy.""" - peername: Address - """The client's address.""" - sockname: Address - """The local address we received this connection on.""" - - mitmcert: Optional[certs.Cert] = None - """ - The certificate used by mitmproxy to establish TLS with the client. - """ - sni: Union[str, None] = None - """The Server Name Indication sent by the client.""" - - timestamp_start: float - """*Timestamp:* TCP SYN received""" - - def __init__(self, peername, sockname, timestamp_start): - self.id = str(uuid.uuid4()) - self.peername = peername - self.sockname = sockname - self.timestamp_start = timestamp_start - self.state = ConnectionState.OPEN - - def __str__(self): - if self.alpn: - tls_state = f", alpn={self.alpn.decode(errors='replace')}" - elif self.tls_established: - tls_state = ", tls" - else: - tls_state = "" - return f"Client({human.format_address(self.peername)}, state={self.state.name.lower()}{tls_state})" - - def get_state(self): - # Important: Retain full compatibility with old proxy core for now! - # This means we need to add all new fields to the old implementation. - return { - 'address': self.peername, - 'alpn': self.alpn, - 'cipher_name': self.cipher, - 'id': self.id, - 'mitmcert': self.mitmcert.get_state() if self.mitmcert is not None else None, - 'sni': self.sni, - 'timestamp_end': self.timestamp_end, - 'timestamp_start': self.timestamp_start, - 'timestamp_tls_setup': self.timestamp_tls_setup, - 'tls_established': self.tls_established, - 'tls_extensions': [], - 'tls_version': self.tls_version, - # only used in sans-io - 'state': self.state.value, - 'sockname': self.sockname, - 'error': self.error, - 'tls': self.tls, - 'certificate_list': [x.get_state() for x in self.certificate_list], - 'alpn_offers': self.alpn_offers, - 'cipher_list': self.cipher_list, - } - - @classmethod - def from_state(cls, state) -> "Client": - client = Client( - state["address"], - ("mitmproxy", 8080), - state["timestamp_start"] - ) - client.set_state(state) - return client - - def set_state(self, state): - self.peername = tuple(state["address"]) if state["address"] else None - self.alpn = state["alpn"] - self.cipher = state["cipher_name"] - self.id = state["id"] - self.sni = state["sni"] - self.timestamp_end = state["timestamp_end"] - self.timestamp_start = state["timestamp_start"] - self.timestamp_tls_setup = state["timestamp_tls_setup"] - self.tls_version = state["tls_version"] - # only used in sans-io - self.state = ConnectionState(state["state"]) - self.sockname = tuple(state["sockname"]) if state["sockname"] else None - self.error = state["error"] - self.tls = state["tls"] - self.certificate_list = [certs.Cert.from_state(x) for x in state["certificate_list"]] - self.mitmcert = certs.Cert.from_state(state["mitmcert"]) if state["mitmcert"] is not None else None - self.alpn_offers = state["alpn_offers"] - self.cipher_list = state["cipher_list"] - - @property - def address(self): # pragma: no cover - """*Deprecated:* An outdated alias for Client.peername.""" - warnings.warn("Client.address is deprecated, use Client.peername instead.", DeprecationWarning, stacklevel=2) - return self.peername - - @address.setter - def address(self, x): # pragma: no cover - warnings.warn("Client.address is deprecated, use Client.peername instead.", DeprecationWarning, stacklevel=2) - self.peername = x - - @property - def cipher_name(self) -> Optional[str]: # pragma: no cover - """*Deprecated:* An outdated alias for Connection.cipher.""" - warnings.warn("Client.cipher_name is deprecated, use Client.cipher instead.", DeprecationWarning, stacklevel=2) - return self.cipher - - @property - def clientcert(self) -> Optional[certs.Cert]: # pragma: no cover - """*Deprecated:* An outdated alias for Connection.certificate_list[0].""" - warnings.warn("Client.clientcert is deprecated, use Client.certificate_list instead.", DeprecationWarning, - stacklevel=2) - if self.certificate_list: - return self.certificate_list[0] - else: - return None - - @clientcert.setter - def clientcert(self, val): # pragma: no cover - warnings.warn("Client.clientcert is deprecated, use Client.certificate_list instead.", DeprecationWarning) - if val: - self.certificate_list = [val] - else: - self.certificate_list = [] - - -class Server(Connection): - """A connection between mitmproxy and an upstream server.""" - - peername: Optional[Address] = None - """The server's resolved `(ip, port)` tuple. Will be set during connection establishment.""" - sockname: Optional[Address] = None - address: Optional[Address] - """The server's `(host, port)` address tuple. The host can either be a domain or a plain IP address.""" - - timestamp_start: Optional[float] = None - """*Timestamp:* TCP SYN sent.""" - timestamp_tcp_setup: Optional[float] = None - """*Timestamp:* TCP ACK received.""" - - sni: Union[str, Literal[True], None] = True - via: Optional[server_spec.ServerSpec] = None - """An optional proxy server specification via which the connection should be established.""" - - def __init__(self, address: Optional[Address]): - self.id = str(uuid.uuid4()) - self.address = address - self.state = ConnectionState.CLOSED - - def __str__(self): - if self.alpn: - tls_state = f", alpn={self.alpn.decode(errors='replace')}" - elif self.tls_established: - tls_state = ", tls" - else: - tls_state = "" - if self.sockname: - local_port = f", src_port={self.sockname[1]}" - else: - local_port = "" - return f"Server({human.format_address(self.address)}, state={self.state.name.lower()}{tls_state}{local_port})" - - def get_state(self): - return { - 'address': self.address, - 'alpn': self.alpn, - 'id': self.id, - 'ip_address': self.peername, - 'sni': self.sni, - 'source_address': self.sockname, - 'timestamp_end': self.timestamp_end, - 'timestamp_start': self.timestamp_start, - 'timestamp_tcp_setup': self.timestamp_tcp_setup, - 'timestamp_tls_setup': self.timestamp_tls_setup, - 'tls_established': self.tls_established, - 'tls_version': self.tls_version, - 'via': None, - # only used in sans-io - 'state': self.state.value, - 'error': self.error, - 'tls': self.tls, - 'certificate_list': [x.get_state() for x in self.certificate_list], - 'alpn_offers': self.alpn_offers, - 'cipher_name': self.cipher, - 'cipher_list': self.cipher_list, - 'via2': self.via, - } - - @classmethod - def from_state(cls, state) -> "Server": - server = Server(None) - server.set_state(state) - return server - - def set_state(self, state): - self.address = tuple(state["address"]) if state["address"] else None - self.alpn = state["alpn"] - self.id = state["id"] - self.peername = tuple(state["ip_address"]) if state["ip_address"] else None - self.sni = state["sni"] - self.sockname = tuple(state["source_address"]) if state["source_address"] else None - self.timestamp_end = state["timestamp_end"] - self.timestamp_start = state["timestamp_start"] - self.timestamp_tcp_setup = state["timestamp_tcp_setup"] - self.timestamp_tls_setup = state["timestamp_tls_setup"] - self.tls_version = state["tls_version"] - self.state = ConnectionState(state["state"]) - self.error = state["error"] - self.tls = state["tls"] - self.certificate_list = [certs.Cert.from_state(x) for x in state["certificate_list"]] - self.alpn_offers = state["alpn_offers"] - self.cipher = state["cipher_name"] - self.cipher_list = state["cipher_list"] - self.via = state["via2"] - - @property - def ip_address(self) -> Optional[Address]: # pragma: no cover - """*Deprecated:* An outdated alias for `Server.peername`.""" - warnings.warn("Server.ip_address is deprecated, use Server.peername instead.", DeprecationWarning, stacklevel=2) - return self.peername - - @property - def cert(self) -> Optional[certs.Cert]: # pragma: no cover - """*Deprecated:* An outdated alias for `Connection.certificate_list[0]`.""" - warnings.warn("Server.cert is deprecated, use Server.certificate_list instead.", DeprecationWarning, - stacklevel=2) - if self.certificate_list: - return self.certificate_list[0] - else: - return None - - @cert.setter - def cert(self, val): # pragma: no cover - warnings.warn("Server.cert is deprecated, use Server.certificate_list instead.", DeprecationWarning, - stacklevel=2) - if val: - self.certificate_list = [val] - else: - self.certificate_list = [] - - class Context: """ The context object provided to each `mitmproxy.proxy.layer.Layer` by its parent layer. """ - client: Client - server: Server + client: connection.Client + server: connection.Server options: Options layers: List["mitmproxy.proxy.layer.Layer"] def __init__( - self, - client: Client, - options: Options, + self, + client: connection.Client, + options: Options, ) -> None: self.client = client self.options = options - self.server = Server(None) + self.server = connection.Server(None) self.layers = [] def fork(self) -> "Context": diff --git a/mitmproxy/proxy/events.py b/mitmproxy/proxy/events.py index fef221da7..fc9bee681 100644 --- a/mitmproxy/proxy/events.py +++ b/mitmproxy/proxy/events.py @@ -9,7 +9,7 @@ import warnings from dataclasses import dataclass, is_dataclass from mitmproxy.proxy import commands -from mitmproxy.proxy.context import Connection +from mitmproxy.connection import Connection class Event: diff --git a/mitmproxy/proxy/layer.py b/mitmproxy/proxy/layer.py index 6798557de..d4f7820f6 100644 --- a/mitmproxy/proxy/layer.py +++ b/mitmproxy/proxy/layer.py @@ -7,9 +7,10 @@ from abc import abstractmethod from dataclasses import dataclass from typing import Optional, List, ClassVar, Deque, NamedTuple, Generator, Any, TypeVar +from mitmproxy.connection import Connection from mitmproxy.proxy import commands, events from mitmproxy.proxy.commands import Command, StartHook -from mitmproxy.proxy.context import Connection, Context +from mitmproxy.proxy.context import Context T = TypeVar('T') CommandGenerator = Generator[Command, Any, T] @@ -96,8 +97,8 @@ class Layer: if self._paused: # did we just receive the reply we were waiting for? pause_finished = ( - isinstance(event, events.CommandCompleted) and - event.command is self._paused.command + isinstance(event, events.CommandCompleted) and + event.command is self._paused.command ) if self.debug is not None: yield self.__debug(f"{'>>' if pause_finished else '>!'} {event}") diff --git a/mitmproxy/proxy/layers/http/__init__.py b/mitmproxy/proxy/layers/http/__init__.py index 84211fca7..ba294f003 100644 --- a/mitmproxy/proxy/layers/http/__init__.py +++ b/mitmproxy/proxy/layers/http/__init__.py @@ -5,15 +5,14 @@ from dataclasses import dataclass from typing import Optional, Tuple, Union, Dict, DefaultDict, List from mitmproxy import flow, http +from mitmproxy.connection import Connection, ConnectionState, Server from mitmproxy.net import server_spec from mitmproxy.net.http import url from mitmproxy.proxy import commands, events, layer, tunnel -from mitmproxy.proxy.context import Connection, ConnectionState, Context, Server from mitmproxy.proxy.layers import tls, websocket, tcp from mitmproxy.proxy.layers.http import _upstream_proxy from mitmproxy.proxy.utils import expect from mitmproxy.utils import human - from ._base import HttpCommand, ReceiveHttp, StreamId, HttpConnection from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError @@ -21,6 +20,7 @@ from ._hooks import HttpConnectHook, HttpErrorHook, HttpRequestHeadersHook, Http HttpResponseHook from ._http1 import Http1Client, Http1Server from ._http2 import Http2Client, Http2Server +from ...context import Context class HTTPMode(enum.Enum): @@ -55,13 +55,13 @@ class GetHttpConnection(HttpCommand): def connection_spec_matches(self, connection: Connection) -> bool: return ( - isinstance(connection, Server) - and - self.address == connection.address - and - self.tls == connection.tls - and - self.via == connection.via + isinstance(connection, Server) + and + self.address == connection.address + and + self.tls == connection.tls + and + self.via == connection.via ) @@ -317,9 +317,9 @@ class HttpStream(layer.Layer): if self.flow.response.status_code == 101: is_websocket = ( - self.flow.response.headers.get("upgrade", "").lower() == "websocket" - and - self.flow.request.headers.get("Sec-WebSocket-Version", "") == "13" + self.flow.response.headers.get("upgrade", "").lower() == "websocket" + and + self.flow.request.headers.get("Sec-WebSocket-Version", "") == "13" ) if is_websocket and self.context.options.websocket: self.child_layer = websocket.WebsocketLayer(self.context, self.flow) @@ -338,7 +338,7 @@ class HttpStream(layer.Layer): def check_killed(self, emit_error_hook: bool) -> layer.CommandGenerator[bool]: killed_by_us = ( - self.flow.error and self.flow.error.msg == flow.Error.KILLED_MESSAGE + self.flow.error and self.flow.error.msg == flow.Error.KILLED_MESSAGE ) # The client may have closed the connection while we were waiting for the hook to complete. # We peek into the event queue to see if that is the case. @@ -366,18 +366,18 @@ class HttpStream(layer.Layer): return False def handle_protocol_error( - self, - event: Union[RequestProtocolError, ResponseProtocolError] + self, + event: Union[RequestProtocolError, ResponseProtocolError] ) -> layer.CommandGenerator[None]: is_client_error_but_we_already_talk_upstream = ( - isinstance(event, RequestProtocolError) - and self.client_state in (self.state_stream_request_body, self.state_done) - and self.server_state != self.state_errored + isinstance(event, RequestProtocolError) + and self.client_state in (self.state_stream_request_body, self.state_done) + and self.server_state != self.state_errored ) need_error_hook = not ( - self.client_state in (self.state_wait_for_request_headers, self.state_errored) - or - self.server_state in (self.state_done, self.state_errored) + self.client_state in (self.state_wait_for_request_headers, self.state_errored) + or + self.server_state in (self.state_done, self.state_errored) ) if is_client_error_but_we_already_talk_upstream: @@ -579,9 +579,9 @@ class HttpLayer(layer.Layer): raise AssertionError(f"Unexpected event: {event}") def event_to_child( - self, - child: Union[layer.Layer, HttpStream], - event: events.Event, + self, + child: Union[layer.Layer, HttpStream], + event: events.Event, ) -> layer.CommandGenerator[None]: for command in child.handle_event(event): assert isinstance(command, commands.Command) @@ -622,13 +622,13 @@ class HttpLayer(layer.Layer): for connection in self.connections: # see "tricky multiplexing edge case" in make_http_connection for an explanation conn_is_pending_or_h2 = ( - connection.alpn == b"h2" - or connection in self.waiting_for_establishment + connection.alpn == b"h2" + or connection in self.waiting_for_establishment ) h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2 connection_suitable = ( - event.connection_spec_matches(connection) - and not h2_to_h1 + event.connection_spec_matches(connection) + and not h2_to_h1 ) if connection_suitable: if connection in self.waiting_for_establishment: @@ -639,9 +639,9 @@ class HttpLayer(layer.Layer): return can_use_context_connection = ( - self.context.server not in self.connections and - self.context.server.connected and - event.connection_spec_matches(self.context.server) + self.context.server not in self.connections and + self.context.server.connected and + event.connection_spec_matches(self.context.server) ) context = self.context.fork() diff --git a/mitmproxy/proxy/layers/http/_base.py b/mitmproxy/proxy/layers/http/_base.py index 4dacecd90..db66f65c6 100644 --- a/mitmproxy/proxy/layers/http/_base.py +++ b/mitmproxy/proxy/layers/http/_base.py @@ -1,7 +1,8 @@ from dataclasses import dataclass from mitmproxy.proxy import events, layer, commands -from mitmproxy.proxy.context import Connection, Context +from mitmproxy.connection import Connection +from mitmproxy.proxy.context import Context StreamId = int diff --git a/mitmproxy/proxy/layers/http/_http1.py b/mitmproxy/proxy/layers/http/_http1.py index 14da52ab8..23f2d7ce0 100644 --- a/mitmproxy/proxy/layers/http/_http1.py +++ b/mitmproxy/proxy/layers/http/_http1.py @@ -9,13 +9,14 @@ from h11._receivebuffer import ReceiveBuffer from mitmproxy import http, version from mitmproxy.net.http import http1, status_codes from mitmproxy.proxy import commands, events, layer -from mitmproxy.proxy.context import Connection, ConnectionState, Context +from mitmproxy.connection import Connection, ConnectionState from mitmproxy.proxy.layers.http._base import ReceiveHttp, StreamId from mitmproxy.proxy.utils import expect from mitmproxy.utils import human from ._base import HttpConnection from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError +from ...context import Context TBodyReader = Union[ChunkedReader, Http10Reader, ContentLengthReader] diff --git a/mitmproxy/proxy/layers/http/_http2.py b/mitmproxy/proxy/layers/http/_http2.py index a233f1807..c792f18ba 100644 --- a/mitmproxy/proxy/layers/http/_http2.py +++ b/mitmproxy/proxy/layers/http/_http2.py @@ -13,6 +13,7 @@ import h2.stream import h2.utilities from mitmproxy import http +from mitmproxy.connection import Connection from mitmproxy.net.http import url, status_codes from mitmproxy.utils import human from . import RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ @@ -20,7 +21,7 @@ from . import RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolE from ._base import HttpConnection, HttpEvent, ReceiveHttp from ._http_h2 import BufferedH2Connection, H2ConnectionLogger from ...commands import CloseConnection, Log, SendData -from ...context import Connection, Context +from ...context import Context from ...events import ConnectionClosed, DataReceived, Event, Start from ...layer import CommandGenerator from ...utils import expect diff --git a/mitmproxy/proxy/layers/http/_upstream_proxy.py b/mitmproxy/proxy/layers/http/_upstream_proxy.py index 5260e5f08..d8d1af348 100644 --- a/mitmproxy/proxy/layers/http/_upstream_proxy.py +++ b/mitmproxy/proxy/layers/http/_upstream_proxy.py @@ -3,7 +3,7 @@ from typing import Optional, Tuple from h11._receivebuffer import ReceiveBuffer -from mitmproxy import http +from mitmproxy import http, connection from mitmproxy.net import server_spec from mitmproxy.net.http import http1 from mitmproxy.proxy import commands, context, layer, tunnel @@ -13,13 +13,13 @@ from mitmproxy.utils import human class HttpUpstreamProxy(tunnel.TunnelLayer): buf: ReceiveBuffer send_connect: bool - conn: context.Server - tunnel_connection: context.Server + conn: connection.Server + tunnel_connection: connection.Server def __init__( self, ctx: context.Context, - tunnel_conn: context.Server, + tunnel_conn: connection.Server, send_connect: bool ): super().__init__( diff --git a/mitmproxy/proxy/layers/tcp.py b/mitmproxy/proxy/layers/tcp.py index 728519bfa..6b6f65f20 100644 --- a/mitmproxy/proxy/layers/tcp.py +++ b/mitmproxy/proxy/layers/tcp.py @@ -4,7 +4,8 @@ from typing import Optional from mitmproxy import flow, tcp from mitmproxy.proxy import commands, events, layer from mitmproxy.proxy.commands import StartHook -from mitmproxy.proxy.context import ConnectionState, Context, Connection +from mitmproxy.connection import ConnectionState, Connection +from mitmproxy.proxy.context import Context from mitmproxy.proxy.utils import expect diff --git a/mitmproxy/proxy/layers/tls.py b/mitmproxy/proxy/layers/tls.py index c617c4960..d8a33ac9c 100644 --- a/mitmproxy/proxy/layers/tls.py +++ b/mitmproxy/proxy/layers/tls.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from typing import Iterator, Optional, Tuple from OpenSSL import SSL -from mitmproxy import certs +from mitmproxy import certs, connection from mitmproxy.net import tls as net_tls from mitmproxy.proxy import commands, events, layer, tunnel from mitmproxy.proxy import context @@ -116,7 +116,7 @@ class TlsClienthelloHook(StartHook): @dataclass class TlsStartData: - conn: context.Connection + conn: connection.Connection context: context.Context ssl_conn: Optional[SSL.Connection] = None @@ -136,7 +136,7 @@ class _TLSLayer(tunnel.TunnelLayer): tls: SSL.Connection = None """The OpenSSL connection object""" - def __init__(self, context: context.Context, conn: context.Connection): + def __init__(self, context: context.Context, conn: connection.Connection): super().__init__( context, tunnel_connection=conn, @@ -240,7 +240,7 @@ class _TLSLayer(tunnel.TunnelLayer): events.DataReceived(self.conn, bytes(plaintext)) ) if close: - self.conn.state &= ~context.ConnectionState.CAN_READ + self.conn.state &= ~connection.ConnectionState.CAN_READ if self.debug: yield commands.Log(f"{self.debug}[tls] close_notify {self.conn}", level="debug") yield from self.event_to_child( @@ -268,7 +268,7 @@ class ServerTLSLayer(_TLSLayer): """ command_to_reply_to: Optional[commands.OpenConnection] = None - def __init__(self, context: context.Context, conn: Optional[context.Server] = None): + def __init__(self, context: context.Context, conn: Optional[connection.Server] = None): super().__init__(context, conn or context.server) def start_handshake(self) -> layer.CommandGenerator[None]: @@ -373,4 +373,4 @@ class MockTLSLayer(_TLSLayer): """ def __init__(self, ctx: context.Context): - super().__init__(ctx, context.Server(None)) + super().__init__(ctx, connection.Server(None)) diff --git a/mitmproxy/proxy/layers/websocket.py b/mitmproxy/proxy/layers/websocket.py index 84a3738bf..a09a76aef 100644 --- a/mitmproxy/proxy/layers/websocket.py +++ b/mitmproxy/proxy/layers/websocket.py @@ -5,8 +5,8 @@ import wsproto import wsproto.extensions import wsproto.frame_protocol import wsproto.utilities -from mitmproxy import flow, websocket, http -from mitmproxy.proxy import commands, events, layer, context +from mitmproxy import flow, websocket, http, connection +from mitmproxy.proxy import commands, events, layer from mitmproxy.proxy.commands import StartHook from mitmproxy.proxy.context import Context from mitmproxy.proxy.utils import expect @@ -60,10 +60,10 @@ class WebsocketConnection(wsproto.Connection): - we add a framebuffer for incomplete messages - we wrap .send() so that we can directly yield it. """ - conn: context.Connection + conn: connection.Connection frame_buf: List[Union[str, bytes]] - def __init__(self, *args, conn: context.Connection, **kwargs): + def __init__(self, *args, conn: connection.Connection, **kwargs): super(WebsocketConnection, self).__init__(*args, **kwargs) self.conn = conn self.frame_buf = [] diff --git a/mitmproxy/proxy/server.py b/mitmproxy/proxy/server.py index cbedaf3fa..a57071274 100644 --- a/mitmproxy/proxy/server.py +++ b/mitmproxy/proxy/server.py @@ -17,9 +17,10 @@ from dataclasses import dataclass from OpenSSL import SSL from mitmproxy import http, options as moptions +from mitmproxy.proxy.context import Context from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import commands, events, layer, layers, server_hooks -from mitmproxy.proxy.context import Address, Client, Connection, ConnectionState, Context +from mitmproxy.connection import Address, Client, Connection, ConnectionState from mitmproxy.proxy.layers import tls from mitmproxy.utils import asyncio_utils from mitmproxy.utils import human diff --git a/mitmproxy/proxy/server_hooks.py b/mitmproxy/proxy/server_hooks.py index 50d07d99d..592beb6ce 100644 --- a/mitmproxy/proxy/server_hooks.py +++ b/mitmproxy/proxy/server_hooks.py @@ -1,6 +1,7 @@ from dataclasses import dataclass -from . import commands, context +from mitmproxy import connection +from . import commands @dataclass @@ -11,7 +12,7 @@ class ClientConnectedHook(commands.StartHook): Setting client.error kills the connection. """ - client: context.Client + client: connection.Client @dataclass @@ -20,13 +21,13 @@ class ClientDisconnectedHook(commands.StartHook): A client connection has been closed (either by us or the client). """ blocking = False - client: context.Client + client: connection.Client @dataclass class ServerConnectionHookData: - server: context.Server - client: context.Client + server: connection.Server + client: connection.Client @dataclass diff --git a/mitmproxy/proxy/tunnel.py b/mitmproxy/proxy/tunnel.py index 8d062aac5..14c54c6f5 100644 --- a/mitmproxy/proxy/tunnel.py +++ b/mitmproxy/proxy/tunnel.py @@ -1,6 +1,7 @@ from enum import Enum, auto from typing import List, Optional, Tuple +from mitmproxy import connection from mitmproxy.proxy import commands, context, events, layer from mitmproxy.proxy.layer import Layer @@ -18,9 +19,9 @@ class TunnelLayer(layer.Layer): or TLS. """ child_layer: layer.Layer - tunnel_connection: context.Connection + tunnel_connection: connection.Connection """The 'outer' connection which provides the tunnel protocol I/O""" - conn: context.Connection + conn: connection.Connection """The 'inner' connection which provides data I/O""" tunnel_state: TunnelState = TunnelState.INACTIVE command_to_reply_to: Optional[commands.OpenConnection] = None @@ -33,8 +34,8 @@ class TunnelLayer(layer.Layer): def __init__( self, context: context.Context, - tunnel_connection: context.Connection, - conn: context.Connection, + tunnel_connection: connection.Connection, + conn: connection.Connection, ): super().__init__(context) self.tunnel_connection = tunnel_connection @@ -47,7 +48,7 @@ class TunnelLayer(layer.Layer): def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]: if isinstance(event, events.Start): - if self.tunnel_connection.state is not context.ConnectionState.CLOSED: + if self.tunnel_connection.state is not connection.ConnectionState.CLOSED: # we might be in the interesting state here where the connection is already half-closed, # for example because next_layer buffered events and the client disconnected in the meantime. # we still expect a close event to arrive, so we carry on here as normal for now. @@ -60,17 +61,17 @@ class TunnelLayer(layer.Layer): done, err = yield from self.receive_handshake_data(event.data) if done: if self.conn != self.tunnel_connection: - self.conn.state = context.ConnectionState.OPEN + self.conn.state = connection.ConnectionState.OPEN if err: if self.conn != self.tunnel_connection: - self.conn.state = context.ConnectionState.CLOSED + self.conn.state = connection.ConnectionState.CLOSED yield from self.on_handshake_error(err) if done or err: yield from self._handshake_finished(err) else: yield from self.receive_data(event.data) elif isinstance(event, events.ConnectionClosed): - self.conn.state &= ~context.ConnectionState.CAN_READ + self.conn.state &= ~connection.ConnectionState.CAN_READ if self.tunnel_state is TunnelState.OPEN: yield from self.receive_close() elif self.tunnel_state is TunnelState.ESTABLISHING: @@ -107,9 +108,9 @@ class TunnelLayer(layer.Layer): elif isinstance(command, commands.CloseConnection): if self.conn != self.tunnel_connection: if command.half_close: - self.conn.state &= ~context.ConnectionState.CAN_WRITE + self.conn.state &= ~connection.ConnectionState.CAN_WRITE else: - self.conn.state = context.ConnectionState.CLOSED + self.conn.state = connection.ConnectionState.CLOSED yield from self.send_close(command.half_close) elif isinstance(command, commands.OpenConnection): # create our own OpenConnection command object that blocks here. diff --git a/mitmproxy/test/tflow.py b/mitmproxy/test/tflow.py index b0a0d74b1..5e745b6cb 100644 --- a/mitmproxy/test/tflow.py +++ b/mitmproxy/test/tflow.py @@ -1,12 +1,12 @@ import uuid +from mitmproxy import connection from mitmproxy import controller from mitmproxy import flow from mitmproxy import http from mitmproxy import tcp from mitmproxy import websocket from mitmproxy.net.http import status_codes -from mitmproxy.proxy import context from mitmproxy.test import tutils from wsproto.frame_protocol import Opcode @@ -144,8 +144,8 @@ def tdummyflow(client_conn=True, server_conn=True, err=None): return f -def tclient_conn() -> context.Client: - c = context.Client.from_state(dict( +def tclient_conn() -> connection.Client: + c = connection.Client.from_state(dict( id=str(uuid.uuid4()), address=("127.0.0.1", 22), mitmcert=None, @@ -170,8 +170,8 @@ def tclient_conn() -> context.Client: return c -def tserver_conn() -> context.Server: - c = context.Server.from_state(dict( +def tserver_conn() -> connection.Server: + c = connection.Server.from_state(dict( id=str(uuid.uuid4()), address=("address", 22), source_address=("address", 22), diff --git a/test/mitmproxy/addons/test_block.py b/test/mitmproxy/addons/test_block.py index 354bf2d2d..ea147a4ca 100644 --- a/test/mitmproxy/addons/test_block.py +++ b/test/mitmproxy/addons/test_block.py @@ -1,7 +1,7 @@ import pytest +from mitmproxy import connection from mitmproxy.addons import block -from mitmproxy.proxy import context from mitmproxy.test import taddons @@ -28,7 +28,6 @@ from mitmproxy.test import taddons (True, False, True, ("2001:4860:4860::8888",)), (True, False, True, (r"2001:4860:4860::8888%scope",)), - # block_private: loopback (False, True, False, ("127.0.0.1",)), (False, True, False, ("::1",)), @@ -56,6 +55,6 @@ async def test_block_global(block_global, block_private, should_be_killed, addre ar = block.Block() with taddons.context(ar) as tctx: tctx.configure(ar, block_global=block_global, block_private=block_private) - client = context.Client(address, ("127.0.0.1", 8080), 1607699500) + client = connection.Client(address, ("127.0.0.1", 8080), 1607699500) ar.client_connected(client) assert bool(client.error) == should_be_killed diff --git a/test/mitmproxy/addons/test_clientplayback.py b/test/mitmproxy/addons/test_clientplayback.py index 668639953..0704b712f 100644 --- a/test/mitmproxy/addons/test_clientplayback.py +++ b/test/mitmproxy/addons/test_clientplayback.py @@ -5,7 +5,7 @@ import pytest from mitmproxy.addons.clientplayback import ClientPlayback, ReplayHandler from mitmproxy.exceptions import CommandError, OptionsError -from mitmproxy.proxy.context import Address +from mitmproxy.connection import Address from mitmproxy.test import taddons, tflow diff --git a/test/mitmproxy/addons/test_next_layer.py b/test/mitmproxy/addons/test_next_layer.py index 25e96b449..a949e9395 100644 --- a/test/mitmproxy/addons/test_next_layer.py +++ b/test/mitmproxy/addons/test_next_layer.py @@ -2,6 +2,7 @@ from unittest.mock import MagicMock import pytest +from mitmproxy import connection from mitmproxy.addons.next_layer import NextLayer from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import context, layers @@ -10,7 +11,7 @@ from mitmproxy.test import taddons @pytest.fixture def tctx(): - context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) client_hello_no_extensions = bytes.fromhex( diff --git a/test/mitmproxy/addons/test_proxyserver.py b/test/mitmproxy/addons/test_proxyserver.py index 92a25a927..333405ec7 100644 --- a/test/mitmproxy/addons/test_proxyserver.py +++ b/test/mitmproxy/addons/test_proxyserver.py @@ -6,7 +6,7 @@ import pytest from mitmproxy.addons.proxyserver import Proxyserver from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import layers -from mitmproxy.proxy.context import Address +from mitmproxy.connection import Address from mitmproxy.test import taddons diff --git a/test/mitmproxy/addons/test_tlsconfig.py b/test/mitmproxy/addons/test_tlsconfig.py index de318583a..3a0e012a2 100644 --- a/test/mitmproxy/addons/test_tlsconfig.py +++ b/test/mitmproxy/addons/test_tlsconfig.py @@ -6,7 +6,7 @@ from typing import Union import pytest from OpenSSL import SSL -from mitmproxy import certs +from mitmproxy import certs, connection from mitmproxy.addons import tlsconfig from mitmproxy.proxy import context from mitmproxy.proxy.layers import tls @@ -54,7 +54,7 @@ class TestTlsConfig: with taddons.context(ta) as tctx: ta.configure(["confdir"]) - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) # Edge case first: We don't have _any_ idea about the server, so we just return "mitmproxy" as subject. entry = ta.get_cert(ctx) @@ -77,7 +77,7 @@ class TestTlsConfig: # only really testing for coverage here, there's no point in mirroring the individual conditions ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ch = tls.ClientHelloData(ctx) ta.tls_clienthello(ch) assert not ch.establish_server_tls_first @@ -113,7 +113,7 @@ class TestTlsConfig: certs=[tdata.path("mitmproxy/net/data/verificationcerts/trusted-leaf.pem")], ciphers_client="ECDHE-ECDSA-AES128-GCM-SHA256", ) - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) tls_start = tls.TlsStartData(ctx.client, context=ctx) ta.tls_start(tls_start) @@ -125,7 +125,7 @@ class TestTlsConfig: def test_create_proxy_server_ssl_conn_verify_failed(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.client.alpn_offers = [b"h2"] ctx.client.cipher_list = ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES128-SHA"] ctx.server.address = ("example.mitmproxy.org", 443) @@ -140,7 +140,7 @@ class TestTlsConfig: def test_create_proxy_server_ssl_conn_verify_ok(self, tdata): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tctx.configure(ta, ssl_verify_upstream_trusted_ca=tdata.path( "mitmproxy/net/data/verificationcerts/trusted-root.crt")) @@ -154,7 +154,7 @@ class TestTlsConfig: def test_create_proxy_server_ssl_conn_insecure(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tctx.configure( @@ -173,7 +173,7 @@ class TestTlsConfig: def test_alpn_selection(self): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tls_start = tls.TlsStartData(ctx.server, context=ctx) @@ -203,7 +203,7 @@ class TestTlsConfig: def test_client_cert_file(self, tdata, client_certs): ta = tlsconfig.TlsConfig() with taddons.context(ta) as tctx: - ctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) + ctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), tctx.options) ctx.server.address = ("example.mitmproxy.org", 443) tctx.configure( ta, diff --git a/test/mitmproxy/net/http/test_headers.py b/test/mitmproxy/net/http/test_headers.py index 3609cea6c..a5238a1e3 100644 --- a/test/mitmproxy/net/http/test_headers.py +++ b/test/mitmproxy/net/http/test_headers.py @@ -1,9 +1,11 @@ import collections + import pytest from mitmproxy.http import Headers from mitmproxy.net.http.headers import parse_content_type, assemble_content_type + class TestHeaders: def _2host(self): return Headers( @@ -79,4 +81,5 @@ def test_assemble_content_type(): p = assemble_content_type assert p("text", "html", {}) == "text/html" assert p("text", "html", {"charset": "utf8"}) == "text/html; charset=utf8" - assert p("text", "html", collections.OrderedDict([("charset", "utf8"), ("foo", "bar")])) == "text/html; charset=utf8; foo=bar" + assert p("text", "html", + collections.OrderedDict([("charset", "utf8"), ("foo", "bar")])) == "text/html; charset=utf8; foo=bar" diff --git a/test/mitmproxy/proxy/conftest.py b/test/mitmproxy/proxy/conftest.py index 2ea6c9ae1..6dde89ffa 100644 --- a/test/mitmproxy/proxy/conftest.py +++ b/test/mitmproxy/proxy/conftest.py @@ -3,7 +3,7 @@ import os import pytest from hypothesis import settings -from mitmproxy import options +from mitmproxy import options, connection from mitmproxy.addons.core import Core from mitmproxy.addons.proxyserver import Proxyserver from mitmproxy.addons.termlog import TermLog @@ -17,7 +17,7 @@ def tctx() -> context.Context: TermLog().load(opts) Core().load(opts) return context.Context( - context.Client( + connection.Client( ("client", 1234), ("127.0.0.1", 8080), 1605699329 diff --git a/test/mitmproxy/proxy/layers/http/test_http.py b/test/mitmproxy/proxy/layers/http/test_http.py index 32b988e8f..875d34e39 100644 --- a/test/mitmproxy/proxy/layers/http/test_http.py +++ b/test/mitmproxy/proxy/layers/http/test_http.py @@ -6,7 +6,7 @@ from mitmproxy.net.server_spec import ServerSpec from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import layer from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData, Log -from mitmproxy.proxy.context import ConnectionState, Server +from mitmproxy.connection import ConnectionState, Server from mitmproxy.proxy.events import ConnectionClosed, DataReceived from mitmproxy.proxy.layers import TCPLayer, http, tls from mitmproxy.proxy.layers.tcp import TcpStartHook diff --git a/test/mitmproxy/proxy/layers/http/test_http2.py b/test/mitmproxy/proxy/layers/http/test_http2.py index aa3b75cc6..52dedce98 100644 --- a/test/mitmproxy/proxy/layers/http/test_http2.py +++ b/test/mitmproxy/proxy/layers/http/test_http2.py @@ -9,9 +9,10 @@ from h2.errors import ErrorCodes from mitmproxy.flow import Error from mitmproxy.http import HTTPFlow, Headers, Request from mitmproxy.net.http import status_codes +from mitmproxy.proxy.context import Context from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData -from mitmproxy.proxy.context import Context, Server +from mitmproxy.connection import Server from mitmproxy.proxy.events import ConnectionClosed, DataReceived from mitmproxy.proxy.layers import http from mitmproxy.proxy.layers.http._http2 import split_pseudo_headers, Http2Client diff --git a/test/mitmproxy/proxy/layers/http/test_http_fuzz.py b/test/mitmproxy/proxy/layers/http/test_http_fuzz.py index 93d4ffd26..6da9b583c 100644 --- a/test/mitmproxy/proxy/layers/http/test_http_fuzz.py +++ b/test/mitmproxy/proxy/layers/http/test_http_fuzz.py @@ -6,13 +6,13 @@ from hypothesis import example, given from hypothesis.strategies import binary, booleans, composite, dictionaries, integers, lists, sampled_from, sets, text, \ data -from mitmproxy import options +from mitmproxy import options, connection from mitmproxy.addons.proxyserver import Proxyserver +from mitmproxy.connection import Server from mitmproxy.http import HTTPFlow from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy import context, events from mitmproxy.proxy.commands import OpenConnection, SendData -from mitmproxy.proxy.context import Server from mitmproxy.proxy.events import DataReceived, Start, ConnectionClosed from mitmproxy.proxy.layers import http from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory @@ -100,7 +100,7 @@ def h2_responses(draw): @given(chunks(mutations(h1_requests()))) def test_fuzz_h1_request(data): - tctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) + tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) layer = http.HttpLayer(tctx, HTTPMode.regular) for _ in layer.handle_event(Start()): @@ -113,8 +113,8 @@ def test_fuzz_h1_request(data): @given(chunks(mutations(h2_responses()))) @example([b'0 OK\r\n\r\n', b'\r\n', b'5\r\n12345\r\n0\r\n\r\n']) def test_fuzz_h1_response(data): - tctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) - server = Placeholder(context.Server) + tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) + server = Placeholder(connection.Server) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) assert ( playbook @@ -207,7 +207,7 @@ def h2_frames(draw): def h2_layer(opts): - tctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) + tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) tctx.client.alpn = b"h2" layer = http.HttpLayer(tctx, HTTPMode.regular) @@ -246,9 +246,9 @@ def test_fuzz_h2_request_mutations(chunks): def _h2_response(chunks): - tctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) + tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) - server = Placeholder(context.Server) + server = Placeholder(connection.Server) assert ( playbook >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") @@ -314,7 +314,7 @@ def _test_cancel(stream_req, stream_resp, draw): """ Test that we don't raise an exception if someone disconnects. """ - tctx = context.Context(context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) + tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts) playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) diff --git a/test/mitmproxy/proxy/layers/http/test_http_version_interop.py b/test/mitmproxy/proxy/layers/http/test_http_version_interop.py index 945029677..923516854 100644 --- a/test/mitmproxy/proxy/layers/http/test_http_version_interop.py +++ b/test/mitmproxy/proxy/layers/http/test_http_version_interop.py @@ -5,9 +5,10 @@ import h2.connection import h2.events from mitmproxy.http import HTTPFlow +from mitmproxy.proxy.context import Context from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData -from mitmproxy.proxy.context import Context, Server +from mitmproxy.connection import Server from mitmproxy.proxy.events import DataReceived from mitmproxy.proxy.layers import http from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory diff --git a/test/mitmproxy/proxy/layers/test_modes.py b/test/mitmproxy/proxy/layers/test_modes.py index 48894a757..b4d6fcab5 100644 --- a/test/mitmproxy/proxy/layers/test_modes.py +++ b/test/mitmproxy/proxy/layers/test_modes.py @@ -3,9 +3,10 @@ import copy import pytest from mitmproxy import platform +from mitmproxy.proxy.context import Context from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData, GetSocket, Log -from mitmproxy.proxy.context import Client, Context, Server +from mitmproxy.connection import Client, Server from mitmproxy.proxy.events import DataReceived, ConnectionClosed from mitmproxy.proxy.layer import NextLayer, NextLayerHook from mitmproxy.proxy.layers import http, modes, tcp, tls diff --git a/test/mitmproxy/proxy/layers/test_tcp.py b/test/mitmproxy/proxy/layers/test_tcp.py index ce2fc1b55..f899c637f 100644 --- a/test/mitmproxy/proxy/layers/test_tcp.py +++ b/test/mitmproxy/proxy/layers/test_tcp.py @@ -1,7 +1,7 @@ import pytest from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData -from mitmproxy.proxy.context import ConnectionState +from mitmproxy.connection import ConnectionState from mitmproxy.proxy.events import ConnectionClosed, DataReceived from mitmproxy.proxy.layers import tcp from mitmproxy.tcp import TCPFlow diff --git a/test/mitmproxy/proxy/layers/test_tls.py b/test/mitmproxy/proxy/layers/test_tls.py index 925896a09..f158bb53d 100644 --- a/test/mitmproxy/proxy/layers/test_tls.py +++ b/test/mitmproxy/proxy/layers/test_tls.py @@ -4,8 +4,9 @@ import typing import pytest from OpenSSL import SSL +from mitmproxy import connection +from mitmproxy.connection import ConnectionState, Server from mitmproxy.proxy import commands, context, events, layer -from mitmproxy.proxy.context import ConnectionState from mitmproxy.proxy.layers import tls from mitmproxy.utils import data from test.mitmproxy.proxy import tutils @@ -121,7 +122,7 @@ class SSLTest: return self.obj.do_handshake() -def _test_echo(playbook: tutils.Playbook, tssl: SSLTest, conn: context.Connection) -> None: +def _test_echo(playbook: tutils.Playbook, tssl: SSLTest, conn: connection.Connection) -> None: tssl.obj.write(b"Hello World") data = tutils.Placeholder(bytes) assert ( @@ -145,7 +146,7 @@ class TlsEchoLayer(tutils.EchoLayer): yield from super()._handle_event(event) -def interact(playbook: tutils.Playbook, conn: context.Connection, tssl: SSLTest): +def interact(playbook: tutils.Playbook, conn: connection.Connection, tssl: SSLTest): data = tutils.Placeholder(bytes) assert ( playbook @@ -383,7 +384,7 @@ class TestClientTLS: # Echo _test_echo(playbook, tssl_client, tctx.client) - other_server = context.Server(None) + other_server = Server(None) assert ( playbook >> events.DataReceived(other_server, b"Plaintext") diff --git a/test/mitmproxy/proxy/layers/test_websocket.py b/test/mitmproxy/proxy/layers/test_websocket.py index 7b69da44a..f4a654d47 100644 --- a/test/mitmproxy/proxy/layers/test_websocket.py +++ b/test/mitmproxy/proxy/layers/test_websocket.py @@ -8,7 +8,7 @@ import wsproto.events from mitmproxy.http import HTTPFlow, Request, Response from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.commands import SendData, CloseConnection, Log -from mitmproxy.proxy.context import ConnectionState +from mitmproxy.connection import ConnectionState from mitmproxy.proxy.events import DataReceived, ConnectionClosed from mitmproxy.proxy.layers import http, websocket from mitmproxy.websocket import WebSocketFlow diff --git a/test/mitmproxy/proxy/test_commands.py b/test/mitmproxy/proxy/test_commands.py index 1fbc4d46a..ead32ca3a 100644 --- a/test/mitmproxy/proxy/test_commands.py +++ b/test/mitmproxy/proxy/test_commands.py @@ -2,13 +2,14 @@ from dataclasses import dataclass import pytest +from mitmproxy import connection from mitmproxy.hooks import all_hooks -from mitmproxy.proxy import commands, context +from mitmproxy.proxy import commands @pytest.fixture -def tconn() -> context.Server: - return context.Server(None) +def tconn() -> connection.Server: + return connection.Server(None) def test_dataclasses(tconn): diff --git a/test/mitmproxy/proxy/test_context.py b/test/mitmproxy/proxy/test_context.py index c1a4516c8..9d58b4e81 100644 --- a/test/mitmproxy/proxy/test_context.py +++ b/test/mitmproxy/proxy/test_context.py @@ -1,10 +1,11 @@ +from mitmproxy import connection from mitmproxy.proxy import context from mitmproxy.test import tflow, taddons class TestConnection: def test_basic(self): - c = context.Client( + c = connection.Client( ("127.0.0.1", 52314), ("127.0.0.1", 8080), 1607780791 @@ -13,7 +14,7 @@ class TestConnection: c.timestamp_tls_setup = 1607780792 assert c.tls_established assert c.connected - c.state = context.ConnectionState.CAN_WRITE + c.state = connection.ConnectionState.CAN_WRITE assert not c.connected def test_eq(self): @@ -30,7 +31,7 @@ class TestConnection: class TestClient: def test_basic(self): - c = context.Client( + c = connection.Client( ("127.0.0.1", 52314), ("127.0.0.1", 8080), 1607780791 @@ -44,7 +45,7 @@ class TestClient: def test_state(self): c = tflow.tclient_conn() - assert context.Client.from_state(c.get_state()).get_state() == c.get_state() + assert connection.Client.from_state(c.get_state()).get_state() == c.get_state() c2 = tflow.tclient_conn() assert c != c2 @@ -61,7 +62,7 @@ class TestClient: class TestServer: def test_basic(self): - s = context.Server(("address", 22)) + s = connection.Server(("address", 22)) assert repr(s) assert str(s) s.timestamp_tls_setup = 1607780791 diff --git a/test/mitmproxy/proxy/test_events.py b/test/mitmproxy/proxy/test_events.py index 21cc98015..df35b216f 100644 --- a/test/mitmproxy/proxy/test_events.py +++ b/test/mitmproxy/proxy/test_events.py @@ -2,12 +2,13 @@ from unittest.mock import Mock import pytest -from mitmproxy.proxy import events, context, commands +from mitmproxy import connection +from mitmproxy.proxy import events, commands @pytest.fixture -def tconn() -> context.Server: - return context.Server(None) +def tconn() -> connection.Server: + return connection.Server(None) def test_dataclasses(tconn): diff --git a/test/mitmproxy/proxy/test_tunnel.py b/test/mitmproxy/proxy/test_tunnel.py index b3bac0249..6f852ce68 100644 --- a/test/mitmproxy/proxy/test_tunnel.py +++ b/test/mitmproxy/proxy/test_tunnel.py @@ -4,7 +4,8 @@ import pytest from mitmproxy.proxy import tunnel, layer from mitmproxy.proxy.commands import SendData, Log, CloseConnection, OpenConnection -from mitmproxy.proxy.context import Context, Server, ConnectionState +from mitmproxy.connection import Server, ConnectionState +from mitmproxy.proxy.context import Context from mitmproxy.proxy.events import Event, DataReceived, Start, ConnectionClosed from test.mitmproxy.proxy.tutils import Playbook, reply diff --git a/test/mitmproxy/proxy/tutils.py b/test/mitmproxy/proxy/tutils.py index 0e55fd161..0a38985af 100644 --- a/test/mitmproxy/proxy/tutils.py +++ b/test/mitmproxy/proxy/tutils.py @@ -7,7 +7,7 @@ import typing from mitmproxy.proxy import commands, context, layer from mitmproxy.proxy import events -from mitmproxy.proxy.context import ConnectionState +from mitmproxy.connection import ConnectionState from mitmproxy.proxy.events import command_reply_subclasses from mitmproxy.proxy.layer import Layer