aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/addonmanager.py5
-rw-r--r--mitmproxy/addons/clientplayback.py6
-rw-r--r--mitmproxy/addons/keepserving.py7
-rw-r--r--mitmproxy/addons/readfile.py6
-rw-r--r--mitmproxy/addons/readstdin.py10
-rw-r--r--mitmproxy/addons/serverplayback.py4
-rw-r--r--mitmproxy/options.py5
-rw-r--r--mitmproxy/test/taddons.py26
-rw-r--r--mitmproxy/tools/dump.py4
-rw-r--r--test/mitmproxy/addons/test_check_alpn.py4
-rw-r--r--test/mitmproxy/addons/test_check_ca.py2
-rw-r--r--test/mitmproxy/addons/test_clientplayback.py13
-rw-r--r--test/mitmproxy/addons/test_dumper.py2
-rw-r--r--test/mitmproxy/addons/test_keepserving.py10
-rw-r--r--test/mitmproxy/addons/test_readfile.py8
-rw-r--r--test/mitmproxy/addons/test_readstdin.py10
-rw-r--r--test/mitmproxy/addons/test_replace.py4
-rw-r--r--test/mitmproxy/addons/test_script.py30
-rw-r--r--test/mitmproxy/addons/test_serverplayback.py3
-rw-r--r--test/mitmproxy/addons/test_termstatus.py4
-rw-r--r--test/mitmproxy/script/test_concurrent.py2
-rw-r--r--test/mitmproxy/test_addonmanager.py12
22 files changed, 102 insertions, 75 deletions
diff --git a/mitmproxy/addonmanager.py b/mitmproxy/addonmanager.py
index 097f87b7..123f64b2 100644
--- a/mitmproxy/addonmanager.py
+++ b/mitmproxy/addonmanager.py
@@ -69,8 +69,8 @@ class AddonManager:
raise exceptions.AddonError(
"invoke_addon called without a handler context."
)
- if name not in eventsequence.Events: # prama: no cover
- raise NotImplementedError("Unknown event")
+ if name not in eventsequence.Events:
+ name = "event_" + name
func = getattr(addon, name, None)
if func:
if not callable(func):
@@ -89,4 +89,3 @@ class AddonManager:
self.invoke_addon(i, name, *args, **kwargs)
except exceptions.AddonHalt:
return
-
diff --git a/mitmproxy/addons/clientplayback.py b/mitmproxy/addons/clientplayback.py
index 34c6c9c9..3345e65a 100644
--- a/mitmproxy/addons/clientplayback.py
+++ b/mitmproxy/addons/clientplayback.py
@@ -10,7 +10,6 @@ class ClientPlayback:
def __init__(self):
self.flows = None
self.current_thread = None
- self.keepserving = False
self.has_replayed = False
def count(self) -> int:
@@ -32,7 +31,6 @@ class ClientPlayback:
self.load(flows)
else:
self.flows = None
- self.keepserving = options.keepserving
def tick(self):
if self.current_thread and not self.current_thread.is_alive():
@@ -41,5 +39,5 @@ class ClientPlayback:
self.current_thread = ctx.master.replay_request(self.flows.pop(0))
self.has_replayed = True
if self.has_replayed:
- if not self.flows and not self.current_thread and not self.keepserving:
- ctx.master.shutdown()
+ if not self.flows and not self.current_thread:
+ ctx.master.addons.trigger("processing_complete")
diff --git a/mitmproxy/addons/keepserving.py b/mitmproxy/addons/keepserving.py
new file mode 100644
index 00000000..9c975a7b
--- /dev/null
+++ b/mitmproxy/addons/keepserving.py
@@ -0,0 +1,7 @@
+from mitmproxy import ctx
+
+
+class KeepServing:
+ def event_processing_complete(self):
+ if not ctx.master.options.keepserving:
+ ctx.master.shutdown()
diff --git a/mitmproxy/addons/readfile.py b/mitmproxy/addons/readfile.py
index a4b92444..03dcd084 100644
--- a/mitmproxy/addons/readfile.py
+++ b/mitmproxy/addons/readfile.py
@@ -11,7 +11,6 @@ class ReadFile:
"""
def __init__(self):
self.path = None
- self.keepserving = False
def load_flows_file(self, path: str) -> int:
path = os.path.expanduser(path)
@@ -33,8 +32,6 @@ class ReadFile:
raise exceptions.FlowReadException(v)
def configure(self, options, updated):
- if "keepserving" in updated:
- self.keepserving = options.keepserving
if "rfile" in updated and options.rfile:
self.path = options.rfile
@@ -46,5 +43,4 @@ class ReadFile:
raise exceptions.OptionsError(v)
finally:
self.path = None
- if not self.keepserving:
- ctx.master.shutdown()
+ ctx.master.addons.trigger("processing_complete")
diff --git a/mitmproxy/addons/readstdin.py b/mitmproxy/addons/readstdin.py
index e45d25b8..93a99f01 100644
--- a/mitmproxy/addons/readstdin.py
+++ b/mitmproxy/addons/readstdin.py
@@ -9,13 +9,6 @@ class ReadStdin:
An addon that reads from stdin if we're not attached to (someting like)
a tty.
"""
- def __init__(self):
- self.keepserving = False
-
- def configure(self, options, updated):
- if "keepserving" in updated:
- self.keepserving = options.keepserving
-
def running(self, stdin = sys.stdin):
if not stdin.isatty():
ctx.log.info("Reading from stdin")
@@ -30,5 +23,4 @@ class ReadStdin:
ctx.master.load_flow(i)
except exceptions.FlowReadException as e:
ctx.log.error("Error reading from stdin: %s" % e)
- if not self.keepserving:
- ctx.master.shutdown()
+ ctx.master.addons.trigger("processing_complete")
diff --git a/mitmproxy/addons/serverplayback.py b/mitmproxy/addons/serverplayback.py
index f2b5f206..be2d6f2b 100644
--- a/mitmproxy/addons/serverplayback.py
+++ b/mitmproxy/addons/serverplayback.py
@@ -104,7 +104,7 @@ class ServerPlayback:
def tick(self):
if self.stop and not self.final_flow.live:
- ctx.master.shutdown()
+ ctx.master.addons.trigger("processing_complete")
def request(self, f):
if self.flowmap:
@@ -115,7 +115,7 @@ class ServerPlayback:
if self.options.refresh_server_playback:
response.refresh()
f.response = response
- if not self.flowmap and not self.options.keepserving:
+ if not self.flowmap:
self.final_flow = f
self.stop = True
elif self.options.replay_kill_extra:
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index 036b3d29..5b84ac93 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -79,7 +79,10 @@ class Options(optmanager.OptManager):
)
self.add_option(
"keepserving", bool, False,
- "Continue serving after client playback or file read."
+ """
+ Instructs mitmdump to continue serving after client playback,
+ server playback or file read. This option is ignored by interactive tools, which always keep serving.
+ """
)
self.add_option(
"server", bool, True,
diff --git a/mitmproxy/test/taddons.py b/mitmproxy/test/taddons.py
index 8d6baa12..c3b65e92 100644
--- a/mitmproxy/test/taddons.py
+++ b/mitmproxy/test/taddons.py
@@ -6,16 +6,36 @@ from mitmproxy import proxy
from mitmproxy import eventsequence
+class _AddonWrapper:
+ def __init__(self, master, addons):
+ self.master = master
+ self.addons = addons
+
+ def trigger(self, event, *args, **kwargs):
+ self.master.events.append((event, args, kwargs))
+ return self.addons.trigger(event, *args, **kwargs)
+
+ def __getattr__(self, attr):
+ return getattr(self.addons, attr)
+
+
class RecordingMaster(mitmproxy.master.Master):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.event_log = []
+ self.addons = _AddonWrapper(self, self.addons)
+ self.events = []
+ self.logs = []
+
+ def has_event(self, name):
+ for i in self.events:
+ if i[0] == name:
+ return True
def add_log(self, e, level):
- self.event_log.append((level, e))
+ self.logs.append((level, e))
def clear(self):
- self.event_log = []
+ self.logs = []
class context:
diff --git a/mitmproxy/tools/dump.py b/mitmproxy/tools/dump.py
index be83fb1d..42930a7e 100644
--- a/mitmproxy/tools/dump.py
+++ b/mitmproxy/tools/dump.py
@@ -2,7 +2,7 @@ from mitmproxy import controller
from mitmproxy import addons
from mitmproxy import options
from mitmproxy import master
-from mitmproxy.addons import dumper, termlog, termstatus, readstdin
+from mitmproxy.addons import dumper, termlog, termstatus, readstdin, keepserving
class DumpMaster(master.Master):
@@ -21,7 +21,7 @@ class DumpMaster(master.Master):
self.addons.add(*addons.default_addons())
if with_dumper:
self.addons.add(dumper.Dumper())
- self.addons.add(readstdin.ReadStdin())
+ self.addons.add(readstdin.ReadStdin(), keepserving.KeepServing())
@controller.handler
def log(self, e):
diff --git a/test/mitmproxy/addons/test_check_alpn.py b/test/mitmproxy/addons/test_check_alpn.py
index 2dc0c835..ffaf6cff 100644
--- a/test/mitmproxy/addons/test_check_alpn.py
+++ b/test/mitmproxy/addons/test_check_alpn.py
@@ -12,7 +12,7 @@ class TestCheckALPN:
with taddons.context() as tctx:
a = check_alpn.CheckALPN()
tctx.configure(a)
- assert not any(msg in m for l, m in tctx.master.event_log)
+ assert not any(msg in m for l, m in tctx.master.logs)
def test_check_no_alpn(self, disable_alpn):
msg = 'ALPN support missing'
@@ -20,4 +20,4 @@ class TestCheckALPN:
with taddons.context() as tctx:
a = check_alpn.CheckALPN()
tctx.configure(a)
- assert any(msg in m for l, m in tctx.master.event_log)
+ assert any(msg in m for l, m in tctx.master.logs)
diff --git a/test/mitmproxy/addons/test_check_ca.py b/test/mitmproxy/addons/test_check_ca.py
index fc64621c..859b6d8d 100644
--- a/test/mitmproxy/addons/test_check_ca.py
+++ b/test/mitmproxy/addons/test_check_ca.py
@@ -16,4 +16,4 @@ class TestCheckCA:
tctx.master.server.config.certstore.default_ca.has_expired = mock.MagicMock(return_value=expired)
a = check_ca.CheckCA()
tctx.configure(a)
- assert any(msg in m for l, m in tctx.master.event_log) is expired
+ assert any(msg in m for l, m in tctx.master.logs) is expired
diff --git a/test/mitmproxy/addons/test_clientplayback.py b/test/mitmproxy/addons/test_clientplayback.py
index c22b3589..f71662f0 100644
--- a/test/mitmproxy/addons/test_clientplayback.py
+++ b/test/mitmproxy/addons/test_clientplayback.py
@@ -23,7 +23,7 @@ class MockThread():
class TestClientPlayback:
def test_playback(self):
cp = clientplayback.ClientPlayback()
- with taddons.context():
+ with taddons.context() as tctx:
assert cp.count() == 0
f = tflow.tflow(resp=True)
cp.load([f])
@@ -35,17 +35,14 @@ class TestClientPlayback:
assert rp.called
assert cp.current_thread
- cp.keepserving = False
cp.flows = None
cp.current_thread = None
- with mock.patch("mitmproxy.master.Master.shutdown") as sd:
- cp.tick()
- assert sd.called
+ cp.tick()
+ assert tctx.master.has_event("processing_complete")
cp.current_thread = MockThread()
- with mock.patch("mitmproxy.master.Master.shutdown") as sd:
- cp.tick()
- assert cp.current_thread is None
+ cp.tick()
+ assert cp.current_thread is None
def test_configure(self, tmpdir):
cp = clientplayback.ClientPlayback()
diff --git a/test/mitmproxy/addons/test_dumper.py b/test/mitmproxy/addons/test_dumper.py
index 47374617..23299431 100644
--- a/test/mitmproxy/addons/test_dumper.py
+++ b/test/mitmproxy/addons/test_dumper.py
@@ -151,7 +151,7 @@ class TestContentView:
with taddons.context(options=options.Options()) as ctx:
ctx.configure(d, flow_detail=4, verbosity=3)
d.response(tflow.tflow())
- assert "Content viewer failed" in ctx.master.event_log[0][1]
+ assert "Content viewer failed" in ctx.master.logs[0][1]
def test_tcp():
diff --git a/test/mitmproxy/addons/test_keepserving.py b/test/mitmproxy/addons/test_keepserving.py
new file mode 100644
index 00000000..70f7e1e6
--- /dev/null
+++ b/test/mitmproxy/addons/test_keepserving.py
@@ -0,0 +1,10 @@
+from mitmproxy.addons import keepserving
+from mitmproxy.test import taddons
+
+
+def test_keepserving():
+ ks = keepserving.KeepServing()
+
+ with taddons.context() as tctx:
+ ks.event_processing_complete()
+ assert tctx.master.should_exit.is_set()
diff --git a/test/mitmproxy/addons/test_readfile.py b/test/mitmproxy/addons/test_readfile.py
index c0cf97ae..b30c147b 100644
--- a/test/mitmproxy/addons/test_readfile.py
+++ b/test/mitmproxy/addons/test_readfile.py
@@ -32,13 +32,13 @@ def test_configure(mck, tmpdir):
with taddons.context() as tctx:
tf = str(tmpdir.join("tfile"))
write_data(tf)
- tctx.configure(rf, rfile=str(tf), keepserving=False)
+ tctx.configure(rf, rfile=str(tf))
assert not mck.called
rf.running()
assert mck.called
write_data(tf, corrupt=True)
- tctx.configure(rf, rfile=str(tf), keepserving=False)
+ tctx.configure(rf, rfile=str(tf))
with pytest.raises(exceptions.OptionsError):
rf.running()
@@ -51,7 +51,7 @@ def test_corruption(mck, tmpdir):
with pytest.raises(exceptions.FlowReadException):
rf.load_flows_file("nonexistent")
assert not mck.called
- assert len(tctx.master.event_log) == 1
+ assert len(tctx.master.logs) == 1
tfc = str(tmpdir.join("tfile"))
write_data(tfc, corrupt=True)
@@ -59,4 +59,4 @@ def test_corruption(mck, tmpdir):
with pytest.raises(exceptions.FlowReadException):
rf.load_flows_file(tfc)
assert mck.called
- assert len(tctx.master.event_log) == 2
+ assert len(tctx.master.logs) == 2
diff --git a/test/mitmproxy/addons/test_readstdin.py b/test/mitmproxy/addons/test_readstdin.py
index bbef81fc..76b01f4f 100644
--- a/test/mitmproxy/addons/test_readstdin.py
+++ b/test/mitmproxy/addons/test_readstdin.py
@@ -26,12 +26,6 @@ def gen_data(corrupt=False):
return tf
-def test_configure(tmpdir):
- rf = readstdin.ReadStdin()
- with taddons.context() as tctx:
- tctx.configure(rf, keepserving=False)
-
-
class mStdin:
def __init__(self, d):
self.buffer = d
@@ -49,11 +43,11 @@ def test_read(m, tmpdir):
assert m.called
rf.running(stdin=mStdin(None))
- assert tctx.master.event_log
+ assert tctx.master.logs
tctx.master.clear()
m.reset_mock()
assert not m.called
rf.running(stdin=mStdin(gen_data(corrupt=True)))
assert m.called
- assert tctx.master.event_log
+ assert tctx.master.logs
diff --git a/test/mitmproxy/addons/test_replace.py b/test/mitmproxy/addons/test_replace.py
index 7d590b35..9002afb5 100644
--- a/test/mitmproxy/addons/test_replace.py
+++ b/test/mitmproxy/addons/test_replace.py
@@ -97,6 +97,6 @@ class TestReplaceFile:
tmpfile.remove()
f = tflow.tflow()
f.request.content = b"foo"
- assert not tctx.master.event_log
+ assert not tctx.master.logs
r.request(f)
- assert tctx.master.event_log
+ assert tctx.master.logs
diff --git a/test/mitmproxy/addons/test_script.py b/test/mitmproxy/addons/test_script.py
index 4c1b2e43..ad3c9a1a 100644
--- a/test/mitmproxy/addons/test_script.py
+++ b/test/mitmproxy/addons/test_script.py
@@ -22,14 +22,14 @@ def test_scriptenv():
with taddons.context() as tctx:
with script.scriptenv("path", []):
raise SystemExit
- assert tctx.master.event_log[0][0] == "error"
- assert "exited" in tctx.master.event_log[0][1]
+ assert tctx.master.logs[0][0] == "error"
+ assert "exited" in tctx.master.logs[0][1]
tctx.master.clear()
with script.scriptenv("path", []):
raise ValueError("fooo")
- assert tctx.master.event_log[0][0] == "error"
- assert "foo" in tctx.master.event_log[0][1]
+ assert tctx.master.logs[0][0] == "error"
+ assert "foo" in tctx.master.logs[0][1]
class Called:
@@ -135,7 +135,7 @@ class TestScript:
f.write(".")
sc.tick()
time.sleep(0.1)
- if tctx.master.event_log:
+ if tctx.master.logs:
return
raise AssertionError("Change event not detected.")
@@ -147,11 +147,11 @@ class TestScript:
sc.start(tctx.options)
f = tflow.tflow(resp=True)
sc.request(f)
- assert tctx.master.event_log[0][0] == "error"
- assert len(tctx.master.event_log[0][1].splitlines()) == 6
- assert re.search(r'addonscripts[\\/]error.py", line \d+, in request', tctx.master.event_log[0][1])
- assert re.search(r'addonscripts[\\/]error.py", line \d+, in mkerr', tctx.master.event_log[0][1])
- assert tctx.master.event_log[0][1].endswith("ValueError: Error!\n")
+ assert tctx.master.logs[0][0] == "error"
+ assert len(tctx.master.logs[0][1].splitlines()) == 6
+ assert re.search(r'addonscripts[\\/]error.py", line \d+, in request', tctx.master.logs[0][1])
+ assert re.search(r'addonscripts[\\/]error.py", line \d+, in mkerr', tctx.master.logs[0][1])
+ assert tctx.master.logs[0][1].endswith("ValueError: Error!\n")
def test_addon(self):
with taddons.context() as tctx:
@@ -256,7 +256,7 @@ class TestScriptLoader:
"%s %s" % (rec, "c"),
]
)
- debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
+ debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
assert debug == [
('debug', 'a start'),
('debug', 'a configure'),
@@ -270,7 +270,7 @@ class TestScriptLoader:
('debug', 'c configure'),
('debug', 'c running'),
]
- tctx.master.event_log = []
+ tctx.master.logs = []
tctx.configure(
sc,
scripts = [
@@ -279,11 +279,11 @@ class TestScriptLoader:
"%s %s" % (rec, "b"),
]
)
- debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
+ debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
# No events, only order has changed
assert debug == []
- tctx.master.event_log = []
+ tctx.master.logs = []
tctx.configure(
sc,
scripts = [
@@ -291,7 +291,7 @@ class TestScriptLoader:
"%s %s" % (rec, "a"),
]
)
- debug = [(i[0], i[1]) for i in tctx.master.event_log if i[0] == "debug"]
+ debug = [(i[0], i[1]) for i in tctx.master.logs if i[0] == "debug"]
assert debug == [
('debug', 'c done'),
('debug', 'b done'),
diff --git a/test/mitmproxy/addons/test_serverplayback.py b/test/mitmproxy/addons/test_serverplayback.py
index 54e4d281..02642c35 100644
--- a/test/mitmproxy/addons/test_serverplayback.py
+++ b/test/mitmproxy/addons/test_serverplayback.py
@@ -34,7 +34,7 @@ def test_tick():
s.final_flow = tflow.tflow()
s.final_flow.live = False
s.tick()
- assert tctx.master.should_exit.is_set()
+ assert tctx.master.has_event("processing_complete")
def test_server_playback():
@@ -315,7 +315,6 @@ def test_server_playback_full():
tctx.configure(
s,
refresh_server_playback = True,
- keepserving=False
)
f = tflow.tflow()
diff --git a/test/mitmproxy/addons/test_termstatus.py b/test/mitmproxy/addons/test_termstatus.py
index 01c14814..7becc857 100644
--- a/test/mitmproxy/addons/test_termstatus.py
+++ b/test/mitmproxy/addons/test_termstatus.py
@@ -6,7 +6,7 @@ def test_configure():
ts = termstatus.TermStatus()
with taddons.context() as ctx:
ts.running()
- assert not ctx.master.event_log
+ assert not ctx.master.logs
ctx.configure(ts, server=True)
ts.running()
- assert ctx.master.event_log
+ assert ctx.master.logs
diff --git a/test/mitmproxy/script/test_concurrent.py b/test/mitmproxy/script/test_concurrent.py
index a9b6f0c4..206482e2 100644
--- a/test/mitmproxy/script/test_concurrent.py
+++ b/test/mitmproxy/script/test_concurrent.py
@@ -43,7 +43,7 @@ class TestConcurrent(tservers.MasterTest):
)
)
sc.start(tctx.options)
- assert "decorator not supported" in tctx.master.event_log[0][1]
+ assert "decorator not supported" in tctx.master.logs[0][1]
def test_concurrent_class(self):
with taddons.context() as tctx:
diff --git a/test/mitmproxy/test_addonmanager.py b/test/mitmproxy/test_addonmanager.py
index ef34371f..e7be25b8 100644
--- a/test/mitmproxy/test_addonmanager.py
+++ b/test/mitmproxy/test_addonmanager.py
@@ -11,6 +11,7 @@ class TAddon:
def __init__(self, name):
self.name = name
self.tick = True
+ self.custom_called = False
def __repr__(self):
return "Addon(%s)" % self.name
@@ -18,11 +19,17 @@ class TAddon:
def done(self):
pass
+ def event_custom(self):
+ self.custom_called = True
+
def test_simple():
o = options.Options()
m = master.Master(o, proxy.DummyServer(o))
a = addonmanager.AddonManager(m)
+ with pytest.raises(exceptions.AddonError):
+ a.invoke_addon(TAddon("one"), "done")
+
a.add(TAddon("one"))
assert a.get("one")
assert not a.get("two")
@@ -33,3 +40,8 @@ def test_simple():
a.trigger("done")
with pytest.raises(exceptions.AddonError):
a.trigger("tick")
+
+ ta = TAddon("one")
+ a.add(ta)
+ a.trigger("custom")
+ assert ta.custom_called