mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-02-07 02:28:50 +00:00
Enable custom options for addons
- Add an options parameter to the start() event. This is to be used by addons on startup to add custom options. - Add a running() event that is called once the proxy is up and running. - With the new paradigm we can't log during master __init__, so add a tiny termstatus addon to print proxy status to terminal once we're running.
This commit is contained in:
parent
ee65894d40
commit
0c6663d0d5
@ -1,11 +1,12 @@
|
|||||||
"""
|
"""
|
||||||
This script makes it possible to use mitmproxy in scenarios where IP spoofing has been used to redirect
|
This script makes it possible to use mitmproxy in scenarios where IP spoofing
|
||||||
connections to mitmproxy. The way this works is that we rely on either the TLS Server Name Indication (SNI) or the
|
has been used to redirect connections to mitmproxy. The way this works is that
|
||||||
Host header of the HTTP request.
|
we rely on either the TLS Server Name Indication (SNI) or the Host header of the
|
||||||
Of course, this is not foolproof - if an HTTPS connection comes without SNI, we don't
|
HTTP request. Of course, this is not foolproof - if an HTTPS connection comes
|
||||||
know the actual target and cannot construct a certificate that looks valid.
|
without SNI, we don't know the actual target and cannot construct a certificate
|
||||||
Similarly, if there's no Host header or a spoofed Host header, we're out of luck as well.
|
that looks valid. Similarly, if there's no Host header or a spoofed Host header,
|
||||||
Using transparent mode is the better option most of the time.
|
we're out of luck as well. Using transparent mode is the better option most of
|
||||||
|
the time.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
mitmproxy
|
mitmproxy
|
||||||
@ -53,5 +54,5 @@ class Rerouter:
|
|||||||
flow.request.port = port
|
flow.request.port = port
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
return Rerouter()
|
return Rerouter()
|
||||||
|
@ -25,7 +25,7 @@ HAR = {}
|
|||||||
SERVERS_SEEN = set()
|
SERVERS_SEEN = set()
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
"""
|
"""
|
||||||
Called once on script startup before any other events.
|
Called once on script startup before any other events.
|
||||||
"""
|
"""
|
||||||
|
@ -14,6 +14,6 @@ Usage:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
import pydevd
|
import pydevd
|
||||||
pydevd.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True)
|
pydevd.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True)
|
||||||
|
@ -112,7 +112,7 @@ class TlsFeedback(TlsLayer):
|
|||||||
tls_strategy = None
|
tls_strategy = None
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
global tls_strategy
|
global tls_strategy
|
||||||
if len(sys.argv) == 2:
|
if len(sys.argv) == 2:
|
||||||
tls_strategy = ProbabilisticStrategy(float(sys.argv[1]))
|
tls_strategy = ProbabilisticStrategy(float(sys.argv[1]))
|
||||||
|
@ -3,5 +3,5 @@ class AddHeader:
|
|||||||
flow.response.headers["newheader"] = "foo"
|
flow.response.headers["newheader"] = "foo"
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
return AddHeader()
|
return AddHeader()
|
||||||
|
@ -20,7 +20,7 @@ class ViewSwapCase(contentviews.View):
|
|||||||
view = ViewSwapCase()
|
view = ViewSwapCase()
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
contentviews.add(view)
|
contentviews.add(view)
|
||||||
|
|
||||||
|
|
||||||
|
10
examples/simple/custom_option.py
Normal file
10
examples/simple/custom_option.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
|
def start(options):
|
||||||
|
ctx.log.info("Registering option 'custom'")
|
||||||
|
options.add_option("custom", str, "default", "A custom option")
|
||||||
|
|
||||||
|
|
||||||
|
def configure(options, updated):
|
||||||
|
ctx.log.info("custom option value: %s" % options.custom)
|
@ -17,7 +17,7 @@ class Filter:
|
|||||||
print(flow)
|
print(flow)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
raise ValueError("Usage: -s 'filt.py FILTER'")
|
raise ValueError("Usage: -s 'filt.py FILTER'")
|
||||||
return Filter(sys.argv[1])
|
return Filter(sys.argv[1])
|
||||||
|
@ -23,7 +23,7 @@ class Writer:
|
|||||||
self.w.add(flow)
|
self.w.add(flow)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
raise ValueError('Usage: -s "flowriter.py filename"')
|
raise ValueError('Usage: -s "flowriter.py filename"')
|
||||||
return Writer(sys.argv[1])
|
return 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
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
ctx.log.info("This is some informative text.")
|
ctx.log.info("This is some informative text.")
|
||||||
ctx.log.error("This is an error.")
|
ctx.log.error("This is an error.")
|
||||||
|
@ -23,7 +23,7 @@ class Injector:
|
|||||||
flow.response.content = str(html).encode("utf8")
|
flow.response.content = str(html).encode("utf8")
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
raise ValueError('Usage: -s "iframe_injector.py url"')
|
raise ValueError('Usage: -s "iframe_injector.py url"')
|
||||||
return Injector(sys.argv[1])
|
return Injector(sys.argv[1])
|
||||||
|
@ -9,7 +9,7 @@ class Replacer:
|
|||||||
flow.response.replace(self.src, self.dst)
|
flow.response.replace(self.src, self.dst)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("src", type=str)
|
parser.add_argument("src", type=str)
|
||||||
parser.add_argument("dst", type=str)
|
parser.add_argument("dst", type=str)
|
||||||
|
@ -14,7 +14,7 @@ def hello_world():
|
|||||||
return 'Hello World!'
|
return 'Hello World!'
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
# Host app at the magic domain "proxapp" on port 80. Requests to this
|
# 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.
|
# domain and port combination will now be routed to the WSGI app instance.
|
||||||
return wsgiapp.WSGIApp(app, "proxapp", 80)
|
return wsgiapp.WSGIApp(app, "proxapp", 80)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy import eventsequence
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ class AddonManager:
|
|||||||
def __init__(self, master):
|
def __init__(self, master):
|
||||||
self.chain = []
|
self.chain = []
|
||||||
self.master = master
|
self.master = master
|
||||||
master.options.changed.connect(self._options_update)
|
master.options.changed.connect(self.configure_all)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
@ -29,22 +30,14 @@ class AddonManager:
|
|||||||
if name == _get_name(i):
|
if name == _get_name(i):
|
||||||
return i
|
return i
|
||||||
|
|
||||||
def _options_update(self, options, updated):
|
def configure_all(self, options, updated):
|
||||||
for i in self.chain:
|
self.invoke_all_with_context("configure", options, updated)
|
||||||
with self.master.handlecontext():
|
|
||||||
self.invoke_with_context(i, "configure", options, updated)
|
|
||||||
|
|
||||||
def startup(self, s):
|
def startup(self, s):
|
||||||
"""
|
"""
|
||||||
Run startup events on addon.
|
Run startup events on addon.
|
||||||
"""
|
"""
|
||||||
self.invoke_with_context(s, "start")
|
self.invoke_with_context(s, "start", self.master.options)
|
||||||
self.invoke_with_context(
|
|
||||||
s,
|
|
||||||
"configure",
|
|
||||||
self.master.options,
|
|
||||||
self.master.options.keys()
|
|
||||||
)
|
|
||||||
|
|
||||||
def add(self, *addons):
|
def add(self, *addons):
|
||||||
"""
|
"""
|
||||||
@ -62,8 +55,7 @@ class AddonManager:
|
|||||||
self.invoke_with_context(addon, "done")
|
self.invoke_with_context(addon, "done")
|
||||||
|
|
||||||
def done(self):
|
def done(self):
|
||||||
for i in self.chain:
|
self.invoke_all_with_context("done")
|
||||||
self.invoke_with_context(i, "done")
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.chain)
|
return len(self.chain)
|
||||||
@ -75,7 +67,14 @@ class AddonManager:
|
|||||||
with self.master.handlecontext():
|
with self.master.handlecontext():
|
||||||
self.invoke(addon, name, *args, **kwargs)
|
self.invoke(addon, name, *args, **kwargs)
|
||||||
|
|
||||||
|
def invoke_all_with_context(self, name, *args, **kwargs):
|
||||||
|
with self.master.handlecontext():
|
||||||
|
for i in self.chain:
|
||||||
|
self.invoke(i, name, *args, **kwargs)
|
||||||
|
|
||||||
def invoke(self, addon, name, *args, **kwargs):
|
def invoke(self, addon, name, *args, **kwargs):
|
||||||
|
if name not in eventsequence.Events: # prama: no cover
|
||||||
|
raise NotImplementedError("Unknown event")
|
||||||
func = getattr(addon, name, None)
|
func = getattr(addon, name, None)
|
||||||
if func:
|
if func:
|
||||||
if not callable(func):
|
if not callable(func):
|
||||||
|
@ -170,22 +170,23 @@ class Script:
|
|||||||
|
|
||||||
def load_script(self):
|
def load_script(self):
|
||||||
self.ns = load_script(self.path, self.args)
|
self.ns = load_script(self.path, self.args)
|
||||||
ret = self.run("start")
|
ret = self.run("start", self.last_options)
|
||||||
if ret:
|
if ret:
|
||||||
self.ns = ret
|
self.ns = ret
|
||||||
self.run("start")
|
self.run("start", self.last_options)
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
if self.should_reload.is_set():
|
if self.should_reload.is_set():
|
||||||
self.should_reload.clear()
|
self.should_reload.clear()
|
||||||
ctx.log.info("Reloading script: %s" % self.name)
|
ctx.log.info("Reloading script: %s" % self.name)
|
||||||
self.ns = load_script(self.path, self.args)
|
self.ns = load_script(self.path, self.args)
|
||||||
self.start()
|
self.start(self.last_options)
|
||||||
self.configure(self.last_options, self.last_options.keys())
|
self.configure(self.last_options, self.last_options.keys())
|
||||||
else:
|
else:
|
||||||
self.run("tick")
|
self.run("tick")
|
||||||
|
|
||||||
def start(self):
|
def start(self, opts):
|
||||||
|
self.last_options = opts
|
||||||
self.load_script()
|
self.load_script()
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, options, updated):
|
||||||
@ -209,6 +210,12 @@ class ScriptLoader:
|
|||||||
"""
|
"""
|
||||||
An addon that manages loading scripts from options.
|
An addon that manages loading scripts from options.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
|
def running(self):
|
||||||
|
self.is_running = True
|
||||||
|
|
||||||
def run_once(self, command, flows):
|
def run_once(self, command, flows):
|
||||||
try:
|
try:
|
||||||
sc = Script(command)
|
sc = Script(command)
|
||||||
@ -267,3 +274,10 @@ class ScriptLoader:
|
|||||||
|
|
||||||
for s in newscripts:
|
for s in newscripts:
|
||||||
ctx.master.addons.startup(s)
|
ctx.master.addons.startup(s)
|
||||||
|
if self.is_running:
|
||||||
|
# If we're already running, we configure and tell the addon
|
||||||
|
# we're up and running.
|
||||||
|
ctx.master.addons.invoke_with_context(
|
||||||
|
s, "configure", options, options.keys()
|
||||||
|
)
|
||||||
|
ctx.master.addons.invoke_with_context(s, "running")
|
||||||
|
23
mitmproxy/addons/termstatus.py
Normal file
23
mitmproxy/addons/termstatus.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
"""
|
||||||
|
A tiny addon to print the proxy status to terminal. Eventually this could
|
||||||
|
also print some stats on exit.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TermStatus:
|
||||||
|
def __init__(self):
|
||||||
|
self.server = False
|
||||||
|
|
||||||
|
def configure(self, options, updated):
|
||||||
|
if "server" in updated:
|
||||||
|
self.server = options.server
|
||||||
|
|
||||||
|
def running(self):
|
||||||
|
if self.server:
|
||||||
|
ctx.log.info(
|
||||||
|
"Proxy server listening at http://{}:{}".format(
|
||||||
|
*ctx.master.server.address,
|
||||||
|
)
|
||||||
|
)
|
@ -33,6 +33,7 @@ Events = frozenset([
|
|||||||
"done",
|
"done",
|
||||||
"log",
|
"log",
|
||||||
"start",
|
"start",
|
||||||
|
"running",
|
||||||
"tick",
|
"tick",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -76,12 +76,16 @@ class Master:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.start()
|
self.start()
|
||||||
|
running = False
|
||||||
try:
|
try:
|
||||||
while not self.should_exit.is_set():
|
while not self.should_exit.is_set():
|
||||||
# Don't choose a very small timeout in Python 2:
|
# Don't choose a very small timeout in Python 2:
|
||||||
# https://github.com/mitmproxy/mitmproxy/issues/443
|
# https://github.com/mitmproxy/mitmproxy/issues/443
|
||||||
# TODO: Lower the timeout value if we move to Python 3.
|
# TODO: Lower the timeout value if we move to Python 3.
|
||||||
self.tick(0.1)
|
self.tick(0.1)
|
||||||
|
if not running:
|
||||||
|
running = True
|
||||||
|
self.addons.invoke_all_with_context("running")
|
||||||
finally:
|
finally:
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
|
@ -324,7 +324,15 @@ class OptManager:
|
|||||||
options=options
|
options=options
|
||||||
)
|
)
|
||||||
|
|
||||||
def set(self, spec):
|
def set(self, *spec):
|
||||||
|
vals = {}
|
||||||
|
for i in spec:
|
||||||
|
vals.update(self._setspec(i))
|
||||||
|
self.update(**vals)
|
||||||
|
|
||||||
|
def _setspec(self, spec):
|
||||||
|
d = {}
|
||||||
|
|
||||||
parts = spec.split("=", maxsplit=1)
|
parts = spec.split("=", maxsplit=1)
|
||||||
if len(parts) == 1:
|
if len(parts) == 1:
|
||||||
optname, optval = parts[0], None
|
optname, optval = parts[0], None
|
||||||
@ -333,14 +341,14 @@ class OptManager:
|
|||||||
o = self._options[optname]
|
o = self._options[optname]
|
||||||
|
|
||||||
if o.typespec in (str, typing.Optional[str]):
|
if o.typespec in (str, typing.Optional[str]):
|
||||||
setattr(self, optname, optval)
|
d[optname] = optval
|
||||||
elif o.typespec in (int, typing.Optional[int]):
|
elif o.typespec in (int, typing.Optional[int]):
|
||||||
if optval:
|
if optval:
|
||||||
try:
|
try:
|
||||||
optval = int(optval)
|
optval = int(optval)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise exceptions.OptionsError("Not an integer: %s" % optval)
|
raise exceptions.OptionsError("Not an integer: %s" % optval)
|
||||||
setattr(self, optname, optval)
|
d[optname] = optval
|
||||||
elif o.typespec == bool:
|
elif o.typespec == bool:
|
||||||
if not optval or optval == "true":
|
if not optval or optval == "true":
|
||||||
v = True
|
v = True
|
||||||
@ -350,18 +358,15 @@ class OptManager:
|
|||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Boolean must be \"true\", \"false\", or have the value " "omitted (a synonym for \"true\")."
|
"Boolean must be \"true\", \"false\", or have the value " "omitted (a synonym for \"true\")."
|
||||||
)
|
)
|
||||||
setattr(self, optname, v)
|
d[optname] = v
|
||||||
elif o.typespec == typing.Sequence[str]:
|
elif o.typespec == typing.Sequence[str]:
|
||||||
if not optval:
|
if not optval:
|
||||||
setattr(self, optname, [])
|
d[optname] = []
|
||||||
else:
|
else:
|
||||||
setattr(
|
d[optname] = getattr(self, optname) + [optval]
|
||||||
self,
|
|
||||||
optname,
|
|
||||||
getattr(self, optname) + [optval]
|
|
||||||
)
|
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
raise NotImplementedError("Unsupported option type: %s", o.typespec)
|
raise NotImplementedError("Unsupported option type: %s", o.typespec)
|
||||||
|
return d
|
||||||
|
|
||||||
def make_parser(self, parser, optname, metavar=None, short=None):
|
def make_parser(self, parser, optname, metavar=None, short=None):
|
||||||
o = self._options[optname]
|
o = self._options[optname]
|
||||||
|
@ -3,7 +3,7 @@ from mitmproxy import exceptions
|
|||||||
from mitmproxy import addons
|
from mitmproxy import addons
|
||||||
from mitmproxy import options
|
from mitmproxy import options
|
||||||
from mitmproxy import master
|
from mitmproxy import master
|
||||||
from mitmproxy.addons import dumper, termlog
|
from mitmproxy.addons import dumper, termlog, termstatus
|
||||||
|
|
||||||
|
|
||||||
class DumpMaster(master.Master):
|
class DumpMaster(master.Master):
|
||||||
@ -18,17 +18,11 @@ class DumpMaster(master.Master):
|
|||||||
master.Master.__init__(self, options, server)
|
master.Master.__init__(self, options, server)
|
||||||
self.has_errored = False
|
self.has_errored = False
|
||||||
if with_termlog:
|
if with_termlog:
|
||||||
self.addons.add(termlog.TermLog())
|
self.addons.add(termlog.TermLog(), termstatus.TermStatus())
|
||||||
self.addons.add(*addons.default_addons())
|
self.addons.add(*addons.default_addons())
|
||||||
if with_dumper:
|
if with_dumper:
|
||||||
self.addons.add(dumper.Dumper())
|
self.addons.add(dumper.Dumper())
|
||||||
|
|
||||||
if self.options.server:
|
|
||||||
self.add_log(
|
|
||||||
"Proxy server listening at http://{}:{}".format(server.address[0], server.address[1]),
|
|
||||||
"info"
|
|
||||||
)
|
|
||||||
|
|
||||||
if options.rfile:
|
if options.rfile:
|
||||||
try:
|
try:
|
||||||
self.load_flows_file(options.rfile)
|
self.load_flows_file(options.rfile)
|
||||||
|
@ -45,9 +45,6 @@ def process_options(parser, opts, args):
|
|||||||
if args.quiet:
|
if args.quiet:
|
||||||
args.flow_detail = 0
|
args.flow_detail = 0
|
||||||
|
|
||||||
for i in args.setoptions:
|
|
||||||
opts.set(i)
|
|
||||||
|
|
||||||
adict = {}
|
adict = {}
|
||||||
for n in dir(args):
|
for n in dir(args):
|
||||||
if n in opts:
|
if n in opts:
|
||||||
@ -77,6 +74,8 @@ def run(MasterKlass, args): # pragma: no cover
|
|||||||
opts.load_paths(args.conf)
|
opts.load_paths(args.conf)
|
||||||
server = process_options(parser, opts, args)
|
server = process_options(parser, opts, args)
|
||||||
master = MasterKlass(opts, server)
|
master = MasterKlass(opts, server)
|
||||||
|
master.addons.configure_all(opts, opts.keys())
|
||||||
|
opts.set(*args.setoptions)
|
||||||
|
|
||||||
def cleankill(*args, **kwargs):
|
def cleankill(*args, **kwargs):
|
||||||
master.shutdown()
|
master.shutdown()
|
||||||
|
@ -116,9 +116,7 @@ class TestScript:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.load_script()
|
sc.load_script()
|
||||||
assert sc.ns.call_log == [
|
assert sc.ns.call_log[0][0:2] == ("solo", "start")
|
||||||
("solo", "start", (), {}),
|
|
||||||
]
|
|
||||||
|
|
||||||
sc.ns.call_log = []
|
sc.ns.call_log = []
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
@ -146,7 +144,7 @@ class TestScript:
|
|||||||
sc = script.Script(
|
sc = script.Script(
|
||||||
tutils.test_data.path("mitmproxy/data/addonscripts/error.py")
|
tutils.test_data.path("mitmproxy/data/addonscripts/error.py")
|
||||||
)
|
)
|
||||||
sc.start()
|
sc.start(tctx.options)
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
sc.request(f)
|
sc.request(f)
|
||||||
assert tctx.master.event_log[0][0] == "error"
|
assert tctx.master.event_log[0][0] == "error"
|
||||||
@ -162,7 +160,7 @@ class TestScript:
|
|||||||
"mitmproxy/data/addonscripts/addon.py"
|
"mitmproxy/data/addonscripts/addon.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.start()
|
sc.start(tctx.options)
|
||||||
tctx.configure(sc)
|
tctx.configure(sc)
|
||||||
assert sc.ns.event_log == [
|
assert sc.ns.event_log == [
|
||||||
'scriptstart', 'addonstart', 'addonconfigure'
|
'scriptstart', 'addonstart', 'addonconfigure'
|
||||||
@ -225,24 +223,31 @@ class TestScriptLoader:
|
|||||||
assert len(m.addons) == 1
|
assert len(m.addons) == 1
|
||||||
|
|
||||||
def test_dupes(self):
|
def test_dupes(self):
|
||||||
o = options.Options(scripts=["one", "one"])
|
|
||||||
m = master.Master(o, proxy.DummyServer())
|
|
||||||
sc = script.ScriptLoader()
|
sc = script.ScriptLoader()
|
||||||
with pytest.raises(exceptions.OptionsError):
|
with taddons.context() as tctx:
|
||||||
m.addons.add(o, sc)
|
tctx.master.addons.add(sc)
|
||||||
|
with pytest.raises(exceptions.OptionsError):
|
||||||
|
tctx.configure(
|
||||||
|
sc,
|
||||||
|
scripts = ["one", "one"]
|
||||||
|
)
|
||||||
|
|
||||||
def test_nonexistent(self):
|
def test_nonexistent(self):
|
||||||
o = options.Options(scripts=["nonexistent"])
|
|
||||||
m = master.Master(o, proxy.DummyServer())
|
|
||||||
sc = script.ScriptLoader()
|
sc = script.ScriptLoader()
|
||||||
with pytest.raises(exceptions.OptionsError):
|
with taddons.context() as tctx:
|
||||||
m.addons.add(o, sc)
|
tctx.master.addons.add(sc)
|
||||||
|
with pytest.raises(exceptions.OptionsError):
|
||||||
|
tctx.configure(
|
||||||
|
sc,
|
||||||
|
scripts = ["nonexistent"]
|
||||||
|
)
|
||||||
|
|
||||||
def test_order(self):
|
def test_order(self):
|
||||||
rec = tutils.test_data.path("mitmproxy/data/addonscripts/recorder.py")
|
rec = tutils.test_data.path("mitmproxy/data/addonscripts/recorder.py")
|
||||||
sc = script.ScriptLoader()
|
sc = script.ScriptLoader()
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
tctx.master.addons.add(sc)
|
tctx.master.addons.add(sc)
|
||||||
|
sc.running()
|
||||||
tctx.configure(
|
tctx.configure(
|
||||||
sc,
|
sc,
|
||||||
scripts = [
|
scripts = [
|
||||||
@ -253,9 +258,17 @@ class TestScriptLoader:
|
|||||||
)
|
)
|
||||||
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.event_log if i[0] == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
('debug', 'a start'), ('debug', 'a configure'),
|
('debug', 'a start'),
|
||||||
('debug', 'b start'), ('debug', 'b configure'),
|
('debug', 'a configure'),
|
||||||
('debug', 'c start'), ('debug', 'c configure')
|
('debug', 'a running'),
|
||||||
|
|
||||||
|
('debug', 'b start'),
|
||||||
|
('debug', 'b configure'),
|
||||||
|
('debug', 'b running'),
|
||||||
|
|
||||||
|
('debug', 'c start'),
|
||||||
|
('debug', 'c configure'),
|
||||||
|
('debug', 'c running'),
|
||||||
]
|
]
|
||||||
tctx.master.event_log = []
|
tctx.master.event_log = []
|
||||||
tctx.configure(
|
tctx.configure(
|
||||||
@ -284,4 +297,5 @@ class TestScriptLoader:
|
|||||||
('debug', 'b done'),
|
('debug', 'b done'),
|
||||||
('debug', 'x start'),
|
('debug', 'x start'),
|
||||||
('debug', 'x configure'),
|
('debug', 'x configure'),
|
||||||
|
('debug', 'x running'),
|
||||||
]
|
]
|
||||||
|
12
test/mitmproxy/addons/test_termstatus.py
Normal file
12
test/mitmproxy/addons/test_termstatus.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from mitmproxy.addons import termstatus
|
||||||
|
from mitmproxy.test import taddons
|
||||||
|
|
||||||
|
|
||||||
|
def test_configure():
|
||||||
|
ts = termstatus.TermStatus()
|
||||||
|
with taddons.context() as ctx:
|
||||||
|
ts.running()
|
||||||
|
assert not ctx.master.event_log
|
||||||
|
ctx.configure(ts, server=True)
|
||||||
|
ts.running()
|
||||||
|
assert ctx.master.event_log
|
@ -6,7 +6,7 @@ class Addon:
|
|||||||
def event_log(self):
|
def event_log(self):
|
||||||
return event_log
|
return event_log
|
||||||
|
|
||||||
def start(self):
|
def start(self, opts):
|
||||||
event_log.append("addonstart")
|
event_log.append("addonstart")
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, options, updated):
|
||||||
@ -17,6 +17,6 @@ def configure(options, updated):
|
|||||||
event_log.append("addonconfigure")
|
event_log.append("addonconfigure")
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
event_log.append("scriptstart")
|
event_log.append("scriptstart")
|
||||||
return Addon()
|
return Addon()
|
||||||
|
@ -9,5 +9,5 @@ class ConcurrentClass:
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
return ConcurrentClass()
|
return ConcurrentClass()
|
||||||
|
@ -2,5 +2,5 @@ from mitmproxy.script import concurrent
|
|||||||
|
|
||||||
|
|
||||||
@concurrent
|
@concurrent
|
||||||
def start():
|
def start(opts):
|
||||||
pass
|
pass
|
||||||
|
@ -22,5 +22,5 @@ class CallLogger:
|
|||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start(opts):
|
||||||
return CallLogger(*sys.argv[1:])
|
return CallLogger(*sys.argv[1:])
|
||||||
|
@ -302,6 +302,9 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin):
|
|||||||
class TestHTTPAuth(tservers.HTTPProxyTest):
|
class TestHTTPAuth(tservers.HTTPProxyTest):
|
||||||
def test_auth(self):
|
def test_auth(self):
|
||||||
self.master.addons.add(proxyauth.ProxyAuth())
|
self.master.addons.add(proxyauth.ProxyAuth())
|
||||||
|
self.master.addons.configure_all(
|
||||||
|
self.master.options, self.master.options.keys()
|
||||||
|
)
|
||||||
self.master.options.proxyauth = "test:test"
|
self.master.options.proxyauth = "test:test"
|
||||||
assert self.pathod("202").status_code == 407
|
assert self.pathod("202").status_code == 407
|
||||||
p = self.pathoc()
|
p = self.pathoc()
|
||||||
|
@ -24,7 +24,7 @@ class TestConcurrent(tservers.MasterTest):
|
|||||||
"mitmproxy/data/addonscripts/concurrent_decorator.py"
|
"mitmproxy/data/addonscripts/concurrent_decorator.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.start()
|
sc.start(tctx.options)
|
||||||
|
|
||||||
f1, f2 = tflow.tflow(), tflow.tflow()
|
f1, f2 = tflow.tflow(), tflow.tflow()
|
||||||
tctx.cycle(sc, f1)
|
tctx.cycle(sc, f1)
|
||||||
@ -42,7 +42,7 @@ class TestConcurrent(tservers.MasterTest):
|
|||||||
"mitmproxy/data/addonscripts/concurrent_decorator_err.py"
|
"mitmproxy/data/addonscripts/concurrent_decorator_err.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.start()
|
sc.start(tctx.options)
|
||||||
assert "decorator not supported" in tctx.master.event_log[0][1]
|
assert "decorator not supported" in tctx.master.event_log[0][1]
|
||||||
|
|
||||||
def test_concurrent_class(self):
|
def test_concurrent_class(self):
|
||||||
@ -52,7 +52,7 @@ class TestConcurrent(tservers.MasterTest):
|
|||||||
"mitmproxy/data/addonscripts/concurrent_decorator_class.py"
|
"mitmproxy/data/addonscripts/concurrent_decorator_class.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sc.start()
|
sc.start(tctx.options)
|
||||||
|
|
||||||
f1, f2 = tflow.tflow(), tflow.tflow()
|
f1, f2 = tflow.tflow(), tflow.tflow()
|
||||||
tctx.cycle(sc, f1)
|
tctx.cycle(sc, f1)
|
||||||
|
@ -10,12 +10,12 @@ from mitmproxy import proxy
|
|||||||
class TAddon:
|
class TAddon:
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.noop_member = True
|
self.tick = True
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Addon(%s)" % self.name
|
return "Addon(%s)" % self.name
|
||||||
|
|
||||||
def noop(self):
|
def done(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -30,6 +30,6 @@ def test_simple():
|
|||||||
assert not a.chain
|
assert not a.chain
|
||||||
|
|
||||||
a.add(TAddon("one"))
|
a.add(TAddon("one"))
|
||||||
a("noop")
|
a("done")
|
||||||
with pytest.raises(exceptions.AddonError):
|
with pytest.raises(exceptions.AddonError):
|
||||||
a("noop_member")
|
a("tick")
|
||||||
|
@ -28,7 +28,9 @@ class TestMaster(tservers.MasterTest):
|
|||||||
if "verbosity" not in opts:
|
if "verbosity" not in opts:
|
||||||
opts["verbosity"] = 1
|
opts["verbosity"] = 1
|
||||||
o = options.Options(**opts)
|
o = options.Options(**opts)
|
||||||
return console.master.ConsoleMaster(o, proxy.DummyServer())
|
m = console.master.ConsoleMaster(o, proxy.DummyServer())
|
||||||
|
m.addons.configure_all(o, o.keys())
|
||||||
|
return m
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
m = self.mkmaster()
|
m = self.mkmaster()
|
||||||
|
@ -79,6 +79,8 @@ class TestMaster(master.Master):
|
|||||||
self.state = TestState()
|
self.state = TestState()
|
||||||
self.addons.add(self.state)
|
self.addons.add(self.state)
|
||||||
self.addons.add(*addons)
|
self.addons.add(*addons)
|
||||||
|
self.addons.configure_all(self.options, self.options.keys())
|
||||||
|
self.addons.invoke_all_with_context("running")
|
||||||
|
|
||||||
def clear_log(self):
|
def clear_log(self):
|
||||||
self.tlog = []
|
self.tlog = []
|
||||||
|
Loading…
Reference in New Issue
Block a user