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.hooks import UpdateHook
from mitmproxy.net import server_spec from mitmproxy.net import server_spec
from mitmproxy.options import Options from mitmproxy.options import Options
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import commands, events, layers, server 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.proxy.layer import CommandGenerator
from mitmproxy.utils import asyncio_utils from mitmproxy.utils import asyncio_utils

View File

@ -1,7 +1,7 @@
import re import re
from typing import Type, Sequence, Union, Tuple, Any, Iterable, Optional, List 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.net.tls import is_tls_record_magic
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import context, layer, layers 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 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: Returns:
True, if the connection should be ignored. True, if the connection should be ignored.

View File

@ -7,11 +7,10 @@ from typing import Tuple
import ldap3 import ldap3
import passlib.apache import passlib.apache
from mitmproxy import ctx from mitmproxy import ctx, connection
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import http from mitmproxy import http
from mitmproxy.net.http import status_codes from mitmproxy.net.http import status_codes
from mitmproxy.proxy import context
REALM = "mitmproxy" REALM = "mitmproxy"
@ -48,7 +47,7 @@ class ProxyAuth:
self.singleuser = None self.singleuser = None
self.ldapconn = None self.ldapconn = None
self.ldapserver = 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""" """Contains all connections that are permanently authenticated after an HTTP CONNECT"""
def load(self, loader): def load(self, loader):

View File

@ -3,7 +3,7 @@ from pathlib import Path
from typing import List, Optional, TypedDict, Any from typing import List, Optional, TypedDict, Any
from OpenSSL import SSL 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.net import tls as net_tls
from mitmproxy.options import CONF_BASENAME from mitmproxy.options import CONF_BASENAME
from mitmproxy.proxy import context from mitmproxy.proxy import context
@ -113,8 +113,8 @@ class TlsConfig:
self.create_proxy_server_ssl_conn(tls_start) self.create_proxy_server_ssl_conn(tls_start)
def create_client_proxy_ssl_conn(self, tls_start: tls.TlsStartData) -> None: def create_client_proxy_ssl_conn(self, tls_start: tls.TlsStartData) -> None:
client: context.Client = tls_start.context.client client: connection.Client = tls_start.context.client
server: context.Server = tls_start.context.server server: connection.Server = tls_start.context.server
entry = self.get_cert(tls_start.context) entry = self.get_cert(tls_start.context)
@ -149,8 +149,8 @@ class TlsConfig:
tls_start.ssl_conn.set_accept_state() tls_start.ssl_conn.set_accept_state()
def create_proxy_server_ssl_conn(self, tls_start: tls.TlsStartData) -> None: def create_proxy_server_ssl_conn(self, tls_start: tls.TlsStartData) -> None:
client: context.Client = tls_start.context.client client: connection.Client = tls_start.context.client
server: context.Server = tls_start.context.server server: connection.Server = tls_start.context.server
assert server.address assert server.address
if ctx.options.ssl_insecure: if ctx.options.ssl_insecure:

View File

@ -15,14 +15,15 @@ import blinker
import sortedcontainers import sortedcontainers
import mitmproxy.flow import mitmproxy.flow
from mitmproxy import flowfilter, hooks
from mitmproxy import exceptions
from mitmproxy import command from mitmproxy import command
from mitmproxy import ctx 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 http
from mitmproxy import io
from mitmproxy import tcp from mitmproxy import tcp
from mitmproxy.proxy import context
from mitmproxy.utils import human from mitmproxy.utils import human
@ -133,15 +134,15 @@ class View(collections.abc.Sequence):
self.default_order = OrderRequestStart(self) self.default_order = OrderRequestStart(self)
self.orders = dict( self.orders = dict(
time = OrderRequestStart(self), method = OrderRequestMethod(self), time=OrderRequestStart(self), method=OrderRequestMethod(self),
url = OrderRequestURL(self), size = OrderKeySize(self), url=OrderRequestURL(self), size=OrderKeySize(self),
) )
self.order_key = self.default_order self.order_key = self.default_order
self.order_reversed = False self.order_reversed = False
self.focus_follow = False self.focus_follow = False
self._view = sortedcontainers.SortedListWithKey( self._view = sortedcontainers.SortedListWithKey(
key = self.order_key key=self.order_key
) )
# The sig_view* signals broadcast events that affect the view. That is, # 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: except ValueError as e:
raise exceptions.CommandError("Invalid URL: %s" % e) raise exceptions.CommandError("Invalid URL: %s" % e)
c = context.Client(("", 0), ("", 0), req.timestamp_start - 0.0001) c = connection.Client(("", 0), ("", 0), req.timestamp_start - 0.0001)
s = context.Server((req.host, req.port)) s = connection.Server((req.host, req.port))
f = http.HTTPFlow(c, s) f = http.HTTPFlow(c, s)
f.request = req f.request = req
@ -622,6 +623,7 @@ class Focus:
""" """
Tracks a focus element within a View. Tracks a focus element within a View.
""" """
def __init__(self, v: View) -> None: def __init__(self, v: View) -> None:
self.view = v self.view = v
self._flow: typing.Optional[mitmproxy.flow.Flow] = None 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 typing # noqa
import uuid import uuid
from mitmproxy import controller from mitmproxy import controller, connection
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import stateobject from mitmproxy import stateobject
from mitmproxy import version from mitmproxy import version
from mitmproxy.proxy import context
class Error(stateobject.StateObject): class Error(stateobject.StateObject):
@ -55,18 +54,17 @@ class Error(stateobject.StateObject):
class Flow(stateobject.StateObject): class Flow(stateobject.StateObject):
""" """
A Flow is a collection of objects representing a single transaction. A Flow is a collection of objects representing a single transaction.
This class is usually subclassed for each protocol, e.g. HTTPFlow. This class is usually subclassed for each protocol, e.g. HTTPFlow.
""" """
def __init__( def __init__(
self, self,
type: str, type: str,
client_conn: context.Client, client_conn: connection.Client,
server_conn: context.Server, server_conn: connection.Server,
live: bool=None live: bool = None
) -> None: ) -> None:
self.type = type self.type = type
self.id = str(uuid.uuid4()) self.id = str(uuid.uuid4())
@ -85,8 +83,8 @@ class Flow(stateobject.StateObject):
_stateobject_attributes = dict( _stateobject_attributes = dict(
id=str, id=str,
error=Error, error=Error,
client_conn=context.Client, client_conn=connection.Client,
server_conn=context.Server, server_conn=connection.Server,
type=str, type=str,
intercepted=bool, intercepted=bool,
is_replay=str, is_replay=str,

View File

@ -1,5 +1,4 @@
import re import re
import re
import time import time
import urllib.parse import urllib.parse
from dataclasses import dataclass from dataclasses import dataclass
@ -13,7 +12,7 @@ from typing import Optional
from typing import Tuple from typing import Tuple
from typing import Union from typing import Union
from mitmproxy import flow from mitmproxy import flow, connection
from mitmproxy.coretypes import multidict from mitmproxy.coretypes import multidict
from mitmproxy.coretypes import serializable from mitmproxy.coretypes import serializable
from mitmproxy.net import encoding 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 status_codes
from mitmproxy.net.http import url from mitmproxy.net.http import url
from mitmproxy.net.http.headers import assemble_content_type, parse_content_type 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 human
from mitmproxy.utils import strutils from mitmproxy.utils import strutils
from mitmproxy.utils import typecheck from mitmproxy.utils import typecheck
@ -1117,8 +1115,8 @@ class HTTPFlow(flow.Flow):
object. This might happen, for instance, when a response was received 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. from the server, but there was an error sending it back to the client.
""" """
server_conn: context.Server server_conn: connection.Server
client_conn: context.Client client_conn: connection.Client
intercepted: bool = False intercepted: bool = False
""" Is this flow currently being intercepted? """ """ Is this flow currently being intercepted? """
mode: str mode: str

View File

@ -9,7 +9,7 @@ The counterpart to commands are events.
from typing import Literal, Union, TYPE_CHECKING from typing import Literal, Union, TYPE_CHECKING
import mitmproxy.hooks import mitmproxy.hooks
from mitmproxy.proxy.context import Connection, Server from mitmproxy.connection import Connection, Server
if TYPE_CHECKING: if TYPE_CHECKING:
import mitmproxy.proxy.layer import mitmproxy.proxy.layer

View File

@ -1,394 +1,30 @@
import uuid from typing import List, TYPE_CHECKING
import warnings
from abc import ABCMeta
from enum import Flag
from typing import List, Literal, Optional, Sequence, Tuple, Union, TYPE_CHECKING
import mitmproxy from mitmproxy import connection
from mitmproxy import certs
from mitmproxy.coretypes import serializable
from mitmproxy.net import server_spec
from mitmproxy.options import Options from mitmproxy.options import Options
from mitmproxy.utils import human
if TYPE_CHECKING: if TYPE_CHECKING:
import mitmproxy.proxy.layer 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: class Context:
""" """
The context object provided to each `mitmproxy.proxy.layer.Layer` by its parent layer. The context object provided to each `mitmproxy.proxy.layer.Layer` by its parent layer.
""" """
client: Client client: connection.Client
server: Server server: connection.Server
options: Options options: Options
layers: List["mitmproxy.proxy.layer.Layer"] layers: List["mitmproxy.proxy.layer.Layer"]
def __init__( def __init__(
self, self,
client: Client, client: connection.Client,
options: Options, options: Options,
) -> None: ) -> None:
self.client = client self.client = client
self.options = options self.options = options
self.server = Server(None) self.server = connection.Server(None)
self.layers = [] self.layers = []
def fork(self) -> "Context": def fork(self) -> "Context":

View File

@ -9,7 +9,7 @@ import warnings
from dataclasses import dataclass, is_dataclass from dataclasses import dataclass, is_dataclass
from mitmproxy.proxy import commands from mitmproxy.proxy import commands
from mitmproxy.proxy.context import Connection from mitmproxy.connection import Connection
class Event: class Event:

View File

@ -7,9 +7,10 @@ from abc import abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, List, ClassVar, Deque, NamedTuple, Generator, Any, TypeVar 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 import commands, events
from mitmproxy.proxy.commands import Command, StartHook from mitmproxy.proxy.commands import Command, StartHook
from mitmproxy.proxy.context import Connection, Context from mitmproxy.proxy.context import Context
T = TypeVar('T') T = TypeVar('T')
CommandGenerator = Generator[Command, Any, T] CommandGenerator = Generator[Command, Any, T]
@ -96,8 +97,8 @@ class Layer:
if self._paused: if self._paused:
# did we just receive the reply we were waiting for? # did we just receive the reply we were waiting for?
pause_finished = ( pause_finished = (
isinstance(event, events.CommandCompleted) and isinstance(event, events.CommandCompleted) and
event.command is self._paused.command event.command is self._paused.command
) )
if self.debug is not None: if self.debug is not None:
yield self.__debug(f"{'>>' if pause_finished else '>!'} {event}") 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 typing import Optional, Tuple, Union, Dict, DefaultDict, List
from mitmproxy import flow, http from mitmproxy import flow, http
from mitmproxy.connection import Connection, ConnectionState, Server
from mitmproxy.net import server_spec from mitmproxy.net import server_spec
from mitmproxy.net.http import url from mitmproxy.net.http import url
from mitmproxy.proxy import commands, events, layer, tunnel 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 import tls, websocket, tcp
from mitmproxy.proxy.layers.http import _upstream_proxy from mitmproxy.proxy.layers.http import _upstream_proxy
from mitmproxy.proxy.utils import expect from mitmproxy.proxy.utils import expect
from mitmproxy.utils import human from mitmproxy.utils import human
from ._base import HttpCommand, ReceiveHttp, StreamId, HttpConnection from ._base import HttpCommand, ReceiveHttp, StreamId, HttpConnection
from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \
ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError
@ -21,6 +20,7 @@ from ._hooks import HttpConnectHook, HttpErrorHook, HttpRequestHeadersHook, Http
HttpResponseHook HttpResponseHook
from ._http1 import Http1Client, Http1Server from ._http1 import Http1Client, Http1Server
from ._http2 import Http2Client, Http2Server from ._http2 import Http2Client, Http2Server
from ...context import Context
class HTTPMode(enum.Enum): class HTTPMode(enum.Enum):
@ -55,13 +55,13 @@ class GetHttpConnection(HttpCommand):
def connection_spec_matches(self, connection: Connection) -> bool: def connection_spec_matches(self, connection: Connection) -> bool:
return ( return (
isinstance(connection, Server) isinstance(connection, Server)
and and
self.address == connection.address self.address == connection.address
and and
self.tls == connection.tls self.tls == connection.tls
and and
self.via == connection.via self.via == connection.via
) )
@ -317,9 +317,9 @@ class HttpStream(layer.Layer):
if self.flow.response.status_code == 101: if self.flow.response.status_code == 101:
is_websocket = ( is_websocket = (
self.flow.response.headers.get("upgrade", "").lower() == "websocket" self.flow.response.headers.get("upgrade", "").lower() == "websocket"
and and
self.flow.request.headers.get("Sec-WebSocket-Version", "") == "13" self.flow.request.headers.get("Sec-WebSocket-Version", "") == "13"
) )
if is_websocket and self.context.options.websocket: if is_websocket and self.context.options.websocket:
self.child_layer = websocket.WebsocketLayer(self.context, self.flow) 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]: def check_killed(self, emit_error_hook: bool) -> layer.CommandGenerator[bool]:
killed_by_us = ( 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. # 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. # We peek into the event queue to see if that is the case.
@ -366,18 +366,18 @@ class HttpStream(layer.Layer):
return False return False
def handle_protocol_error( def handle_protocol_error(
self, self,
event: Union[RequestProtocolError, ResponseProtocolError] event: Union[RequestProtocolError, ResponseProtocolError]
) -> layer.CommandGenerator[None]: ) -> layer.CommandGenerator[None]:
is_client_error_but_we_already_talk_upstream = ( is_client_error_but_we_already_talk_upstream = (
isinstance(event, RequestProtocolError) isinstance(event, RequestProtocolError)
and self.client_state in (self.state_stream_request_body, self.state_done) and self.client_state in (self.state_stream_request_body, self.state_done)
and self.server_state != self.state_errored and self.server_state != self.state_errored
) )
need_error_hook = not ( need_error_hook = not (
self.client_state in (self.state_wait_for_request_headers, self.state_errored) self.client_state in (self.state_wait_for_request_headers, self.state_errored)
or or
self.server_state in (self.state_done, self.state_errored) self.server_state in (self.state_done, self.state_errored)
) )
if is_client_error_but_we_already_talk_upstream: if is_client_error_but_we_already_talk_upstream:
@ -579,9 +579,9 @@ class HttpLayer(layer.Layer):
raise AssertionError(f"Unexpected event: {event}") raise AssertionError(f"Unexpected event: {event}")
def event_to_child( def event_to_child(
self, self,
child: Union[layer.Layer, HttpStream], child: Union[layer.Layer, HttpStream],
event: events.Event, event: events.Event,
) -> layer.CommandGenerator[None]: ) -> layer.CommandGenerator[None]:
for command in child.handle_event(event): for command in child.handle_event(event):
assert isinstance(command, commands.Command) assert isinstance(command, commands.Command)
@ -622,13 +622,13 @@ class HttpLayer(layer.Layer):
for connection in self.connections: for connection in self.connections:
# see "tricky multiplexing edge case" in make_http_connection for an explanation # see "tricky multiplexing edge case" in make_http_connection for an explanation
conn_is_pending_or_h2 = ( conn_is_pending_or_h2 = (
connection.alpn == b"h2" connection.alpn == b"h2"
or connection in self.waiting_for_establishment or connection in self.waiting_for_establishment
) )
h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2 h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2
connection_suitable = ( connection_suitable = (
event.connection_spec_matches(connection) event.connection_spec_matches(connection)
and not h2_to_h1 and not h2_to_h1
) )
if connection_suitable: if connection_suitable:
if connection in self.waiting_for_establishment: if connection in self.waiting_for_establishment:
@ -639,9 +639,9 @@ class HttpLayer(layer.Layer):
return return
can_use_context_connection = ( can_use_context_connection = (
self.context.server not in self.connections and self.context.server not in self.connections and
self.context.server.connected and self.context.server.connected and
event.connection_spec_matches(self.context.server) event.connection_spec_matches(self.context.server)
) )
context = self.context.fork() context = self.context.fork()

View File

@ -1,7 +1,8 @@
from dataclasses import dataclass from dataclasses import dataclass
from mitmproxy.proxy import events, layer, commands 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 StreamId = int

View File

@ -9,13 +9,14 @@ from h11._receivebuffer import ReceiveBuffer
from mitmproxy import http, version from mitmproxy import http, version
from mitmproxy.net.http import http1, status_codes from mitmproxy.net.http import http1, status_codes
from mitmproxy.proxy import commands, events, layer 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.layers.http._base import ReceiveHttp, StreamId
from mitmproxy.proxy.utils import expect from mitmproxy.proxy.utils import expect
from mitmproxy.utils import human from mitmproxy.utils import human
from ._base import HttpConnection from ._base import HttpConnection
from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ from ._events import HttpEvent, RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \
ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError ResponseEndOfMessage, ResponseHeaders, ResponseProtocolError
from ...context import Context
TBodyReader = Union[ChunkedReader, Http10Reader, ContentLengthReader] TBodyReader = Union[ChunkedReader, Http10Reader, ContentLengthReader]

View File

@ -13,6 +13,7 @@ import h2.stream
import h2.utilities import h2.utilities
from mitmproxy import http from mitmproxy import http
from mitmproxy.connection import Connection
from mitmproxy.net.http import url, status_codes from mitmproxy.net.http import url, status_codes
from mitmproxy.utils import human from mitmproxy.utils import human
from . import RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \ from . import RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolError, ResponseData, \
@ -20,7 +21,7 @@ from . import RequestData, RequestEndOfMessage, RequestHeaders, RequestProtocolE
from ._base import HttpConnection, HttpEvent, ReceiveHttp from ._base import HttpConnection, HttpEvent, ReceiveHttp
from ._http_h2 import BufferedH2Connection, H2ConnectionLogger from ._http_h2 import BufferedH2Connection, H2ConnectionLogger
from ...commands import CloseConnection, Log, SendData from ...commands import CloseConnection, Log, SendData
from ...context import Connection, Context from ...context import Context
from ...events import ConnectionClosed, DataReceived, Event, Start from ...events import ConnectionClosed, DataReceived, Event, Start
from ...layer import CommandGenerator from ...layer import CommandGenerator
from ...utils import expect from ...utils import expect

View File

@ -3,7 +3,7 @@ from typing import Optional, Tuple
from h11._receivebuffer import ReceiveBuffer from h11._receivebuffer import ReceiveBuffer
from mitmproxy import http from mitmproxy import http, connection
from mitmproxy.net import server_spec from mitmproxy.net import server_spec
from mitmproxy.net.http import http1 from mitmproxy.net.http import http1
from mitmproxy.proxy import commands, context, layer, tunnel from mitmproxy.proxy import commands, context, layer, tunnel
@ -13,13 +13,13 @@ from mitmproxy.utils import human
class HttpUpstreamProxy(tunnel.TunnelLayer): class HttpUpstreamProxy(tunnel.TunnelLayer):
buf: ReceiveBuffer buf: ReceiveBuffer
send_connect: bool send_connect: bool
conn: context.Server conn: connection.Server
tunnel_connection: context.Server tunnel_connection: connection.Server
def __init__( def __init__(
self, self,
ctx: context.Context, ctx: context.Context,
tunnel_conn: context.Server, tunnel_conn: connection.Server,
send_connect: bool send_connect: bool
): ):
super().__init__( super().__init__(

View File

@ -4,7 +4,8 @@ from typing import Optional
from mitmproxy import flow, tcp from mitmproxy import flow, tcp
from mitmproxy.proxy import commands, events, layer from mitmproxy.proxy import commands, events, layer
from mitmproxy.proxy.commands import StartHook 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 from mitmproxy.proxy.utils import expect

View File

@ -4,7 +4,7 @@ from dataclasses import dataclass
from typing import Iterator, Optional, Tuple from typing import Iterator, Optional, Tuple
from OpenSSL import SSL from OpenSSL import SSL
from mitmproxy import certs from mitmproxy import certs, connection
from mitmproxy.net import tls as net_tls from mitmproxy.net import tls as net_tls
from mitmproxy.proxy import commands, events, layer, tunnel from mitmproxy.proxy import commands, events, layer, tunnel
from mitmproxy.proxy import context from mitmproxy.proxy import context
@ -116,7 +116,7 @@ class TlsClienthelloHook(StartHook):
@dataclass @dataclass
class TlsStartData: class TlsStartData:
conn: context.Connection conn: connection.Connection
context: context.Context context: context.Context
ssl_conn: Optional[SSL.Connection] = None ssl_conn: Optional[SSL.Connection] = None
@ -136,7 +136,7 @@ class _TLSLayer(tunnel.TunnelLayer):
tls: SSL.Connection = None tls: SSL.Connection = None
"""The OpenSSL connection object""" """The OpenSSL connection object"""
def __init__(self, context: context.Context, conn: context.Connection): def __init__(self, context: context.Context, conn: connection.Connection):
super().__init__( super().__init__(
context, context,
tunnel_connection=conn, tunnel_connection=conn,
@ -240,7 +240,7 @@ class _TLSLayer(tunnel.TunnelLayer):
events.DataReceived(self.conn, bytes(plaintext)) events.DataReceived(self.conn, bytes(plaintext))
) )
if close: if close:
self.conn.state &= ~context.ConnectionState.CAN_READ self.conn.state &= ~connection.ConnectionState.CAN_READ
if self.debug: if self.debug:
yield commands.Log(f"{self.debug}[tls] close_notify {self.conn}", level="debug") yield commands.Log(f"{self.debug}[tls] close_notify {self.conn}", level="debug")
yield from self.event_to_child( yield from self.event_to_child(
@ -268,7 +268,7 @@ class ServerTLSLayer(_TLSLayer):
""" """
command_to_reply_to: Optional[commands.OpenConnection] = None 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) super().__init__(context, conn or context.server)
def start_handshake(self) -> layer.CommandGenerator[None]: def start_handshake(self) -> layer.CommandGenerator[None]:
@ -373,4 +373,4 @@ class MockTLSLayer(_TLSLayer):
""" """
def __init__(self, ctx: context.Context): 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.extensions
import wsproto.frame_protocol import wsproto.frame_protocol
import wsproto.utilities import wsproto.utilities
from mitmproxy import flow, websocket, http from mitmproxy import flow, websocket, http, connection
from mitmproxy.proxy import commands, events, layer, context from mitmproxy.proxy import commands, events, layer
from mitmproxy.proxy.commands import StartHook from mitmproxy.proxy.commands import StartHook
from mitmproxy.proxy.context import Context from mitmproxy.proxy.context import Context
from mitmproxy.proxy.utils import expect from mitmproxy.proxy.utils import expect
@ -60,10 +60,10 @@ class WebsocketConnection(wsproto.Connection):
- we add a framebuffer for incomplete messages - we add a framebuffer for incomplete messages
- we wrap .send() so that we can directly yield it. - we wrap .send() so that we can directly yield it.
""" """
conn: context.Connection conn: connection.Connection
frame_buf: List[Union[str, bytes]] 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) super(WebsocketConnection, self).__init__(*args, **kwargs)
self.conn = conn self.conn = conn
self.frame_buf = [] self.frame_buf = []

View File

@ -17,9 +17,10 @@ from dataclasses import dataclass
from OpenSSL import SSL from OpenSSL import SSL
from mitmproxy import http, options as moptions from mitmproxy import http, options as moptions
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import commands, events, layer, layers, server_hooks 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.proxy.layers import tls
from mitmproxy.utils import asyncio_utils from mitmproxy.utils import asyncio_utils
from mitmproxy.utils import human from mitmproxy.utils import human

View File

@ -1,6 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from . import commands, context from mitmproxy import connection
from . import commands
@dataclass @dataclass
@ -11,7 +12,7 @@ class ClientConnectedHook(commands.StartHook):
Setting client.error kills the connection. Setting client.error kills the connection.
""" """
client: context.Client client: connection.Client
@dataclass @dataclass
@ -20,13 +21,13 @@ class ClientDisconnectedHook(commands.StartHook):
A client connection has been closed (either by us or the client). A client connection has been closed (either by us or the client).
""" """
blocking = False blocking = False
client: context.Client client: connection.Client
@dataclass @dataclass
class ServerConnectionHookData: class ServerConnectionHookData:
server: context.Server server: connection.Server
client: context.Client client: connection.Client
@dataclass @dataclass

View File

@ -1,6 +1,7 @@
from enum import Enum, auto from enum import Enum, auto
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from mitmproxy import connection
from mitmproxy.proxy import commands, context, events, layer from mitmproxy.proxy import commands, context, events, layer
from mitmproxy.proxy.layer import Layer from mitmproxy.proxy.layer import Layer
@ -18,9 +19,9 @@ class TunnelLayer(layer.Layer):
or TLS. or TLS.
""" """
child_layer: layer.Layer child_layer: layer.Layer
tunnel_connection: context.Connection tunnel_connection: connection.Connection
"""The 'outer' connection which provides the tunnel protocol I/O""" """The 'outer' connection which provides the tunnel protocol I/O"""
conn: context.Connection conn: connection.Connection
"""The 'inner' connection which provides data I/O""" """The 'inner' connection which provides data I/O"""
tunnel_state: TunnelState = TunnelState.INACTIVE tunnel_state: TunnelState = TunnelState.INACTIVE
command_to_reply_to: Optional[commands.OpenConnection] = None command_to_reply_to: Optional[commands.OpenConnection] = None
@ -33,8 +34,8 @@ class TunnelLayer(layer.Layer):
def __init__( def __init__(
self, self,
context: context.Context, context: context.Context,
tunnel_connection: context.Connection, tunnel_connection: connection.Connection,
conn: context.Connection, conn: connection.Connection,
): ):
super().__init__(context) super().__init__(context)
self.tunnel_connection = tunnel_connection self.tunnel_connection = tunnel_connection
@ -47,7 +48,7 @@ class TunnelLayer(layer.Layer):
def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]: def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]:
if isinstance(event, events.Start): 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, # 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. # 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. # 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) done, err = yield from self.receive_handshake_data(event.data)
if done: if done:
if self.conn != self.tunnel_connection: if self.conn != self.tunnel_connection:
self.conn.state = context.ConnectionState.OPEN self.conn.state = connection.ConnectionState.OPEN
if err: if err:
if self.conn != self.tunnel_connection: 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) yield from self.on_handshake_error(err)
if done or err: if done or err:
yield from self._handshake_finished(err) yield from self._handshake_finished(err)
else: else:
yield from self.receive_data(event.data) yield from self.receive_data(event.data)
elif isinstance(event, events.ConnectionClosed): 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: if self.tunnel_state is TunnelState.OPEN:
yield from self.receive_close() yield from self.receive_close()
elif self.tunnel_state is TunnelState.ESTABLISHING: elif self.tunnel_state is TunnelState.ESTABLISHING:
@ -107,9 +108,9 @@ class TunnelLayer(layer.Layer):
elif isinstance(command, commands.CloseConnection): elif isinstance(command, commands.CloseConnection):
if self.conn != self.tunnel_connection: if self.conn != self.tunnel_connection:
if command.half_close: if command.half_close:
self.conn.state &= ~context.ConnectionState.CAN_WRITE self.conn.state &= ~connection.ConnectionState.CAN_WRITE
else: else:
self.conn.state = context.ConnectionState.CLOSED self.conn.state = connection.ConnectionState.CLOSED
yield from self.send_close(command.half_close) yield from self.send_close(command.half_close)
elif isinstance(command, commands.OpenConnection): elif isinstance(command, commands.OpenConnection):
# create our own OpenConnection command object that blocks here. # create our own OpenConnection command object that blocks here.

View File

@ -1,12 +1,12 @@
import uuid import uuid
from mitmproxy import connection
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import flow from mitmproxy import flow
from mitmproxy import http from mitmproxy import http
from mitmproxy import tcp from mitmproxy import tcp
from mitmproxy import websocket from mitmproxy import websocket
from mitmproxy.net.http import status_codes from mitmproxy.net.http import status_codes
from mitmproxy.proxy import context
from mitmproxy.test import tutils from mitmproxy.test import tutils
from wsproto.frame_protocol import Opcode from wsproto.frame_protocol import Opcode
@ -144,8 +144,8 @@ def tdummyflow(client_conn=True, server_conn=True, err=None):
return f return f
def tclient_conn() -> context.Client: def tclient_conn() -> connection.Client:
c = context.Client.from_state(dict( c = connection.Client.from_state(dict(
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
address=("127.0.0.1", 22), address=("127.0.0.1", 22),
mitmcert=None, mitmcert=None,
@ -170,8 +170,8 @@ def tclient_conn() -> context.Client:
return c return c
def tserver_conn() -> context.Server: def tserver_conn() -> connection.Server:
c = context.Server.from_state(dict( c = connection.Server.from_state(dict(
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
address=("address", 22), address=("address", 22),
source_address=("address", 22), source_address=("address", 22),

View File

@ -1,7 +1,7 @@
import pytest import pytest
from mitmproxy import connection
from mitmproxy.addons import block from mitmproxy.addons import block
from mitmproxy.proxy import context
from mitmproxy.test import taddons from mitmproxy.test import taddons
@ -28,7 +28,6 @@ from mitmproxy.test import taddons
(True, False, True, ("2001:4860:4860::8888",)), (True, False, True, ("2001:4860:4860::8888",)),
(True, False, True, (r"2001:4860:4860::8888%scope",)), (True, False, True, (r"2001:4860:4860::8888%scope",)),
# block_private: loopback # block_private: loopback
(False, True, False, ("127.0.0.1",)), (False, True, False, ("127.0.0.1",)),
(False, True, False, ("::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() ar = block.Block()
with taddons.context(ar) as tctx: with taddons.context(ar) as tctx:
tctx.configure(ar, block_global=block_global, block_private=block_private) 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) ar.client_connected(client)
assert bool(client.error) == should_be_killed assert bool(client.error) == should_be_killed

View File

@ -5,7 +5,7 @@ import pytest
from mitmproxy.addons.clientplayback import ClientPlayback, ReplayHandler from mitmproxy.addons.clientplayback import ClientPlayback, ReplayHandler
from mitmproxy.exceptions import CommandError, OptionsError from mitmproxy.exceptions import CommandError, OptionsError
from mitmproxy.proxy.context import Address from mitmproxy.connection import Address
from mitmproxy.test import taddons, tflow from mitmproxy.test import taddons, tflow

View File

@ -2,6 +2,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from mitmproxy import connection
from mitmproxy.addons.next_layer import NextLayer from mitmproxy.addons.next_layer import NextLayer
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import context, layers from mitmproxy.proxy import context, layers
@ -10,7 +11,7 @@ from mitmproxy.test import taddons
@pytest.fixture @pytest.fixture
def tctx(): 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( client_hello_no_extensions = bytes.fromhex(

View File

@ -6,7 +6,7 @@ import pytest
from mitmproxy.addons.proxyserver import Proxyserver from mitmproxy.addons.proxyserver import Proxyserver
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import layers from mitmproxy.proxy import layers
from mitmproxy.proxy.context import Address from mitmproxy.connection import Address
from mitmproxy.test import taddons from mitmproxy.test import taddons

View File

@ -6,7 +6,7 @@ from typing import Union
import pytest import pytest
from OpenSSL import SSL from OpenSSL import SSL
from mitmproxy import certs from mitmproxy import certs, connection
from mitmproxy.addons import tlsconfig from mitmproxy.addons import tlsconfig
from mitmproxy.proxy import context from mitmproxy.proxy import context
from mitmproxy.proxy.layers import tls from mitmproxy.proxy.layers import tls
@ -54,7 +54,7 @@ class TestTlsConfig:
with taddons.context(ta) as tctx: with taddons.context(ta) as tctx:
ta.configure(["confdir"]) 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. # Edge case first: We don't have _any_ idea about the server, so we just return "mitmproxy" as subject.
entry = ta.get_cert(ctx) 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 # only really testing for coverage here, there's no point in mirroring the individual conditions
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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) ch = tls.ClientHelloData(ctx)
ta.tls_clienthello(ch) ta.tls_clienthello(ch)
assert not ch.establish_server_tls_first assert not ch.establish_server_tls_first
@ -113,7 +113,7 @@ class TestTlsConfig:
certs=[tdata.path("mitmproxy/net/data/verificationcerts/trusted-leaf.pem")], certs=[tdata.path("mitmproxy/net/data/verificationcerts/trusted-leaf.pem")],
ciphers_client="ECDHE-ECDSA-AES128-GCM-SHA256", 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) tls_start = tls.TlsStartData(ctx.client, context=ctx)
ta.tls_start(tls_start) ta.tls_start(tls_start)
@ -125,7 +125,7 @@ class TestTlsConfig:
def test_create_proxy_server_ssl_conn_verify_failed(self): def test_create_proxy_server_ssl_conn_verify_failed(self):
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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.alpn_offers = [b"h2"]
ctx.client.cipher_list = ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES128-SHA"] ctx.client.cipher_list = ["TLS_AES_256_GCM_SHA384", "ECDHE-RSA-AES128-SHA"]
ctx.server.address = ("example.mitmproxy.org", 443) ctx.server.address = ("example.mitmproxy.org", 443)
@ -140,7 +140,7 @@ class TestTlsConfig:
def test_create_proxy_server_ssl_conn_verify_ok(self, tdata): def test_create_proxy_server_ssl_conn_verify_ok(self, tdata):
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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) ctx.server.address = ("example.mitmproxy.org", 443)
tctx.configure(ta, ssl_verify_upstream_trusted_ca=tdata.path( tctx.configure(ta, ssl_verify_upstream_trusted_ca=tdata.path(
"mitmproxy/net/data/verificationcerts/trusted-root.crt")) "mitmproxy/net/data/verificationcerts/trusted-root.crt"))
@ -154,7 +154,7 @@ class TestTlsConfig:
def test_create_proxy_server_ssl_conn_insecure(self): def test_create_proxy_server_ssl_conn_insecure(self):
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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) ctx.server.address = ("example.mitmproxy.org", 443)
tctx.configure( tctx.configure(
@ -173,7 +173,7 @@ class TestTlsConfig:
def test_alpn_selection(self): def test_alpn_selection(self):
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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) ctx.server.address = ("example.mitmproxy.org", 443)
tls_start = tls.TlsStartData(ctx.server, context=ctx) tls_start = tls.TlsStartData(ctx.server, context=ctx)
@ -203,7 +203,7 @@ class TestTlsConfig:
def test_client_cert_file(self, tdata, client_certs): def test_client_cert_file(self, tdata, client_certs):
ta = tlsconfig.TlsConfig() ta = tlsconfig.TlsConfig()
with taddons.context(ta) as tctx: 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) ctx.server.address = ("example.mitmproxy.org", 443)
tctx.configure( tctx.configure(
ta, ta,

View File

@ -1,9 +1,11 @@
import collections import collections
import pytest import pytest
from mitmproxy.http import Headers from mitmproxy.http import Headers
from mitmproxy.net.http.headers import parse_content_type, assemble_content_type from mitmproxy.net.http.headers import parse_content_type, assemble_content_type
class TestHeaders: class TestHeaders:
def _2host(self): def _2host(self):
return Headers( return Headers(
@ -79,4 +81,5 @@ def test_assemble_content_type():
p = assemble_content_type p = assemble_content_type
assert p("text", "html", {}) == "text/html" assert p("text", "html", {}) == "text/html"
assert p("text", "html", {"charset": "utf8"}) == "text/html; charset=utf8" 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 import pytest
from hypothesis import settings from hypothesis import settings
from mitmproxy import options from mitmproxy import options, connection
from mitmproxy.addons.core import Core from mitmproxy.addons.core import Core
from mitmproxy.addons.proxyserver import Proxyserver from mitmproxy.addons.proxyserver import Proxyserver
from mitmproxy.addons.termlog import TermLog from mitmproxy.addons.termlog import TermLog
@ -17,7 +17,7 @@ def tctx() -> context.Context:
TermLog().load(opts) TermLog().load(opts)
Core().load(opts) Core().load(opts)
return context.Context( return context.Context(
context.Client( connection.Client(
("client", 1234), ("client", 1234),
("127.0.0.1", 8080), ("127.0.0.1", 8080),
1605699329 1605699329

View File

@ -6,7 +6,7 @@ from mitmproxy.net.server_spec import ServerSpec
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import layer from mitmproxy.proxy import layer
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData, Log 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.events import ConnectionClosed, DataReceived
from mitmproxy.proxy.layers import TCPLayer, http, tls from mitmproxy.proxy.layers import TCPLayer, http, tls
from mitmproxy.proxy.layers.tcp import TcpStartHook 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.flow import Error
from mitmproxy.http import HTTPFlow, Headers, Request from mitmproxy.http import HTTPFlow, Headers, Request
from mitmproxy.net.http import status_codes from mitmproxy.net.http import status_codes
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData 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.events import ConnectionClosed, DataReceived
from mitmproxy.proxy.layers import http from mitmproxy.proxy.layers import http
from mitmproxy.proxy.layers.http._http2 import split_pseudo_headers, Http2Client 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, \ from hypothesis.strategies import binary, booleans, composite, dictionaries, integers, lists, sampled_from, sets, text, \
data data
from mitmproxy import options from mitmproxy import options, connection
from mitmproxy.addons.proxyserver import Proxyserver from mitmproxy.addons.proxyserver import Proxyserver
from mitmproxy.connection import Server
from mitmproxy.http import HTTPFlow from mitmproxy.http import HTTPFlow
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy import context, events from mitmproxy.proxy import context, events
from mitmproxy.proxy.commands import OpenConnection, SendData from mitmproxy.proxy.commands import OpenConnection, SendData
from mitmproxy.proxy.context import Server
from mitmproxy.proxy.events import DataReceived, Start, ConnectionClosed from mitmproxy.proxy.events import DataReceived, Start, ConnectionClosed
from mitmproxy.proxy.layers import http from mitmproxy.proxy.layers import http
from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory 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()))) @given(chunks(mutations(h1_requests())))
def test_fuzz_h1_request(data): 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) layer = http.HttpLayer(tctx, HTTPMode.regular)
for _ in layer.handle_event(Start()): for _ in layer.handle_event(Start()):
@ -113,8 +113,8 @@ def test_fuzz_h1_request(data):
@given(chunks(mutations(h2_responses()))) @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']) @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): def test_fuzz_h1_response(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)
server = Placeholder(context.Server) server = Placeholder(connection.Server)
playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
assert ( assert (
playbook playbook
@ -207,7 +207,7 @@ def h2_frames(draw):
def h2_layer(opts): 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" tctx.client.alpn = b"h2"
layer = http.HttpLayer(tctx, HTTPMode.regular) layer = http.HttpLayer(tctx, HTTPMode.regular)
@ -246,9 +246,9 @@ def test_fuzz_h2_request_mutations(chunks):
def _h2_response(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) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
server = Placeholder(context.Server) server = Placeholder(connection.Server)
assert ( assert (
playbook playbook
>> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") >> 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. 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) playbook, cff = start_h2_client(tctx)
flow = Placeholder(HTTPFlow) flow = Placeholder(HTTPFlow)
server = Placeholder(Server) server = Placeholder(Server)

View File

@ -5,9 +5,10 @@ import h2.connection
import h2.events import h2.events
from mitmproxy.http import HTTPFlow from mitmproxy.http import HTTPFlow
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData 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.events import DataReceived
from mitmproxy.proxy.layers import http from mitmproxy.proxy.layers import http
from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory

View File

@ -3,9 +3,10 @@ import copy
import pytest import pytest
from mitmproxy import platform from mitmproxy import platform
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData, GetSocket, Log 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.events import DataReceived, ConnectionClosed
from mitmproxy.proxy.layer import NextLayer, NextLayerHook from mitmproxy.proxy.layer import NextLayer, NextLayerHook
from mitmproxy.proxy.layers import http, modes, tcp, tls from mitmproxy.proxy.layers import http, modes, tcp, tls

View File

@ -1,7 +1,7 @@
import pytest import pytest
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData 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.events import ConnectionClosed, DataReceived
from mitmproxy.proxy.layers import tcp from mitmproxy.proxy.layers import tcp
from mitmproxy.tcp import TCPFlow from mitmproxy.tcp import TCPFlow

View File

@ -4,8 +4,9 @@ import typing
import pytest import pytest
from OpenSSL import SSL 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 import commands, context, events, layer
from mitmproxy.proxy.context import ConnectionState
from mitmproxy.proxy.layers import tls from mitmproxy.proxy.layers import tls
from mitmproxy.utils import data from mitmproxy.utils import data
from test.mitmproxy.proxy import tutils from test.mitmproxy.proxy import tutils
@ -121,7 +122,7 @@ class SSLTest:
return self.obj.do_handshake() 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") tssl.obj.write(b"Hello World")
data = tutils.Placeholder(bytes) data = tutils.Placeholder(bytes)
assert ( assert (
@ -145,7 +146,7 @@ class TlsEchoLayer(tutils.EchoLayer):
yield from super()._handle_event(event) 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) data = tutils.Placeholder(bytes)
assert ( assert (
playbook playbook
@ -383,7 +384,7 @@ class TestClientTLS:
# Echo # Echo
_test_echo(playbook, tssl_client, tctx.client) _test_echo(playbook, tssl_client, tctx.client)
other_server = context.Server(None) other_server = Server(None)
assert ( assert (
playbook playbook
>> events.DataReceived(other_server, b"Plaintext") >> events.DataReceived(other_server, b"Plaintext")

View File

@ -8,7 +8,7 @@ import wsproto.events
from mitmproxy.http import HTTPFlow, Request, Response from mitmproxy.http import HTTPFlow, Request, Response
from mitmproxy.proxy.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy.commands import SendData, CloseConnection, Log 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.events import DataReceived, ConnectionClosed
from mitmproxy.proxy.layers import http, websocket from mitmproxy.proxy.layers import http, websocket
from mitmproxy.websocket import WebSocketFlow from mitmproxy.websocket import WebSocketFlow

View File

@ -2,13 +2,14 @@ from dataclasses import dataclass
import pytest import pytest
from mitmproxy import connection
from mitmproxy.hooks import all_hooks from mitmproxy.hooks import all_hooks
from mitmproxy.proxy import commands, context from mitmproxy.proxy import commands
@pytest.fixture @pytest.fixture
def tconn() -> context.Server: def tconn() -> connection.Server:
return context.Server(None) return connection.Server(None)
def test_dataclasses(tconn): def test_dataclasses(tconn):

View File

@ -1,10 +1,11 @@
from mitmproxy import connection
from mitmproxy.proxy import context from mitmproxy.proxy import context
from mitmproxy.test import tflow, taddons from mitmproxy.test import tflow, taddons
class TestConnection: class TestConnection:
def test_basic(self): def test_basic(self):
c = context.Client( c = connection.Client(
("127.0.0.1", 52314), ("127.0.0.1", 52314),
("127.0.0.1", 8080), ("127.0.0.1", 8080),
1607780791 1607780791
@ -13,7 +14,7 @@ class TestConnection:
c.timestamp_tls_setup = 1607780792 c.timestamp_tls_setup = 1607780792
assert c.tls_established assert c.tls_established
assert c.connected assert c.connected
c.state = context.ConnectionState.CAN_WRITE c.state = connection.ConnectionState.CAN_WRITE
assert not c.connected assert not c.connected
def test_eq(self): def test_eq(self):
@ -30,7 +31,7 @@ class TestConnection:
class TestClient: class TestClient:
def test_basic(self): def test_basic(self):
c = context.Client( c = connection.Client(
("127.0.0.1", 52314), ("127.0.0.1", 52314),
("127.0.0.1", 8080), ("127.0.0.1", 8080),
1607780791 1607780791
@ -44,7 +45,7 @@ class TestClient:
def test_state(self): def test_state(self):
c = tflow.tclient_conn() 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() c2 = tflow.tclient_conn()
assert c != c2 assert c != c2
@ -61,7 +62,7 @@ class TestClient:
class TestServer: class TestServer:
def test_basic(self): def test_basic(self):
s = context.Server(("address", 22)) s = connection.Server(("address", 22))
assert repr(s) assert repr(s)
assert str(s) assert str(s)
s.timestamp_tls_setup = 1607780791 s.timestamp_tls_setup = 1607780791

View File

@ -2,12 +2,13 @@ from unittest.mock import Mock
import pytest import pytest
from mitmproxy.proxy import events, context, commands from mitmproxy import connection
from mitmproxy.proxy import events, commands
@pytest.fixture @pytest.fixture
def tconn() -> context.Server: def tconn() -> connection.Server:
return context.Server(None) return connection.Server(None)
def test_dataclasses(tconn): def test_dataclasses(tconn):

View File

@ -4,7 +4,8 @@ import pytest
from mitmproxy.proxy import tunnel, layer from mitmproxy.proxy import tunnel, layer
from mitmproxy.proxy.commands import SendData, Log, CloseConnection, OpenConnection 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 mitmproxy.proxy.events import Event, DataReceived, Start, ConnectionClosed
from test.mitmproxy.proxy.tutils import Playbook, reply 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 commands, context, layer
from mitmproxy.proxy import events 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.events import command_reply_subclasses
from mitmproxy.proxy.layer import Layer from mitmproxy.proxy.layer import Layer