mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-25 09:37:37 +00:00
addon loader: add boot_into, which replaces returning from start()
While we're here, expand test coverage for addonmanager to 100%, and promote to individual coverage.
This commit is contained in:
parent
d69a411303
commit
65f0885bd6
@ -54,5 +54,5 @@ class Rerouter:
|
||||
flow.request.port = port
|
||||
|
||||
|
||||
def load(opts):
|
||||
return Rerouter()
|
||||
def load(l):
|
||||
l.boot_into(Rerouter())
|
||||
|
@ -25,7 +25,7 @@ HAR = {}
|
||||
SERVERS_SEEN = set()
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
"""
|
||||
Called once on script startup before any other events.
|
||||
"""
|
||||
|
@ -14,6 +14,6 @@ Usage:
|
||||
"""
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
import pydevd
|
||||
pydevd.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True)
|
||||
|
@ -112,7 +112,7 @@ class TlsFeedback(TlsLayer):
|
||||
tls_strategy = None
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
global tls_strategy
|
||||
if len(sys.argv) == 2:
|
||||
tls_strategy = ProbabilisticStrategy(float(sys.argv[1]))
|
||||
|
@ -3,5 +3,5 @@ class AddHeader:
|
||||
flow.response.headers["newheader"] = "foo"
|
||||
|
||||
|
||||
def load(opts):
|
||||
return AddHeader()
|
||||
def load(l):
|
||||
return l.boot_into(AddHeader())
|
||||
|
@ -20,7 +20,7 @@ class ViewSwapCase(contentviews.View):
|
||||
view = ViewSwapCase()
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
contentviews.add(view)
|
||||
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
from mitmproxy import ctx
|
||||
|
||||
|
||||
def load(options):
|
||||
def load(l):
|
||||
ctx.log.info("Registering option 'custom'")
|
||||
options.add_option("custom", bool, False, "A custom option")
|
||||
l.add_option("custom", bool, False, "A custom option")
|
||||
|
||||
|
||||
def configure(options, updated):
|
||||
|
@ -17,7 +17,7 @@ class Filter:
|
||||
print(flow)
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
if len(sys.argv) != 2:
|
||||
raise ValueError("Usage: -s 'filt.py FILTER'")
|
||||
return Filter(sys.argv[1])
|
||||
l.boot_into(Filter(sys.argv[1]))
|
||||
|
@ -23,7 +23,7 @@ class Writer:
|
||||
self.w.add(flow)
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
if len(sys.argv) != 2:
|
||||
raise ValueError('Usage: -s "flowriter.py filename"')
|
||||
return Writer(sys.argv[1])
|
||||
l.boot_into(Writer(sys.argv[1]))
|
||||
|
@ -7,6 +7,6 @@ If you want to help us out: https://github.com/mitmproxy/mitmproxy/issues/1530 :
|
||||
from mitmproxy import ctx
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
ctx.log.info("This is some informative text.")
|
||||
ctx.log.error("This is an error.")
|
||||
|
@ -23,7 +23,7 @@ class Injector:
|
||||
flow.response.content = str(html).encode("utf8")
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
if len(sys.argv) != 2:
|
||||
raise ValueError('Usage: -s "iframe_injector.py url"')
|
||||
return Injector(sys.argv[1])
|
||||
return l.boot_into(Injector(sys.argv[1]))
|
||||
|
@ -9,9 +9,9 @@ class Replacer:
|
||||
flow.response.replace(self.src, self.dst)
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("src", type=str)
|
||||
parser.add_argument("dst", type=str)
|
||||
args = parser.parse_args()
|
||||
return Replacer(args.src, args.dst)
|
||||
l.boot_into(Replacer(args.src, args.dst))
|
||||
|
@ -14,7 +14,7 @@ def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
# Host app at the magic domain "proxapp" on port 80. Requests to this
|
||||
# domain and port combination will now be routed to the WSGI app instance.
|
||||
return wsgiapp.WSGIApp(app, "proxapp", 80)
|
||||
|
@ -17,6 +17,7 @@ class Loader:
|
||||
"""
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
self.boot_into_addon = None
|
||||
|
||||
def add_option(
|
||||
self,
|
||||
@ -34,6 +35,12 @@ class Loader:
|
||||
choices
|
||||
)
|
||||
|
||||
def boot_into(self, addon):
|
||||
self.boot_into_addon = addon
|
||||
func = getattr(addon, "load", None)
|
||||
if func:
|
||||
func(self)
|
||||
|
||||
|
||||
class AddonManager:
|
||||
def __init__(self, master):
|
||||
@ -65,11 +72,14 @@ class AddonManager:
|
||||
"""
|
||||
Add addons to the end of the chain, and run their startup events.
|
||||
"""
|
||||
self.chain.extend(addons)
|
||||
with self.master.handlecontext():
|
||||
l = Loader(self.master)
|
||||
for i in addons:
|
||||
l = Loader(self.master)
|
||||
self.invoke_addon(i, "load", l)
|
||||
if l.boot_into_addon:
|
||||
self.chain.append(l.boot_into_addon)
|
||||
else:
|
||||
self.chain.append(i)
|
||||
|
||||
def remove(self, addon):
|
||||
"""
|
||||
|
@ -186,23 +186,21 @@ class Script:
|
||||
def load_script(self):
|
||||
self.ns = load_script(self.path, self.args)
|
||||
l = addonmanager.Loader(ctx.master)
|
||||
ret = self.run("load", l)
|
||||
if ret:
|
||||
self.ns = ret
|
||||
self.run("load", l)
|
||||
self.run("load", l)
|
||||
if l.boot_into_addon:
|
||||
self.ns = l.boot_into_addon
|
||||
|
||||
def tick(self):
|
||||
if self.should_reload.is_set():
|
||||
self.should_reload.clear()
|
||||
ctx.log.info("Reloading script: %s" % self.name)
|
||||
self.ns = load_script(self.path, self.args)
|
||||
self.load(self.last_options)
|
||||
self.configure(self.last_options, self.last_options.keys())
|
||||
else:
|
||||
self.run("tick")
|
||||
|
||||
def load(self, opts):
|
||||
self.last_options = opts
|
||||
def load(self, l):
|
||||
self.last_options = ctx.master.options
|
||||
self.load_script()
|
||||
|
||||
def configure(self, options, updated):
|
||||
@ -289,7 +287,8 @@ class ScriptLoader:
|
||||
ctx.master.addons.chain = ochain[:pos + 1] + ordered + ochain[pos + 1:]
|
||||
|
||||
for s in newscripts:
|
||||
ctx.master.addons.invoke_addon(s, "load", options)
|
||||
l = addonmanager.Loader(ctx.master)
|
||||
ctx.master.addons.invoke_addon(s, "load", l)
|
||||
if self.is_running:
|
||||
# If we're already running, we configure and tell the addon
|
||||
# we're up and running.
|
||||
|
@ -16,11 +16,11 @@ class ErrorCheck:
|
||||
class DumpMaster(master.Master):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: options.Options,
|
||||
server,
|
||||
with_termlog=True,
|
||||
with_dumper=True,
|
||||
self,
|
||||
options: options.Options,
|
||||
server,
|
||||
with_termlog=True,
|
||||
with_dumper=True,
|
||||
) -> None:
|
||||
master.Master.__init__(self, options, server)
|
||||
self.errorcheck = ErrorCheck()
|
||||
|
@ -46,7 +46,6 @@ exclude =
|
||||
|
||||
[tool:individual_coverage]
|
||||
exclude =
|
||||
mitmproxy/addonmanager.py
|
||||
mitmproxy/addons/onboardingapp/app.py
|
||||
mitmproxy/addons/termlog.py
|
||||
mitmproxy/contentviews/base.py
|
||||
|
@ -9,6 +9,7 @@ from unittest import mock
|
||||
from mitmproxy.test import tflow
|
||||
from mitmproxy.test import tutils
|
||||
from mitmproxy.test import taddons
|
||||
from mitmproxy import addonmanager
|
||||
from mitmproxy import exceptions
|
||||
from mitmproxy import options
|
||||
from mitmproxy import proxy
|
||||
@ -116,7 +117,7 @@ def test_script_print_stdout():
|
||||
"mitmproxy/data/addonscripts/print.py"
|
||||
), []
|
||||
)
|
||||
ns.start(tctx.options)
|
||||
ns.load(addonmanager.Loader(tctx.master))
|
||||
mock_warn.assert_called_once_with("stdoutprint")
|
||||
|
||||
|
||||
@ -157,7 +158,8 @@ class TestScript:
|
||||
sc = script.Script(
|
||||
tutils.test_data.path("mitmproxy/data/addonscripts/error.py")
|
||||
)
|
||||
sc.load(tctx.options)
|
||||
l = addonmanager.Loader(tctx.master)
|
||||
sc.load(l)
|
||||
f = tflow.tflow(resp=True)
|
||||
sc.request(f)
|
||||
assert tctx.master.logs[0].level == "error"
|
||||
@ -173,7 +175,8 @@ class TestScript:
|
||||
"mitmproxy/data/addonscripts/addon.py"
|
||||
)
|
||||
)
|
||||
sc.load(tctx.options)
|
||||
l = addonmanager.Loader(tctx.master)
|
||||
sc.load(l)
|
||||
tctx.configure(sc)
|
||||
assert sc.ns.event_log == [
|
||||
'scriptload', 'addonload', 'addonconfigure'
|
||||
|
@ -17,6 +17,6 @@ def configure(options, updated):
|
||||
event_log.append("addonconfigure")
|
||||
|
||||
|
||||
def load(opts):
|
||||
def load(l):
|
||||
event_log.append("scriptload")
|
||||
return Addon()
|
||||
l.boot_into(Addon())
|
||||
|
@ -9,5 +9,5 @@ class ConcurrentClass:
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def load(opts):
|
||||
return ConcurrentClass()
|
||||
def load(l):
|
||||
l.boot_into(ConcurrentClass())
|
||||
|
@ -22,5 +22,5 @@ class CallLogger:
|
||||
raise AttributeError
|
||||
|
||||
|
||||
def load(opts):
|
||||
return CallLogger(*sys.argv[1:])
|
||||
def load(l):
|
||||
l.boot_into(CallLogger(*sys.argv[1:]))
|
||||
|
@ -2,6 +2,7 @@ from mitmproxy.test import tflow
|
||||
from mitmproxy.test import tutils
|
||||
from mitmproxy.test import taddons
|
||||
|
||||
from mitmproxy import addonmanager
|
||||
from mitmproxy import controller
|
||||
from mitmproxy.addons import script
|
||||
|
||||
@ -24,7 +25,8 @@ class TestConcurrent(tservers.MasterTest):
|
||||
"mitmproxy/data/addonscripts/concurrent_decorator.py"
|
||||
)
|
||||
)
|
||||
sc.load(tctx.options)
|
||||
l = addonmanager.Loader(tctx.master)
|
||||
sc.load(l)
|
||||
|
||||
f1, f2 = tflow.tflow(), tflow.tflow()
|
||||
tctx.cycle(sc, f1)
|
||||
@ -42,7 +44,8 @@ class TestConcurrent(tservers.MasterTest):
|
||||
"mitmproxy/data/addonscripts/concurrent_decorator_err.py"
|
||||
)
|
||||
)
|
||||
sc.load(tctx.options)
|
||||
l = addonmanager.Loader(tctx.master)
|
||||
sc.load(l)
|
||||
assert tctx.master.has_log("decorator not supported")
|
||||
|
||||
def test_concurrent_class(self):
|
||||
@ -52,7 +55,8 @@ class TestConcurrent(tservers.MasterTest):
|
||||
"mitmproxy/data/addonscripts/concurrent_decorator_class.py"
|
||||
)
|
||||
)
|
||||
sc.load(tctx.options)
|
||||
l = addonmanager.Loader(tctx.master)
|
||||
sc.load(l)
|
||||
|
||||
f1, f2 = tflow.tflow(), tflow.tflow()
|
||||
tctx.cycle(sc, f1)
|
||||
|
@ -5,6 +5,7 @@ from mitmproxy import exceptions
|
||||
from mitmproxy import options
|
||||
from mitmproxy import master
|
||||
from mitmproxy import proxy
|
||||
from mitmproxy.test import tflow
|
||||
|
||||
|
||||
class TAddon:
|
||||
@ -23,6 +24,58 @@ class TAddon:
|
||||
self.custom_called = True
|
||||
|
||||
|
||||
class THalt:
|
||||
def event_custom(self):
|
||||
raise exceptions.AddonHalt
|
||||
|
||||
|
||||
class AOption:
|
||||
def load(self, l):
|
||||
l.add_option("custom_option", bool, False, "help")
|
||||
|
||||
|
||||
class AChain:
|
||||
def __init__(self, name, next):
|
||||
self.name = name
|
||||
self.next = next
|
||||
|
||||
def load(self, l):
|
||||
if self.next:
|
||||
l.boot_into(self.next)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s>" % self.name
|
||||
|
||||
|
||||
def test_halt():
|
||||
o = options.Options()
|
||||
m = master.Master(o, proxy.DummyServer(o))
|
||||
a = addonmanager.AddonManager(m)
|
||||
halt = THalt()
|
||||
end = TAddon("end")
|
||||
a.add(halt)
|
||||
a.add(end)
|
||||
|
||||
a.trigger("custom")
|
||||
assert not end.custom_called
|
||||
|
||||
a.remove(halt)
|
||||
a.trigger("custom")
|
||||
assert end.custom_called
|
||||
|
||||
|
||||
def test_lifecycle():
|
||||
o = options.Options()
|
||||
m = master.Master(o, proxy.DummyServer(o))
|
||||
a = addonmanager.AddonManager(m)
|
||||
a.add(TAddon("one"))
|
||||
|
||||
f = tflow.tflow()
|
||||
a.handle_lifecycle("request", f)
|
||||
|
||||
a.configure_all(o, o.keys())
|
||||
|
||||
|
||||
def test_simple():
|
||||
o = options.Options()
|
||||
m = master.Master(o, proxy.DummyServer(o))
|
||||
@ -30,10 +83,13 @@ def test_simple():
|
||||
with pytest.raises(exceptions.AddonError):
|
||||
a.invoke_addon(TAddon("one"), "done")
|
||||
|
||||
assert len(a) == 0
|
||||
a.add(TAddon("one"))
|
||||
assert a.get("one")
|
||||
assert not a.get("two")
|
||||
assert len(a) == 1
|
||||
a.clear()
|
||||
assert len(a) == 0
|
||||
assert not a.chain
|
||||
|
||||
a.add(TAddon("one"))
|
||||
@ -41,7 +97,46 @@ def test_simple():
|
||||
with pytest.raises(exceptions.AddonError):
|
||||
a.trigger("tick")
|
||||
|
||||
a.remove(a.get("one"))
|
||||
assert not a.get("one")
|
||||
|
||||
ta = TAddon("one")
|
||||
a.add(ta)
|
||||
a.trigger("custom")
|
||||
assert ta.custom_called
|
||||
|
||||
|
||||
def test_load_option():
|
||||
o = options.Options()
|
||||
m = master.Master(o, proxy.DummyServer(o))
|
||||
a = addonmanager.AddonManager(m)
|
||||
a.add(AOption())
|
||||
assert "custom_option" in m.options._options
|
||||
|
||||
|
||||
def test_loadchain():
|
||||
o = options.Options()
|
||||
m = master.Master(o, proxy.DummyServer(o))
|
||||
a = addonmanager.AddonManager(m)
|
||||
|
||||
a.add(AChain("one", None))
|
||||
assert a.get("one")
|
||||
a.clear()
|
||||
|
||||
a.add(AChain("one", AChain("two", None)))
|
||||
assert not a.get("one")
|
||||
assert a.get("two")
|
||||
a.clear()
|
||||
|
||||
a.add(AChain("one", AChain("two", AChain("three", None))))
|
||||
assert not a.get("one")
|
||||
assert not a.get("two")
|
||||
assert a.get("three")
|
||||
a.clear()
|
||||
|
||||
a.add(AChain("one", AChain("two", AChain("three", AChain("four", None)))))
|
||||
assert not a.get("one")
|
||||
assert not a.get("two")
|
||||
assert not a.get("three")
|
||||
assert a.get("four")
|
||||
a.clear()
|
||||
|
Loading…
Reference in New Issue
Block a user