move Connections to mitmproxy.connection

This commit is contained in:
Maximilian Hils 2021-02-04 03:20:06 +01:00
parent d68c364b35
commit dc6c5f55cd
44 changed files with 559 additions and 532 deletions

View File

@ -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

View File

@ -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.

View File

@ -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):

View File

@ -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:

View File

@ -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

374
mitmproxy/connection.py Normal file
View File

@ -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"
]

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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":

View File

@ -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:

View File

@ -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}")

View File

@ -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()

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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__(

View File

@ -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

View File

@ -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))

View File

@ -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 = []

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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,

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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