streamline event/hook/command naming conventions

This commit is contained in:
Maximilian Hils 2021-01-05 08:17:40 +01:00
parent 90df4168f8
commit 6c0e4f1cb7
35 changed files with 171 additions and 170 deletions

View File

@ -4,14 +4,14 @@ import textwrap
from typing import List, Type from typing import List, Type
import mitmproxy.addons.next_layer # noqa import mitmproxy.addons.next_layer # noqa
from mitmproxy import events, log, addonmanager from mitmproxy import event_hooks, log, addonmanager
from mitmproxy.proxy import server_hooks, layer from mitmproxy.proxy import server_hooks, layer
from mitmproxy.proxy.layers import http, tcp, tls, websocket from mitmproxy.proxy.layers import http, tcp, tls, websocket
known = set() known = set()
def category(name: str, hooks: List[Type[events.MitmproxyEvent]]) -> None: def category(name: str, hooks: List[Type[event_hooks.EventHook]]) -> None:
print(f"### {name} Events") print(f"### {name} Events")
print("```python") print("```python")
@ -65,10 +65,10 @@ def category(name: str, hooks: List[Type[events.MitmproxyEvent]]) -> None:
category( category(
"Lifecycle", "Lifecycle",
[ [
addonmanager.LoadEvent, addonmanager.LoadEventHook,
events.RunningEvent, event_hooks.RunningEventHook,
events.ConfigureEvent, event_hooks.ConfigureEventHook,
events.DoneEvent, event_hooks.DoneEventHook,
] ]
) )
@ -127,12 +127,12 @@ category(
"Advanced Lifecycle", "Advanced Lifecycle",
[ [
layer.NextLayerHook, layer.NextLayerHook,
events.UpdateEvent, event_hooks.UpdateEventHook,
log.AddLogEvent, log.AddLogEventHook,
] ]
) )
not_documented = set(events.all_events.keys()) - known not_documented = set(event_hooks.all_events.keys()) - known
if not_documented: if not_documented:
raise RuntimeError(f"Not documented: {not_documented}") raise RuntimeError(f"Not documented: {not_documented}")

View File

@ -1,5 +1,5 @@
--- ---
title: "Events" title: "Event Hooks"
menu: menu:
addons: addons:
weight: 2 weight: 2

View File

@ -7,7 +7,7 @@ import typing
from dataclasses import dataclass from dataclasses import dataclass
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import events from mitmproxy import event_hooks
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import flow from mitmproxy import flow
from . import ctx from . import ctx
@ -112,7 +112,7 @@ def traverse(chain):
@dataclass @dataclass
class LoadEvent(events.MitmproxyEvent): class LoadEventHook(event_hooks.EventHook):
""" """
Called when an addon is first loaded. This event receives a Loader Called when an addon is first loaded. This event receives a Loader
object, which contains methods for adding options and commands. This object, which contains methods for adding options and commands. This
@ -129,14 +129,14 @@ class AddonManager:
master.options.changed.connect(self._configure_all) master.options.changed.connect(self._configure_all)
def _configure_all(self, options, updated): def _configure_all(self, options, updated):
self.trigger(events.ConfigureEvent(updated)) self.trigger(event_hooks.ConfigureEventHook(updated))
def clear(self): def clear(self):
""" """
Remove all addons. Remove all addons.
""" """
for a in self.chain: for a in self.chain:
self.invoke_addon(a, events.DoneEvent()) self.invoke_addon(a, event_hooks.DoneEventHook())
self.lookup = {} self.lookup = {}
self.chain = [] self.chain = []
@ -176,7 +176,7 @@ class AddonManager:
"An addon called '%s' already exists." % name "An addon called '%s' already exists." % name
) )
l = Loader(self.master) l = Loader(self.master)
self.invoke_addon(addon, LoadEvent(l)) self.invoke_addon(addon, LoadEventHook(l))
for a in traverse([addon]): for a in traverse([addon]):
name = _get_name(a) name = _get_name(a)
self.lookup[name] = a self.lookup[name] = a
@ -207,7 +207,7 @@ class AddonManager:
raise exceptions.AddonManagerError("No such addon: %s" % n) raise exceptions.AddonManagerError("No such addon: %s" % n)
self.chain = [i for i in self.chain if i is not a] self.chain = [i for i in self.chain if i is not a]
del self.lookup[_get_name(a)] del self.lookup[_get_name(a)]
self.invoke_addon(addon, events.DoneEvent()) self.invoke_addon(addon, event_hooks.DoneEventHook())
def __len__(self): def __len__(self):
return len(self.chain) return len(self.chain)
@ -219,7 +219,7 @@ class AddonManager:
name = _get_name(item) name = _get_name(item)
return name in self.lookup return name in self.lookup
async def handle_lifecycle(self, event: events.MitmproxyEvent): async def handle_lifecycle(self, event: event_hooks.EventHook):
""" """
Handle a lifecycle event. Handle a lifecycle event.
""" """
@ -245,13 +245,13 @@ class AddonManager:
message.reply.mark_reset() message.reply.mark_reset()
if isinstance(message, flow.Flow): if isinstance(message, flow.Flow):
self.trigger(events.UpdateEvent([message])) self.trigger(event_hooks.UpdateEventHook([message]))
def invoke_addon(self, addon, event: events.MitmproxyEvent): def invoke_addon(self, addon, event: event_hooks.EventHook):
""" """
Invoke an event on an addon and all its children. Invoke an event on an addon and all its children.
""" """
assert isinstance(event, events.MitmproxyEvent) assert isinstance(event, event_hooks.EventHook)
for a in traverse([addon]): for a in traverse([addon]):
func = getattr(a, event.name, None) func = getattr(a, event.name, None)
if func: if func:
@ -268,7 +268,7 @@ class AddonManager:
f"Addon handler {event.name} ({a}) not callable" f"Addon handler {event.name} ({a}) not callable"
) )
def trigger(self, event: events.MitmproxyEvent): def trigger(self, event: event_hooks.EventHook):
""" """
Trigger an event across all addons. Trigger an event across all addons.
""" """

View File

@ -11,7 +11,7 @@ from mitmproxy import flow
from mitmproxy import http from mitmproxy import http
from mitmproxy import io from mitmproxy import io
from mitmproxy.addons.proxyserver import AsyncReply from mitmproxy.addons.proxyserver import AsyncReply
from mitmproxy.events import UpdateEvent from mitmproxy.event_hooks import UpdateEventHook
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.layers.http import HTTPMode from mitmproxy.proxy.layers.http import HTTPMode
@ -85,7 +85,7 @@ class ReplayHandler(server.ConnectionHandler):
def log(self, message: str, level: str = "info") -> None: def log(self, message: str, level: str = "info") -> None:
ctx.log(f"[replay] {message}", level) ctx.log(f"[replay] {message}", level)
async def handle_hook(self, hook: commands.Hook) -> None: async def handle_hook(self, hook: commands.StartHook) -> None:
data, = hook.args() data, = hook.args()
data.reply = AsyncReply(data) data.reply = AsyncReply(data)
await ctx.master.addons.handle_lifecycle(hook) await ctx.master.addons.handle_lifecycle(hook)
@ -185,7 +185,7 @@ class ClientPlayback:
f.revert() f.revert()
updated.append(f) updated.append(f)
ctx.master.addons.trigger(UpdateEvent(updated)) ctx.master.addons.trigger(UpdateEventHook(updated))
ctx.log.alert("Client replay queue cleared.") ctx.log.alert("Client replay queue cleared.")
@command.command("replay.client") @command.command("replay.client")
@ -209,7 +209,7 @@ class ClientPlayback:
http_flow.error = None http_flow.error = None
self.queue.put_nowait(http_flow) self.queue.put_nowait(http_flow)
updated.append(http_flow) updated.append(http_flow)
ctx.master.addons.trigger(UpdateEvent(updated)) ctx.master.addons.trigger(UpdateEventHook(updated))
@command.command("replay.client.file") @command.command("replay.client.file")
def load_file(self, path: mitmproxy.types.Path) -> None: def load_file(self, path: mitmproxy.types.Path) -> None:

View File

@ -3,7 +3,7 @@ import typing
import os import os
from mitmproxy.utils import human from mitmproxy.utils import human
from mitmproxy import ctx, events from mitmproxy import ctx, event_hooks
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import command from mitmproxy import command
from mitmproxy import flow from mitmproxy import flow
@ -99,7 +99,7 @@ class Core:
intercepted = [i for i in flows if i.intercepted] intercepted = [i for i in flows if i.intercepted]
for f in intercepted: for f in intercepted:
f.resume() f.resume()
ctx.master.addons.trigger(events.UpdateEvent(intercepted)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(intercepted))
# FIXME: this will become view.mark later # FIXME: this will become view.mark later
@command.command("flow.mark") @command.command("flow.mark")
@ -112,7 +112,7 @@ class Core:
if i.marked != boolean: if i.marked != boolean:
i.marked = boolean i.marked = boolean
updated.append(i) updated.append(i)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
# FIXME: this will become view.mark.toggle later # FIXME: this will become view.mark.toggle later
@command.command("flow.mark.toggle") @command.command("flow.mark.toggle")
@ -122,7 +122,7 @@ class Core:
""" """
for i in flows: for i in flows:
i.marked = not i.marked i.marked = not i.marked
ctx.master.addons.trigger(events.UpdateEvent(flows)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(flows))
@command.command("flow.kill") @command.command("flow.kill")
def kill(self, flows: typing.Sequence[flow.Flow]) -> None: def kill(self, flows: typing.Sequence[flow.Flow]) -> None:
@ -135,7 +135,7 @@ class Core:
f.kill() f.kill()
updated.append(f) updated.append(f)
ctx.log.alert("Killed %s flows." % len(updated)) ctx.log.alert("Killed %s flows." % len(updated))
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
# FIXME: this will become view.revert later # FIXME: this will become view.revert later
@command.command("flow.revert") @command.command("flow.revert")
@ -149,7 +149,7 @@ class Core:
f.revert() f.revert()
updated.append(f) updated.append(f)
ctx.log.alert("Reverted %s flows." % len(updated)) ctx.log.alert("Reverted %s flows." % len(updated))
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
@command.command("flow.set.options") @command.command("flow.set.options")
def flow_set_options(self) -> typing.Sequence[str]: def flow_set_options(self) -> typing.Sequence[str]:
@ -218,7 +218,7 @@ class Core:
if rupdate or supdate: if rupdate or supdate:
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
ctx.log.alert("Set {} on {} flows.".format(attr, len(updated))) ctx.log.alert("Set {} on {} flows.".format(attr, len(updated)))
@command.command("flow.decode") @command.command("flow.decode")
@ -233,7 +233,7 @@ class Core:
f.backup() f.backup()
p.decode() p.decode()
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
ctx.log.alert("Decoded %s flows." % len(updated)) ctx.log.alert("Decoded %s flows." % len(updated))
@command.command("flow.encode.toggle") @command.command("flow.encode.toggle")
@ -252,7 +252,7 @@ class Core:
else: else:
p.decode() p.decode()
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
ctx.log.alert("Toggled encoding on %s flows." % len(updated)) ctx.log.alert("Toggled encoding on %s flows." % len(updated))
@command.command("flow.encode") @command.command("flow.encode")
@ -275,7 +275,7 @@ class Core:
f.backup() f.backup()
p.encode(encoding) p.encode(encoding)
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
ctx.log.alert("Encoded %s flows." % len(updated)) ctx.log.alert("Encoded %s flows." % len(updated))
@command.command("flow.encode.options") @command.command("flow.encode.options")

View File

@ -40,7 +40,7 @@ class ProxyConnectionHandler(server.StreamConnectionHandler):
super().__init__(r, w, options) super().__init__(r, w, options)
self.log_prefix = f"{human.format_address(self.client.peername)}: " self.log_prefix = f"{human.format_address(self.client.peername)}: "
async def handle_hook(self, hook: commands.Hook) -> None: async def handle_hook(self, hook: commands.StartHook) -> None:
with self.timeout_watchdog.disarm(): with self.timeout_watchdog.disarm():
# We currently only support single-argument hooks. # We currently only support single-argument hooks.
data, = hook.args() data, = hook.args()
@ -53,7 +53,7 @@ class ProxyConnectionHandler(server.StreamConnectionHandler):
x = log.LogEntry(self.log_prefix + message, level) x = log.LogEntry(self.log_prefix + message, level)
x.reply = controller.DummyReply() # type: ignore x.reply = controller.DummyReply() # type: ignore
asyncio_utils.create_task( asyncio_utils.create_task(
self.master.addons.handle_lifecycle(log.AddLogEvent(x)), self.master.addons.handle_lifecycle(log.AddLogEventHook(x)),
name="ProxyConnectionHandler.log" name="ProxyConnectionHandler.log"
) )

View File

@ -7,7 +7,7 @@ import types
import typing import typing
import traceback import traceback
from mitmproxy import addonmanager, events from mitmproxy import addonmanager, event_hooks
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import flow from mitmproxy import flow
from mitmproxy import command from mitmproxy import command
@ -104,11 +104,11 @@ class Script:
if self.ns: if self.ns:
# We're already running, so we have to explicitly register and # We're already running, so we have to explicitly register and
# configure the addon # configure the addon
ctx.master.addons.invoke_addon(self.ns, events.RunningEvent()) ctx.master.addons.invoke_addon(self.ns, event_hooks.RunningEventHook())
try: try:
ctx.master.addons.invoke_addon( ctx.master.addons.invoke_addon(
self.ns, self.ns,
events.ConfigureEvent(ctx.options.keys()) event_hooks.ConfigureEventHook(ctx.options.keys())
) )
except exceptions.OptionsError as e: except exceptions.OptionsError as e:
script_error_handler(self.fullpath, e, msg=str(e)) script_error_handler(self.fullpath, e, msg=str(e))
@ -160,10 +160,10 @@ class ScriptLoader:
mod = load_script(path) mod = load_script(path)
if mod: if mod:
with addonmanager.safecall(): with addonmanager.safecall():
ctx.master.addons.invoke_addon(mod, events.RunningEvent()) ctx.master.addons.invoke_addon(mod, event_hooks.RunningEventHook())
ctx.master.addons.invoke_addon( ctx.master.addons.invoke_addon(
mod, mod,
events.ConfigureEvent(ctx.options.keys()), event_hooks.ConfigureEventHook(ctx.options.keys()),
) )
for f in flows: for f in flows:
for evt in eventsequence.iterate(f): for evt in eventsequence.iterate(f):
@ -208,4 +208,4 @@ class ScriptLoader:
if self.is_running: if self.is_running:
# If we're already running, we configure and tell the addon # If we're already running, we configure and tell the addon
# we're up and running. # we're up and running.
ctx.master.addons.invoke_addon(s, events.RunningEvent()) ctx.master.addons.invoke_addon(s, event_hooks.RunningEventHook())

View File

@ -3,7 +3,7 @@ import typing
import urllib import urllib
import mitmproxy.types import mitmproxy.types
from mitmproxy import command, events from mitmproxy import command, event_hooks
from mitmproxy import ctx, http from mitmproxy import ctx, http
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import flow from mitmproxy import flow
@ -89,7 +89,7 @@ class ServerPlayback:
if isinstance(f, http.HTTPFlow): if isinstance(f, http.HTTPFlow):
lst = self.flowmap.setdefault(self._hash(f), []) lst = self.flowmap.setdefault(self._hash(f), [])
lst.append(f) lst.append(f)
ctx.master.addons.trigger(events.UpdateEvent([])) ctx.master.addons.trigger(event_hooks.UpdateEventHook([]))
@command.command("replay.server.file") @command.command("replay.server.file")
def load_file(self, path: mitmproxy.types.Path) -> None: def load_file(self, path: mitmproxy.types.Path) -> None:
@ -105,7 +105,7 @@ class ServerPlayback:
Stop server replay. Stop server replay.
""" """
self.flowmap = {} self.flowmap = {}
ctx.master.addons.trigger(events.UpdateEvent([])) ctx.master.addons.trigger(event_hooks.UpdateEventHook([]))
@command.command("replay.server.count") @command.command("replay.server.count")
def count(self) -> int: def count(self) -> int:

View File

@ -15,7 +15,7 @@ import blinker
import sortedcontainers import sortedcontainers
import mitmproxy.flow import mitmproxy.flow
from mitmproxy import flowfilter, events from mitmproxy import flowfilter, event_hooks
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import command from mitmproxy import command
from mitmproxy import ctx from mitmproxy import ctx
@ -381,7 +381,7 @@ class View(collections.abc.Sequence):
current = self.settings[f].get("key", "false") current = self.settings[f].get("key", "false")
self.settings[f][key] = "false" if current == "true" else "true" self.settings[f][key] = "false" if current == "true" else "true"
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
@command.command("view.settings.setval") @command.command("view.settings.setval")
def setvalue( def setvalue(
@ -396,7 +396,7 @@ class View(collections.abc.Sequence):
for f in flows: for f in flows:
self.settings[f][key] = value self.settings[f][key] = value
updated.append(f) updated.append(f)
ctx.master.addons.trigger(events.UpdateEvent(updated)) ctx.master.addons.trigger(event_hooks.UpdateEventHook(updated))
# Flows # Flows
@command.command("view.flows.duplicate") @command.command("view.flows.duplicate")

View File

@ -9,7 +9,7 @@ if TYPE_CHECKING:
import mitmproxy.log import mitmproxy.log
class MitmproxyEvent: class EventHook:
name: ClassVar[str] name: ClassVar[str]
def args(self) -> List[Any]: def args(self) -> List[Any]:
@ -19,7 +19,7 @@ class MitmproxyEvent:
return args return args
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if cls is MitmproxyEvent: if cls is EventHook:
raise TypeError("MitmproxyEvent may not be instantiated directly.") raise TypeError("MitmproxyEvent may not be instantiated directly.")
if not is_dataclass(cls): if not is_dataclass(cls):
raise TypeError("Subclass is not a dataclass.") raise TypeError("Subclass is not a dataclass.")
@ -42,11 +42,11 @@ class MitmproxyEvent:
cls.__eq__ = object.__eq__ cls.__eq__ = object.__eq__
all_events: Dict[str, Type[MitmproxyEvent]] = {} all_events: Dict[str, Type[EventHook]] = {}
@dataclass @dataclass
class ConfigureEvent(MitmproxyEvent): class ConfigureEventHook(EventHook):
""" """
Called when configuration changes. The updated argument is a Called when configuration changes. The updated argument is a
set-like object containing the keys of all changed options. This set-like object containing the keys of all changed options. This
@ -56,7 +56,7 @@ class ConfigureEvent(MitmproxyEvent):
@dataclass @dataclass
class DoneEvent(MitmproxyEvent): class DoneEventHook(EventHook):
""" """
Called when the addon shuts down, either by being removed from Called when the addon shuts down, either by being removed from
the mitmproxy instance, or when mitmproxy itself shuts down. On the mitmproxy instance, or when mitmproxy itself shuts down. On
@ -68,7 +68,7 @@ class DoneEvent(MitmproxyEvent):
@dataclass @dataclass
class RunningEvent(MitmproxyEvent): class RunningEventHook(EventHook):
""" """
Called when the proxy is completely up and running. At this point, Called when the proxy is completely up and running. At this point,
you can expect the proxy to be bound to a port, and all addons to be you can expect the proxy to be bound to a port, and all addons to be
@ -77,7 +77,7 @@ class RunningEvent(MitmproxyEvent):
@dataclass @dataclass
class UpdateEvent(MitmproxyEvent): class UpdateEventHook(EventHook):
""" """
Update is called when one or more flow objects have been modified, Update is called when one or more flow objects have been modified,
usually from a different addon. usually from a different addon.

View File

@ -1,14 +1,14 @@
from typing import Iterator, Any, Dict, Type, Callable from typing import Iterator, Any, Dict, Type, Callable
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import events from mitmproxy import event_hooks
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.proxy import layers from mitmproxy.proxy import layers
TEventGenerator = Iterator[events.MitmproxyEvent] TEventGenerator = Iterator[event_hooks.EventHook]
def _iterate_http(f: http.HTTPFlow) -> TEventGenerator: def _iterate_http(f: http.HTTPFlow) -> TEventGenerator:

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
from dataclasses import dataclass from dataclasses import dataclass
from mitmproxy import events from mitmproxy import event_hooks
class LogEntry: class LogEntry:
@ -60,12 +60,12 @@ class Log:
def __call__(self, text, level="info"): def __call__(self, text, level="info"):
asyncio.get_event_loop().call_soon( asyncio.get_event_loop().call_soon(
self.master.addons.trigger, AddLogEvent(LogEntry(text, level)), self.master.addons.trigger, AddLogEventHook(LogEntry(text, level)),
) )
@dataclass @dataclass
class AddLogEvent(events.MitmproxyEvent): class AddLogEventHook(event_hooks.EventHook):
""" """
Called whenever a new log entry is created through the mitmproxy Called whenever a new log entry is created through the mitmproxy
context. Be careful not to log from this event, which will cause an context. Be careful not to log from this event, which will cause an

View File

@ -4,7 +4,7 @@ import sys
import threading import threading
import traceback import traceback
from mitmproxy import addonmanager, events from mitmproxy import addonmanager, event_hooks
from mitmproxy import command from mitmproxy import command
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import eventsequence from mitmproxy import eventsequence
@ -45,7 +45,7 @@ class Master:
self.should_exit.clear() self.should_exit.clear()
async def running(self): async def running(self):
self.addons.trigger(events.RunningEvent()) self.addons.trigger(event_hooks.RunningEventHook())
def run_loop(self, loop): def run_loop(self, loop):
self.start() self.start()
@ -71,7 +71,7 @@ class Master:
print("Please lodge a bug report at:", file=sys.stderr) print("Please lodge a bug report at:", file=sys.stderr)
print("\thttps://github.com/mitmproxy/mitmproxy", file=sys.stderr) print("\thttps://github.com/mitmproxy/mitmproxy", file=sys.stderr)
self.addons.trigger(events.DoneEvent()) self.addons.trigger(event_hooks.DoneEventHook())
def run(self): def run(self):
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()

View File

@ -8,7 +8,7 @@ The counterpart to commands are events.
""" """
from typing import Literal, Union, TYPE_CHECKING from typing import Literal, Union, TYPE_CHECKING
import mitmproxy.events import mitmproxy.event_hooks
from mitmproxy.proxy.context import Connection, Server from mitmproxy.proxy.context import Connection, Server
if TYPE_CHECKING: if TYPE_CHECKING:
@ -86,15 +86,16 @@ class CloseConnection(ConnectionCommand):
self.half_close = half_close self.half_close = half_close
class Hook(Command, mitmproxy.events.MitmproxyEvent): class StartHook(Command, mitmproxy.event_hooks.EventHook):
""" """
Callback to the master (like ".ask()") Start an event hook in the mitmproxy core.
This triggers a particular function (derived from the class name) in all addons.
""" """
blocking = True blocking = True
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if cls is Hook: if cls is StartHook:
raise TypeError("Hook may not be instantiated directly.") raise TypeError("StartHook may not be instantiated directly.")
return super().__new__(cls, *args, **kwargs) return super().__new__(cls, *args, **kwargs)

View File

@ -55,7 +55,7 @@ class ConnectionClosed(ConnectionEvent):
pass pass
class CommandReply(Event): class CommandCompleted(Event):
""" """
Emitted when a command has been finished, e.g. Emitted when a command has been finished, e.g.
when the master has replied or when we have established a server connection. when the master has replied or when we have established a server connection.
@ -64,8 +64,8 @@ class CommandReply(Event):
reply: typing.Any reply: typing.Any
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if cls is CommandReply: if cls is CommandCompleted:
raise TypeError("CommandReply may not be instantiated directly.") raise TypeError("CommandCompleted may not be instantiated directly.")
assert is_dataclass(cls) assert is_dataclass(cls)
return super().__new__(cls) return super().__new__(cls)
@ -85,23 +85,23 @@ class CommandReply(Event):
return f"Reply({repr(self.command)})" return f"Reply({repr(self.command)})"
command_reply_subclasses: typing.Dict[commands.Command, typing.Type[CommandReply]] = {} command_reply_subclasses: typing.Dict[commands.Command, typing.Type[CommandCompleted]] = {}
@dataclass(repr=False) @dataclass(repr=False)
class OpenConnectionReply(CommandReply): class OpenConnectionCompleted(CommandCompleted):
command: commands.OpenConnection command: commands.OpenConnection
reply: typing.Optional[str] reply: typing.Optional[str]
"""error message""" """error message"""
@dataclass(repr=False) @dataclass(repr=False)
class HookReply(CommandReply): class HookCompleted(CommandCompleted):
command: commands.Hook command: commands.StartHook
reply: None = None reply: None = None
@dataclass(repr=False) @dataclass(repr=False)
class GetSocketReply(CommandReply): class GetSocketCompleted(CommandCompleted):
command: commands.GetSocket command: commands.GetSocket
reply: socket.socket reply: socket.socket

View File

@ -8,7 +8,7 @@ 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.proxy import commands, events from mitmproxy.proxy import commands, events
from mitmproxy.proxy.commands import Command, Hook from mitmproxy.proxy.commands import Command, StartHook
from mitmproxy.proxy.context import Connection, Context from mitmproxy.proxy.context import Connection, Context
T = TypeVar('T') T = TypeVar('T')
@ -35,8 +35,8 @@ class Layer:
Most layers only implement ._handle_event, which is called by the default implementation of .handle_event. Most layers only implement ._handle_event, which is called by the default implementation of .handle_event.
The default implementation allows layers to emulate blocking code: The default implementation allows layers to emulate blocking code:
When ._handle_event yields a command that has its blocking attribute set to True, .handle_event pauses When ._handle_event yields a command that has its blocking attribute set to True, .handle_event pauses
the execution of ._handle_event and waits until it is called with the corresponding CommandReply. All events the execution of ._handle_event and waits until it is called with the corresponding CommandCompleted event. All
encountered in the meantime are buffered and replayed after execution is resumed. events encountered in the meantime are buffered and replayed after execution is resumed.
The result is code that looks like blocking code, but is not blocking: The result is code that looks like blocking code, but is not blocking:
@ -96,13 +96,13 @@ 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.CommandReply) 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}")
if pause_finished: if pause_finished:
assert isinstance(event, events.CommandReply) assert isinstance(event, events.CommandCompleted)
yield from self.__continue(event) yield from self.__continue(event)
else: else:
self._paused_event_queue.append(event) self._paused_event_queue.append(event)
@ -142,7 +142,7 @@ class Layer:
except StopIteration: except StopIteration:
return return
def __continue(self, event: events.CommandReply): def __continue(self, event: events.CommandCompleted):
"""continue processing events after being paused""" """continue processing events after being paused"""
assert self._paused is not None assert self._paused is not None
command_generator = self._paused.generator command_generator = self._paused.generator
@ -241,7 +241,7 @@ class NextLayer(Layer):
@dataclass @dataclass
class NextLayerHook(Hook): class NextLayerHook(StartHook):
""" """
Network layers are being switched. You may change which layer will be used by setting data.layer. Network layers are being switched. You may change which layer will be used by setting data.layer.

View File

@ -66,7 +66,7 @@ class GetHttpConnection(HttpCommand):
@dataclass @dataclass
class GetHttpConnectionReply(events.CommandReply): class GetHttpConnectionCompleted(events.CommandCompleted):
command: GetHttpConnection command: GetHttpConnection
reply: Union[Tuple[None, str], Tuple[Connection, None]] reply: Union[Tuple[None, str], Tuple[Connection, None]]
"""connection object, error message""" """connection object, error message"""
@ -554,7 +554,7 @@ class HttpLayer(layer.Layer):
yield from self.event_to_child(self.connections[self.context.client], event) yield from self.event_to_child(self.connections[self.context.client], event)
if self.mode is HTTPMode.upstream: if self.mode is HTTPMode.upstream:
self.context.server.via = server_spec.parse_with_mode(self.context.options.mode)[1] self.context.server.via = server_spec.parse_with_mode(self.context.options.mode)[1]
elif isinstance(event, events.CommandReply): elif isinstance(event, events.CommandCompleted):
stream = self.command_sources.pop(event.command) stream = self.command_sources.pop(event.command)
yield from self.event_to_child(stream, event) yield from self.event_to_child(stream, event)
elif isinstance(event, events.ConnectionEvent): elif isinstance(event, events.ConnectionEvent):
@ -574,7 +574,7 @@ class HttpLayer(layer.Layer):
) -> 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)
# Streams may yield blocking commands, which ultimately generate CommandReply events. # Streams may yield blocking commands, which ultimately generate CommandCompleted events.
# Those need to be routed back to the correct stream, so we need to keep track of that. # Those need to be routed back to the correct stream, so we need to keep track of that.
if command.blocking: if command.blocking:
@ -624,7 +624,7 @@ class HttpLayer(layer.Layer):
self.waiting_for_establishment[connection].append(event) self.waiting_for_establishment[connection].append(event)
else: else:
stream = self.command_sources.pop(event) stream = self.command_sources.pop(event)
yield from self.event_to_child(stream, GetHttpConnectionReply(event, (connection, None))) yield from self.event_to_child(stream, GetHttpConnectionCompleted(event, (connection, None)))
return return
can_use_context_connection = ( can_use_context_connection = (
@ -674,7 +674,7 @@ class HttpLayer(layer.Layer):
for cmd in waiting: for cmd in waiting:
stream = self.command_sources.pop(cmd) stream = self.command_sources.pop(cmd)
yield from self.event_to_child(stream, GetHttpConnectionReply(cmd, reply)) yield from self.event_to_child(stream, GetHttpConnectionCompleted(cmd, reply))
# Somewhat ugly edge case: If we do HTTP/2 -> HTTP/1 proxying we don't want # Somewhat ugly edge case: If we do HTTP/2 -> HTTP/1 proxying we don't want
# to handle everything over a single connection. # to handle everything over a single connection.

View File

@ -5,7 +5,7 @@ from mitmproxy.proxy import commands
@dataclass @dataclass
class HttpRequestHeadersHook(commands.Hook): class HttpRequestHeadersHook(commands.StartHook):
""" """
HTTP request headers were successfully read. At this point, the body is empty. HTTP request headers were successfully read. At this point, the body is empty.
""" """
@ -14,7 +14,7 @@ class HttpRequestHeadersHook(commands.Hook):
@dataclass @dataclass
class HttpRequestHook(commands.Hook): class HttpRequestHook(commands.StartHook):
""" """
The full HTTP request has been read. The full HTTP request has been read.
@ -26,7 +26,7 @@ class HttpRequestHook(commands.Hook):
@dataclass @dataclass
class HttpResponseHeadersHook(commands.Hook): class HttpResponseHeadersHook(commands.StartHook):
""" """
The full HTTP response has been read. The full HTTP response has been read.
""" """
@ -35,7 +35,7 @@ class HttpResponseHeadersHook(commands.Hook):
@dataclass @dataclass
class HttpResponseHook(commands.Hook): class HttpResponseHook(commands.StartHook):
""" """
HTTP response headers were successfully read. At this point, the body is empty. HTTP response headers were successfully read. At this point, the body is empty.
@ -46,7 +46,7 @@ class HttpResponseHook(commands.Hook):
@dataclass @dataclass
class HttpErrorHook(commands.Hook): class HttpErrorHook(commands.StartHook):
""" """
An HTTP error has occurred, e.g. invalid server responses, or An HTTP error has occurred, e.g. invalid server responses, or
interrupted connections. This is distinct from a valid server HTTP interrupted connections. This is distinct from a valid server HTTP
@ -59,7 +59,7 @@ class HttpErrorHook(commands.Hook):
@dataclass @dataclass
class HttpConnectHook(commands.Hook): class HttpConnectHook(commands.StartHook):
""" """
An HTTP CONNECT request was received. This event can be ignored for most practical purposes. An HTTP CONNECT request was received. This event can be ignored for most practical purposes.

View File

@ -3,13 +3,13 @@ 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 Hook from mitmproxy.proxy.commands import StartHook
from mitmproxy.proxy.context import ConnectionState, Context, Connection from mitmproxy.proxy.context import ConnectionState, Context, Connection
from mitmproxy.proxy.utils import expect from mitmproxy.proxy.utils import expect
@dataclass @dataclass
class TcpStartHook(Hook): class TcpStartHook(StartHook):
""" """
A TCP connection has started. A TCP connection has started.
""" """
@ -18,7 +18,7 @@ class TcpStartHook(Hook):
@dataclass @dataclass
class TcpMessageHook(Hook): class TcpMessageHook(StartHook):
""" """
A TCP connection has received a message. The most recent message A TCP connection has received a message. The most recent message
will be flow.messages[-1]. The message is user-modifiable. will be flow.messages[-1]. The message is user-modifiable.
@ -27,7 +27,7 @@ class TcpMessageHook(Hook):
@dataclass @dataclass
class TcpEndHook(Hook): class TcpEndHook(StartHook):
""" """
A TCP connection has ended. A TCP connection has ended.
""" """
@ -35,7 +35,7 @@ class TcpEndHook(Hook):
@dataclass @dataclass
class TcpErrorHook(Hook): class TcpErrorHook(StartHook):
""" """
A TCP error has occurred. A TCP error has occurred.

View File

@ -8,7 +8,7 @@ from mitmproxy import certs
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
from mitmproxy.proxy.commands import Hook from mitmproxy.proxy.commands import StartHook
from mitmproxy.utils import human from mitmproxy.utils import human
@ -104,7 +104,7 @@ class ClientHelloData:
@dataclass @dataclass
class TlsClienthelloHook(Hook): class TlsClienthelloHook(StartHook):
""" """
Mitmproxy has received a TLS ClientHello message. Mitmproxy has received a TLS ClientHello message.
@ -122,7 +122,7 @@ class TlsStartData:
@dataclass @dataclass
class TlsStartHook(Hook): class TlsStartHook(StartHook):
""" """
TLS Negotation is about to start. TLS Negotation is about to start.

View File

@ -7,7 +7,7 @@ import wsproto.frame_protocol
import wsproto.utilities import wsproto.utilities
from mitmproxy import flow, websocket, http from mitmproxy import flow, websocket, http
from mitmproxy.proxy import commands, events, layer, context from mitmproxy.proxy import commands, events, layer, context
from mitmproxy.proxy.commands import Hook 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
from wsproto import ConnectionState from wsproto import ConnectionState
@ -15,7 +15,7 @@ from wsproto.frame_protocol import CloseReason, Opcode
@dataclass @dataclass
class WebsocketStartHook(Hook): class WebsocketStartHook(StartHook):
""" """
A WebSocket connection has commenced. A WebSocket connection has commenced.
""" """
@ -23,7 +23,7 @@ class WebsocketStartHook(Hook):
@dataclass @dataclass
class WebsocketMessageHook(Hook): class WebsocketMessageHook(StartHook):
""" """
Called when a WebSocket message is received from the client or Called when a WebSocket message is received from the client or
server. The most recent message will be flow.messages[-1]. The server. The most recent message will be flow.messages[-1]. The
@ -34,7 +34,7 @@ class WebsocketMessageHook(Hook):
@dataclass @dataclass
class WebsocketEndHook(Hook): class WebsocketEndHook(StartHook):
""" """
A WebSocket connection has ended. A WebSocket connection has ended.
""" """
@ -43,7 +43,7 @@ class WebsocketEndHook(Hook):
@dataclass @dataclass
class WebsocketErrorHook(Hook): class WebsocketErrorHook(StartHook):
""" """
A WebSocket connection has had an error. A WebSocket connection has had an error.

View File

@ -133,7 +133,7 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
async def open_connection(self, command: commands.OpenConnection) -> None: async def open_connection(self, command: commands.OpenConnection) -> None:
if not command.connection.address: if not command.connection.address:
self.log(f"Cannot open connection, no hostname given.") self.log(f"Cannot open connection, no hostname given.")
self.server_event(events.OpenConnectionReply(command, f"Cannot open connection, no hostname given.")) self.server_event(events.OpenConnectionCompleted(command, f"Cannot open connection, no hostname given."))
return return
hook_data = server_hooks.ServerConnectionHookData( hook_data = server_hooks.ServerConnectionHookData(
@ -143,7 +143,7 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
await self.handle_hook(server_hooks.ServerConnectHook(hook_data)) await self.handle_hook(server_hooks.ServerConnectHook(hook_data))
if command.connection.error: if command.connection.error:
self.log(f"server connection to {human.format_address(command.connection.address)} killed before connect.") self.log(f"server connection to {human.format_address(command.connection.address)} killed before connect.")
self.server_event(events.OpenConnectionReply(command, "Connection killed.")) self.server_event(events.OpenConnectionCompleted(command, "Connection killed."))
return return
async with self.max_conns[command.connection.address]: async with self.max_conns[command.connection.address]:
@ -156,7 +156,7 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
err = "connection cancelled" err = "connection cancelled"
self.log(f"error establishing server connection: {err}") self.log(f"error establishing server connection: {err}")
command.connection.error = err command.connection.error = err
self.server_event(events.OpenConnectionReply(command, err)) self.server_event(events.OpenConnectionCompleted(command, err))
if isinstance(e, asyncio.CancelledError): if isinstance(e, asyncio.CancelledError):
# From https://docs.python.org/3/library/asyncio-exceptions.html#asyncio.CancelledError: # From https://docs.python.org/3/library/asyncio-exceptions.html#asyncio.CancelledError:
# > In almost all situations the exception must be re-raised. # > In almost all situations the exception must be re-raised.
@ -184,7 +184,7 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
if not connected_hook: if not connected_hook:
return # this should not be needed, see asyncio_utils.create_task return # this should not be needed, see asyncio_utils.create_task
self.server_event(events.OpenConnectionReply(command, None)) self.server_event(events.OpenConnectionCompleted(command, None))
# during connection opening, this function is the designated handler that can be cancelled. # during connection opening, this function is the designated handler that can be cancelled.
# once we have a connection, we do want the teardown here to happen in any case, so we # once we have a connection, we do want the teardown here to happen in any case, so we
@ -256,13 +256,13 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
assert handler assert handler
asyncio_utils.cancel_task(handler, "timeout") asyncio_utils.cancel_task(handler, "timeout")
async def hook_task(self, hook: commands.Hook) -> None: async def hook_task(self, hook: commands.StartHook) -> None:
await self.handle_hook(hook) await self.handle_hook(hook)
if hook.blocking: if hook.blocking:
self.server_event(events.HookReply(hook)) self.server_event(events.HookCompleted(hook))
@abc.abstractmethod @abc.abstractmethod
async def handle_hook(self, hook: commands.Hook) -> None: async def handle_hook(self, hook: commands.StartHook) -> None:
pass pass
def log(self, message: str, level: str = "info") -> None: def log(self, message: str, level: str = "info") -> None:
@ -294,8 +294,8 @@ class ConnectionHandler(metaclass=abc.ABCMeta):
writer = self.transports[command.connection].writer writer = self.transports[command.connection].writer
assert writer assert writer
socket = writer.get_extra_info("socket") socket = writer.get_extra_info("socket")
self.server_event(events.GetSocketReply(command, socket)) self.server_event(events.GetSocketCompleted(command, socket))
elif isinstance(command, commands.Hook): elif isinstance(command, commands.StartHook):
asyncio_utils.create_task( asyncio_utils.create_task(
self.hook_task(command), self.hook_task(command),
name=f"handle_hook({command.name})", name=f"handle_hook({command.name})",
@ -354,7 +354,7 @@ class SimpleConnectionHandler(StreamConnectionHandler): # pragma: no cover
async def handle_hook( async def handle_hook(
self, self,
hook: commands.Hook hook: commands.StartHook
) -> None: ) -> None:
if hook.name in self.hook_handlers: if hook.name in self.hook_handlers:
self.hook_handlers[hook.name](*hook.args()) self.hook_handlers[hook.name](*hook.args())

View File

@ -4,7 +4,7 @@ from . import commands, context
@dataclass @dataclass
class ClientConnectedHook(commands.Hook): class ClientConnectedHook(commands.StartHook):
""" """
A client has connected to mitmproxy. Note that a connection can A client has connected to mitmproxy. Note that a connection can
correspond to multiple HTTP requests. correspond to multiple HTTP requests.
@ -15,7 +15,7 @@ class ClientConnectedHook(commands.Hook):
@dataclass @dataclass
class ClientDisconnectedHook(commands.Hook): 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).
""" """
@ -30,7 +30,7 @@ class ServerConnectionHookData:
@dataclass @dataclass
class ServerConnectHook(commands.Hook): class ServerConnectHook(commands.StartHook):
""" """
Mitmproxy is about to connect to a server. Mitmproxy is about to connect to a server.
Note that a connection can correspond to multiple requests. Note that a connection can correspond to multiple requests.
@ -41,7 +41,7 @@ class ServerConnectHook(commands.Hook):
@dataclass @dataclass
class ServerConnectedHook(commands.Hook): class ServerConnectedHook(commands.StartHook):
""" """
Mitmproxy has connected to a server. Mitmproxy has connected to a server.
""" """
@ -50,7 +50,7 @@ class ServerConnectedHook(commands.Hook):
@dataclass @dataclass
class ServerDisconnectedHook(commands.Hook): class ServerDisconnectedHook(commands.StartHook):
""" """
A server connection has been closed (either by us or the server). A server connection has been closed (either by us or the server).
""" """

View File

@ -89,7 +89,7 @@ class TunnelLayer(layer.Layer):
else: else:
self.tunnel_state = TunnelState.OPEN self.tunnel_state = TunnelState.OPEN
if self.command_to_reply_to: if self.command_to_reply_to:
yield from self.event_to_child(events.OpenConnectionReply(self.command_to_reply_to, err)) yield from self.event_to_child(events.OpenConnectionCompleted(self.command_to_reply_to, err))
self.command_to_reply_to = None self.command_to_reply_to = None
else: else:
for evt in self._event_queue: for evt in self._event_queue:
@ -117,7 +117,7 @@ class TunnelLayer(layer.Layer):
self.tunnel_state = TunnelState.ESTABLISHING self.tunnel_state = TunnelState.ESTABLISHING
err = yield commands.OpenConnection(self.tunnel_connection) err = yield commands.OpenConnection(self.tunnel_connection)
if err: if err:
yield from self.event_to_child(events.OpenConnectionReply(command, err)) yield from self.event_to_child(events.OpenConnectionCompleted(command, err))
self.tunnel_state = TunnelState.CLOSED self.tunnel_state = TunnelState.CLOSED
else: else:
yield from self.start_handshake() yield from self.start_handshake()

View File

@ -3,7 +3,7 @@ This module provides a @concurrent decorator primitive to
offload computations from mitmproxy's main master thread. offload computations from mitmproxy's main master thread.
""" """
from mitmproxy import events from mitmproxy import event_hooks
from mitmproxy.coretypes import basethread from mitmproxy.coretypes import basethread
@ -12,7 +12,7 @@ class ScriptThread(basethread.BaseThread):
def concurrent(fn): def concurrent(fn):
if fn.__name__ not in set(events.all_events.keys()) - {"load", "configure"}: if fn.__name__ not in set(event_hooks.all_events.keys()) - {"load", "configure"}:
raise NotImplementedError( raise NotImplementedError(
"Concurrent decorator not supported for '%s' method." % fn.__name__ "Concurrent decorator not supported for '%s' method." % fn.__name__
) )

View File

@ -4,7 +4,7 @@ import sys
import mitmproxy.master import mitmproxy.master
import mitmproxy.options import mitmproxy.options
from mitmproxy import addonmanager, events, log from mitmproxy import addonmanager, event_hooks, log
from mitmproxy import command from mitmproxy import command
from mitmproxy import eventsequence from mitmproxy import eventsequence
from mitmproxy.addons import script, core from mitmproxy.addons import script, core
@ -14,8 +14,8 @@ class TestAddons(addonmanager.AddonManager):
def __init__(self, master): def __init__(self, master):
super().__init__(master) super().__init__(master)
def trigger(self, event: events.MitmproxyEvent): def trigger(self, event: event_hooks.EventHook):
if isinstance(event, log.AddLogEvent): if isinstance(event, log.AddLogEventHook):
self.master.logs.append(event.entry) self.master.logs.append(event.entry)
super().trigger(event) super().trigger(event)
@ -106,7 +106,7 @@ class context:
if kwargs: if kwargs:
self.options.update(**kwargs) self.options.update(**kwargs)
else: else:
self.master.addons.invoke_addon(addon, events.ConfigureEvent(set())) self.master.addons.invoke_addon(addon, event_hooks.ConfigureEventHook(set()))
def script(self, path): def script(self, path):
""" """
@ -115,7 +115,7 @@ class context:
sc = script.Script(path, False) sc = script.Script(path, False)
return sc.addons[0] if sc.addons else None return sc.addons[0] if sc.addons else None
def invoke(self, addon, event: events.MitmproxyEvent): def invoke(self, addon, event: event_hooks.EventHook):
""" """
Recursively invoke an event on an addon and all its children. Recursively invoke an event on an addon and all its children.
""" """

View File

@ -1,5 +1,5 @@
from mitmproxy import ctx from mitmproxy import ctx
from mitmproxy import events from mitmproxy import event_hooks
class Recorder: class Recorder:
@ -9,7 +9,7 @@ class Recorder:
self.name = name self.name = name
def __getattr__(self, attr): def __getattr__(self, attr):
if attr in events.all_events: if attr in event_hooks.all_events:
def prox(*args, **kwargs): def prox(*args, **kwargs):
lg = (self.name, attr, args, kwargs) lg = (self.name, attr, args, kwargs)
if attr != "add_log": if attr != "add_log":

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
import pytest import pytest
from mitmproxy.events import all_events from mitmproxy.event_hooks import all_events
from mitmproxy.proxy import commands, context from mitmproxy.proxy import commands, context
@ -21,10 +21,10 @@ def test_dataclasses(tconn):
def test_hook(): def test_hook():
with pytest.raises(TypeError): with pytest.raises(TypeError):
commands.Hook() commands.StartHook()
@dataclass @dataclass
class TestHook(commands.Hook): class TestHook(commands.StartHook):
data: bytes data: bytes
f = TestHook(b"foo") f = TestHook(b"foo")

View File

@ -16,21 +16,21 @@ def test_dataclasses(tconn):
assert repr(events.ConnectionClosed(tconn)) assert repr(events.ConnectionClosed(tconn))
def test_commandreply(): def test_command_completed():
with pytest.raises(TypeError): with pytest.raises(TypeError):
events.CommandReply() events.CommandCompleted()
assert repr(events.HookReply(Mock(), None)) assert repr(events.HookCompleted(Mock(), None))
class FooCommand(commands.Command): class FooCommand(commands.Command):
pass pass
with pytest.raises(RuntimeError, match="properly annotated"): with pytest.raises(RuntimeError, match="properly annotated"):
class FooReply(events.CommandReply): class FooCompleted(events.CommandCompleted):
pass pass
class FooReply1(events.CommandReply): class FooCompleted1(events.CommandCompleted):
command: FooCommand command: FooCommand
with pytest.raises(RuntimeError, match="conflicting subclasses"): with pytest.raises(RuntimeError, match="conflicting subclasses"):
class FooReply2(events.CommandReply): class FooCompleted2(events.CommandCompleted):
command: FooCommand command: FooCommand

View File

@ -22,7 +22,7 @@ class TCommand(commands.Command):
@dataclass @dataclass
class TCommandReply(events.CommandReply): class TCommandCompleted(events.CommandCompleted):
command: TCommand command: TCommand

View File

@ -79,7 +79,7 @@ def _merge_sends(lst: typing.List[commands.Command], ignore_hooks: bool, ignore_
current_send.data += x.data current_send.data += x.data
else: else:
ignore = ( ignore = (
(ignore_hooks and isinstance(x, commands.Hook)) (ignore_hooks and isinstance(x, commands.StartHook))
or or
(ignore_logs and isinstance(x, commands.Log)) (ignore_logs and isinstance(x, commands.Log))
) )
@ -191,7 +191,7 @@ class Playbook:
for name, value in vars(x).items(): for name, value in vars(x).items():
if isinstance(value, _Placeholder): if isinstance(value, _Placeholder):
setattr(x, name, value()) setattr(x, name, value())
if isinstance(x, events.OpenConnectionReply) and not x.reply: if isinstance(x, events.OpenConnectionCompleted) and not x.reply:
x.command.connection.state = ConnectionState.OPEN x.command.connection.state = ConnectionState.OPEN
elif isinstance(x, events.ConnectionClosed): elif isinstance(x, events.ConnectionClosed):
x.connection.state &= ~ConnectionState.CAN_READ x.connection.state &= ~ConnectionState.CAN_READ
@ -227,13 +227,13 @@ class Playbook:
) )
if need_to_emulate_log: if need_to_emulate_log:
self.expected.insert(pos, cmd) self.expected.insert(pos, cmd)
elif isinstance(cmd, commands.Hook) and not self.hooks: elif isinstance(cmd, commands.StartHook) and not self.hooks:
need_to_emulate_hook = ( need_to_emulate_hook = (
not self.hooks not self.hooks
and ( and (
pos >= len(self.expected) or pos >= len(self.expected) or
(not ( (not (
isinstance(self.expected[pos], commands.Hook) isinstance(self.expected[pos], commands.StartHook)
and self.expected[pos].name == cmd.name and self.expected[pos].name == cmd.name
)) ))
) )
@ -243,7 +243,7 @@ class Playbook:
if cmd.blocking: if cmd.blocking:
# the current event may still have yielded more events, so we need to insert # the current event may still have yielded more events, so we need to insert
# the reply *after* those additional events. # the reply *after* those additional events.
hook_replies.append(events.HookReply(cmd)) hook_replies.append(events.HookCompleted(cmd))
self.expected = self.expected[:pos + 1] + hook_replies + self.expected[pos + 1:] self.expected = self.expected[:pos + 1] + hook_replies + self.expected[pos + 1:]
eq(self.expected[i:], self.actual[i:]) # compare now already to set placeholders eq(self.expected[i:], self.actual[i:]) # compare now already to set placeholders
@ -288,7 +288,7 @@ class reply(events.Event):
self.to = to self.to = to
self.side_effect = side_effect self.side_effect = side_effect
def playbook_eval(self, playbook: Playbook) -> events.CommandReply: def playbook_eval(self, playbook: Playbook) -> events.CommandCompleted:
if isinstance(self.to, int): if isinstance(self.to, int):
expected = playbook.expected[:playbook.expected.index(self)] expected = playbook.expected[:playbook.expected.index(self)]
assert abs(self.to) < len(expected) assert abs(self.to) < len(expected)
@ -305,9 +305,9 @@ class reply(events.Event):
raise AssertionError(f"Expected command {self.to} did not occur.") raise AssertionError(f"Expected command {self.to} did not occur.")
assert isinstance(self.to, commands.Command) assert isinstance(self.to, commands.Command)
if isinstance(self.to, commands.Hook): if isinstance(self.to, commands.StartHook):
self.side_effect(*self.to.args()) self.side_effect(*self.to.args())
reply_cls = events.HookReply reply_cls = events.HookCompleted
else: else:
self.side_effect(self.to) self.side_effect(self.to)
reply_cls = command_reply_subclasses[type(self.to)] reply_cls = command_reply_subclasses[type(self.to)]

View File

@ -3,7 +3,7 @@ from unittest import mock
import pytest import pytest
from mitmproxy import addonmanager from mitmproxy import addonmanager
from mitmproxy import addons, events from mitmproxy import addons, event_hooks
from mitmproxy import command from mitmproxy import command
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import master from mitmproxy import master
@ -66,11 +66,11 @@ def test_halt():
a.add(end) a.add(end)
assert not end.running_called assert not end.running_called
a.trigger(events.RunningEvent()) a.trigger(event_hooks.RunningEventHook())
assert not end.running_called assert not end.running_called
a.remove(halt) a.remove(halt)
a.trigger(events.RunningEvent()) a.trigger(event_hooks.RunningEventHook())
assert end.running_called assert end.running_called
@ -138,7 +138,7 @@ async def test_simple():
await tctx.master.await_log("AssertionError") await tctx.master.await_log("AssertionError")
f = tflow.tflow() f = tflow.tflow()
a.trigger(events.RunningEvent()) a.trigger(event_hooks.RunningEventHook())
a.trigger(HttpResponseHook(f)) a.trigger(HttpResponseHook(f))
await tctx.master.await_log("not callable") await tctx.master.await_log("not callable")
@ -153,7 +153,7 @@ async def test_simple():
ta = TAddon("one") ta = TAddon("one")
a.add(ta) a.add(ta)
a.trigger(events.RunningEvent()) a.trigger(event_hooks.RunningEventHook())
assert ta.running_called assert ta.running_called
assert ta in a assert ta in a
@ -187,7 +187,7 @@ def test_nesting():
assert a.get("three") assert a.get("three")
assert a.get("four") assert a.get("four")
a.trigger(events.RunningEvent()) a.trigger(event_hooks.RunningEventHook())
assert a.get("one").running_called assert a.get("one").running_called
assert a.get("two").running_called assert a.get("two").running_called
assert a.get("three").running_called assert a.get("three").running_called

View File

@ -2,35 +2,35 @@ from dataclasses import dataclass
import pytest import pytest
from mitmproxy import events from mitmproxy import event_hooks
def test_event(): def test_event():
with pytest.raises(TypeError, match="may not be instantiated directly"): with pytest.raises(TypeError, match="may not be instantiated directly"):
events.MitmproxyEvent() event_hooks.EventHook()
class NoDataClass(events.MitmproxyEvent): class NoDataClass(event_hooks.EventHook):
pass pass
with pytest.raises(TypeError, match="not a dataclass"): with pytest.raises(TypeError, match="not a dataclass"):
NoDataClass() NoDataClass()
@dataclass @dataclass
class FooEvent(events.MitmproxyEvent): class FooEventHook(event_hooks.EventHook):
data: bytes data: bytes
e = FooEvent(b"foo") e = FooEventHook(b"foo")
assert repr(e) assert repr(e)
assert e.args() == [b"foo"] assert e.args() == [b"foo"]
assert FooEvent in events.all_events.values() assert FooEventHook in event_hooks.all_events.values()
with pytest.raises(RuntimeError, match="Two conflicting event classes"): with pytest.raises(RuntimeError, match="Two conflicting event classes"):
@dataclass @dataclass
class FooEvent2(events.MitmproxyEvent): class FooEventHook2(event_hooks.EventHook):
name = "foo" name = "foo"
@dataclass @dataclass
class AnotherABC(events.MitmproxyEvent): class AnotherABC(event_hooks.EventHook):
name = "" name = ""
assert AnotherABC not in events.all_events.values() assert AnotherABC not in event_hooks.all_events.values()

View File

@ -2,7 +2,7 @@ import urwid
import pytest import pytest
from mitmproxy import options, events from mitmproxy import options, event_hooks
from mitmproxy.tools import console from mitmproxy.tools import console
from ... import tservers from ... import tservers
@ -13,7 +13,7 @@ class TestMaster(tservers.MasterTest):
def mkmaster(self, **opts): def mkmaster(self, **opts):
o = options.Options(**opts) o = options.Options(**opts)
m = console.master.ConsoleMaster(o) m = console.master.ConsoleMaster(o)
m.addons.trigger(events.ConfigureEvent(o.keys())) m.addons.trigger(event_hooks.ConfigureEventHook(o.keys()))
return m return m
async def test_basic(self): async def test_basic(self):

View File

@ -18,7 +18,7 @@ class TestDumpMaster:
m = self.mkmaster() m = self.mkmaster()
ent = log.LogEntry("foo", "error") ent = log.LogEntry("foo", "error")
ent.reply = controller.DummyReply() ent.reply = controller.DummyReply()
m.addons.trigger(log.AddLogEvent(ent)) m.addons.trigger(log.AddLogEventHook(ent))
assert m.errorcheck.has_errored assert m.errorcheck.has_errored
@pytest.mark.parametrize("termlog", [False, True]) @pytest.mark.parametrize("termlog", [False, True])