diff options
43 files changed, 380 insertions, 274 deletions
diff --git a/.travis.yml b/.travis.yml index e832d058..e9566ebe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,10 +20,10 @@ matrix: include: - python: 3.5 env: TOXENV=lint -# - os: osx -# osx_image: xcode7.3 -# language: generic -# env: TOXENV=py35 + - os: osx + osx_image: xcode7.3 + language: generic + env: TOXENV=py35 - python: 3.5 env: TOXENV=py35 - python: 3.5 diff --git a/examples/filt.py b/examples/filt.py index 21744edd..9ccf9fa1 100644 --- a/examples/filt.py +++ b/examples/filt.py @@ -1,18 +1,20 @@ -# This scripts demonstrates how to use mitmproxy's filter pattern in inline scripts. +# This scripts demonstrates how to use mitmproxy's filter pattern in scripts. # Usage: mitmdump -s "filt.py FILTER" import sys from mitmproxy import filt -state = {} + +class Filter: + def __init__(self, spec): + self.filter = filt.parse(spec) + + def response(self, flow): + if flow.match(self.filter): + print("Flow matches filter:") + print(flow) def start(): if len(sys.argv) != 2: raise ValueError("Usage: -s 'filt.py FILTER'") - state["filter"] = filt.parse(sys.argv[1]) - - -def response(flow): - if flow.match(state["filter"]): - print("Flow matches filter:") - print(flow) + return Filter(sys.argv[1]) diff --git a/examples/flowwriter.py b/examples/flowwriter.py index 07c7ca20..df2e5a40 100644 --- a/examples/flowwriter.py +++ b/examples/flowwriter.py @@ -3,20 +3,21 @@ import sys from mitmproxy.flow import FlowWriter -state = {} + +class Writer: + def __init__(self, path): + if path == "-": + f = sys.stdout + else: + f = open(path, "wb") + self.w = FlowWriter(f) + + def response(self, flow): + if random.choice([True, False]): + self.w.add(flow) def start(): if len(sys.argv) != 2: raise ValueError('Usage: -s "flowriter.py filename"') - - if sys.argv[1] == "-": - f = sys.stdout - else: - f = open(sys.argv[1], "wb") - state["flow_writer"] = FlowWriter(f) - - -def response(flow): - if random.choice([True, False]): - state["flow_writer"].add(flow) + return Writer(sys.argv[1]) diff --git a/examples/iframe_injector.py b/examples/iframe_injector.py index 352c3c24..33d18bbd 100644 --- a/examples/iframe_injector.py +++ b/examples/iframe_injector.py @@ -3,26 +3,27 @@ import sys from bs4 import BeautifulSoup -iframe_url = None + +class Injector: + def __init__(self, iframe_url): + self.iframe_url = iframe_url + + def response(self, flow): + if flow.request.host in self.iframe_url: + return + html = BeautifulSoup(flow.response.content, "lxml") + if html.body: + iframe = html.new_tag( + "iframe", + src=self.iframe_url, + frameborder=0, + height=0, + width=0) + html.body.insert(0, iframe) + flow.response.content = str(html).encode("utf8") def start(): if len(sys.argv) != 2: raise ValueError('Usage: -s "iframe_injector.py url"') - global iframe_url - iframe_url = sys.argv[1] - - -def response(flow): - if flow.request.host in iframe_url: - return - html = BeautifulSoup(flow.response.content, "lxml") - if html.body: - iframe = html.new_tag( - "iframe", - src=iframe_url, - frameborder=0, - height=0, - width=0) - html.body.insert(0, iframe) - flow.response.content = str(html).encode("utf8") + return Injector(sys.argv[1]) diff --git a/examples/stub.py b/examples/stub.py index e5b4a39a..4f5061e2 100644 --- a/examples/stub.py +++ b/examples/stub.py @@ -11,7 +11,7 @@ def start(): mitmproxy.ctx.log("start") -def configure(options): +def configure(options, updated): """ Called once on script startup before any other events, and whenever options changes. """ diff --git a/mitmproxy/addons.py b/mitmproxy/addons.py index c779aaf8..a4bea9fa 100644 --- a/mitmproxy/addons.py +++ b/mitmproxy/addons.py @@ -13,16 +13,23 @@ class Addons(object): self.master = master master.options.changed.connect(self.options_update) - def options_update(self, options): + def options_update(self, options, updated): for i in self.chain: with self.master.handlecontext(): - i.configure(options) + i.configure(options, updated) - def add(self, *addons): + def add(self, options, *addons): + if not addons: + raise ValueError("No adons specified.") self.chain.extend(addons) for i in addons: self.invoke_with_context(i, "start") - self.invoke_with_context(i, "configure", self.master.options) + self.invoke_with_context( + i, + "configure", + self.master.options, + self.master.options.keys() + ) def remove(self, addon): self.chain = [i for i in self.chain if i is not addon] diff --git a/mitmproxy/builtins/anticache.py b/mitmproxy/builtins/anticache.py index f208e2fb..41a5ed95 100644 --- a/mitmproxy/builtins/anticache.py +++ b/mitmproxy/builtins/anticache.py @@ -5,7 +5,7 @@ class AntiCache: def __init__(self): self.enabled = False - def configure(self, options): + def configure(self, options, updated): self.enabled = options.anticache def request(self, flow): diff --git a/mitmproxy/builtins/anticomp.py b/mitmproxy/builtins/anticomp.py index 50bd1b73..823e960c 100644 --- a/mitmproxy/builtins/anticomp.py +++ b/mitmproxy/builtins/anticomp.py @@ -5,7 +5,7 @@ class AntiComp: def __init__(self): self.enabled = False - def configure(self, options): + def configure(self, options, updated): self.enabled = options.anticomp def request(self, flow): diff --git a/mitmproxy/builtins/dumper.py b/mitmproxy/builtins/dumper.py index 239630fb..74c2e6b2 100644 --- a/mitmproxy/builtins/dumper.py +++ b/mitmproxy/builtins/dumper.py @@ -5,6 +5,8 @@ import traceback import click +import typing # noqa + from mitmproxy import contentviews from mitmproxy import ctx from mitmproxy import exceptions @@ -19,12 +21,25 @@ def indent(n, text): return "\n".join(pad + i for i in l) -class Dumper(): +class Dumper(object): def __init__(self): - self.filter = None - self.flow_detail = None - self.outfp = None - self.showhost = None + self.filter = None # type: filt.TFilter + self.flow_detail = None # type: int + self.outfp = None # type: typing.io.TextIO + self.showhost = None # type: bool + + def configure(self, options, updated): + if options.filtstr: + self.filter = filt.parse(options.filtstr) + if not self.filter: + raise exceptions.OptionsError( + "Invalid filter expression: %s" % options.filtstr + ) + else: + self.filter = None + self.flow_detail = options.flow_detail + self.outfp = options.tfile + self.showhost = options.showhost def echo(self, text, ident=None, **style): if ident: @@ -59,7 +74,7 @@ class Dumper(): self.echo("") try: - type, lines = contentviews.get_content_view( + _, lines = contentviews.get_content_view( contentviews.get("Auto"), content, headers=getattr(message, "headers", None) @@ -67,7 +82,7 @@ class Dumper(): except exceptions.ContentViewException: s = "Content viewer failed: \n" + traceback.format_exc() ctx.log.debug(s) - type, lines = contentviews.get_content_view( + _, lines = contentviews.get_content_view( contentviews.get("Raw"), content, headers=getattr(message, "headers", None) @@ -114,9 +129,8 @@ class Dumper(): if flow.client_conn: client = click.style( strutils.escape_control_characters( - flow.client_conn.address.host - ), - bold=True + repr(flow.client_conn.address) + ) ) elif flow.request.is_replay: client = click.style("[replay]", fg="yellow", bold=True) @@ -139,17 +153,23 @@ class Dumper(): url = flow.request.url url = click.style(strutils.escape_control_characters(url), bold=True) - httpversion = "" + http_version = "" if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"): # We hide "normal" HTTP 1. - httpversion = " " + flow.request.http_version + http_version = " " + flow.request.http_version - line = "{stickycookie}{client} {method} {url}{httpversion}".format( - stickycookie=stickycookie, + if self.flow_detail >= 2: + linebreak = "\n " + else: + linebreak = "" + + line = "{client}: {linebreak}{stickycookie}{method} {url}{http_version}".format( client=client, + stickycookie=stickycookie, + linebreak=linebreak, method=method, url=url, - httpversion=httpversion + http_version=http_version ) self.echo(line) @@ -185,9 +205,14 @@ class Dumper(): size = human.pretty_size(len(flow.response.raw_content)) size = click.style(size, bold=True) - arrows = click.style(" <<", bold=True) + arrows = click.style(" <<", bold=True) + if self.flow_detail == 1: + # This aligns the HTTP response code with the HTTP request method: + # 127.0.0.1:59519: GET http://example.com/ + # << 304 Not Modified 0b + arrows = " " * (len(repr(flow.client_conn.address)) - 2) + arrows - line = "{replay} {arrows} {code} {reason} {size}".format( + line = "{replay}{arrows} {code} {reason} {size}".format( replay=replay, arrows=arrows, code=code, @@ -211,25 +236,12 @@ class Dumper(): def match(self, f): if self.flow_detail == 0: return False - if not self.filt: + if not self.filter: return True - elif f.match(self.filt): + elif f.match(self.filter): return True return False - def configure(self, options): - if options.filtstr: - self.filt = filt.parse(options.filtstr) - if not self.filt: - raise exceptions.OptionsError( - "Invalid filter expression: %s" % options.filtstr - ) - else: - self.filt = None - self.flow_detail = options.flow_detail - self.outfp = options.tfile - self.showhost = options.showhost - def response(self, f): if self.match(f): self.echo_flow(f) @@ -239,8 +251,7 @@ class Dumper(): self.echo_flow(f) def tcp_message(self, f): - # FIXME: Filter should be applied here - if self.options.flow_detail == 0: + if not self.match(f): return message = f.messages[-1] direction = "->" if message.from_client else "<-" diff --git a/mitmproxy/builtins/filestreamer.py b/mitmproxy/builtins/filestreamer.py index 97ddc7c4..ffa565ac 100644 --- a/mitmproxy/builtins/filestreamer.py +++ b/mitmproxy/builtins/filestreamer.py @@ -19,7 +19,7 @@ class FileStreamer: self.stream = io.FilteredFlowWriter(f, filt) self.active_flows = set() - def configure(self, options): + def configure(self, options, updated): # We're already streaming - stop the previous stream and restart if self.stream: self.done() diff --git a/mitmproxy/builtins/replace.py b/mitmproxy/builtins/replace.py index 83b96cee..74d30c05 100644 --- a/mitmproxy/builtins/replace.py +++ b/mitmproxy/builtins/replace.py @@ -8,7 +8,7 @@ class Replace: def __init__(self): self.lst = [] - def configure(self, options): + def configure(self, options, updated): """ .replacements is a list of tuples (fpat, rex, s): diff --git a/mitmproxy/builtins/script.py b/mitmproxy/builtins/script.py index ab068e47..c960dd1c 100644 --- a/mitmproxy/builtins/script.py +++ b/mitmproxy/builtins/script.py @@ -16,6 +16,19 @@ import watchdog.events from watchdog.observers import polling +class NS: + def __init__(self, ns): + self.__dict__["ns"] = ns + + def __getattr__(self, key): + if key not in self.ns: + raise AttributeError("No such element: %s", key) + return self.ns[key] + + def __setattr__(self, key, value): + self.__dict__["ns"][key] = value + + def parse_command(command): """ Returns a (path, args) tuple. @@ -74,18 +87,27 @@ def load_script(path, args): ns = {'__file__': os.path.abspath(path)} with scriptenv(path, args): exec(code, ns, ns) - return ns + return NS(ns) class ReloadHandler(watchdog.events.FileSystemEventHandler): def __init__(self, callback): self.callback = callback + def filter(self, event): + if event.is_directory: + return False + if os.path.basename(event.src_path).startswith("."): + return False + return True + def on_modified(self, event): - self.callback() + if self.filter(event): + self.callback() def on_created(self, event): - self.callback() + if self.filter(event): + self.callback() class Script: @@ -118,29 +140,35 @@ class Script: # It's possible for ns to be un-initialised if we failed during # configure if self.ns is not None and not self.dead: - func = self.ns.get(name) + func = getattr(self.ns, name, None) if func: with scriptenv(self.path, self.args): - func(*args, **kwargs) + return func(*args, **kwargs) def reload(self): self.should_reload.set() + def load_script(self): + self.ns = load_script(self.path, self.args) + ret = self.run("start") + if ret: + self.ns = ret + self.run("start") + 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.start() - self.configure(self.last_options) + self.configure(self.last_options, self.last_options.keys()) else: self.run("tick") def start(self): - self.ns = load_script(self.path, self.args) - self.run("start") + self.load_script() - def configure(self, options): + def configure(self, options, updated): self.last_options = options if not self.observer: self.observer = polling.PollingObserver() @@ -150,7 +178,7 @@ class Script: os.path.dirname(self.path) or "." ) self.observer.start() - self.run("configure", options) + self.run("configure", options, updated) def done(self): self.run("done") @@ -161,26 +189,27 @@ class ScriptLoader(): """ An addon that manages loading scripts from options. """ - def configure(self, options): - for s in options.scripts: - if options.scripts.count(s) > 1: - raise exceptions.OptionsError("Duplicate script: %s" % s) - - for a in ctx.master.addons.chain[:]: - if isinstance(a, Script) and a.name not in options.scripts: - ctx.log.info("Un-loading script: %s" % a.name) - ctx.master.addons.remove(a) - - current = {} - for a in ctx.master.addons.chain[:]: - if isinstance(a, Script): - current[a.name] = a - ctx.master.addons.chain.remove(a) - - for s in options.scripts: - if s in current: - ctx.master.addons.chain.append(current[s]) - else: - ctx.log.info("Loading script: %s" % s) - sc = Script(s) - ctx.master.addons.add(sc) + def configure(self, options, updated): + if "scripts" in updated: + for s in options.scripts: + if options.scripts.count(s) > 1: + raise exceptions.OptionsError("Duplicate script: %s" % s) + + for a in ctx.master.addons.chain[:]: + if isinstance(a, Script) and a.name not in options.scripts: + ctx.log.info("Un-loading script: %s" % a.name) + ctx.master.addons.remove(a) + + current = {} + for a in ctx.master.addons.chain[:]: + if isinstance(a, Script): + current[a.name] = a + ctx.master.addons.chain.remove(a) + + for s in options.scripts: + if s in current: + ctx.master.addons.chain.append(current[s]) + else: + ctx.log.info("Loading script: %s" % s) + sc = Script(s) + ctx.master.addons.add(options, sc) diff --git a/mitmproxy/builtins/setheaders.py b/mitmproxy/builtins/setheaders.py index 6bda3f55..4a784a1d 100644 --- a/mitmproxy/builtins/setheaders.py +++ b/mitmproxy/builtins/setheaders.py @@ -6,7 +6,7 @@ class SetHeaders: def __init__(self): self.lst = [] - def configure(self, options): + def configure(self, options, updated): """ options.setheaders is a tuple of (fpatt, header, value) diff --git a/mitmproxy/builtins/stickyauth.py b/mitmproxy/builtins/stickyauth.py index 1309911c..98fb65ed 100644 --- a/mitmproxy/builtins/stickyauth.py +++ b/mitmproxy/builtins/stickyauth.py @@ -10,7 +10,7 @@ class StickyAuth: self.flt = None self.hosts = {} - def configure(self, options): + def configure(self, options, updated): if options.stickyauth: flt = filt.parse(options.stickyauth) if not flt: diff --git a/mitmproxy/builtins/stickycookie.py b/mitmproxy/builtins/stickycookie.py index dc699bb4..88333d5c 100644 --- a/mitmproxy/builtins/stickycookie.py +++ b/mitmproxy/builtins/stickycookie.py @@ -32,7 +32,7 @@ class StickyCookie: self.jar = collections.defaultdict(dict) self.flt = None - def configure(self, options): + def configure(self, options, updated): if options.stickycookie: flt = filt.parse(options.stickycookie) if not flt: diff --git a/mitmproxy/console/master.py b/mitmproxy/console/master.py index f4617f62..fad4c375 100644 --- a/mitmproxy/console/master.py +++ b/mitmproxy/console/master.py @@ -242,7 +242,7 @@ class ConsoleMaster(flow.FlowMaster): signals.pop_view_state.connect(self.sig_pop_view_state) signals.push_view_state.connect(self.sig_push_view_state) signals.sig_add_log.connect(self.sig_add_log) - self.addons.add(*builtins.default_addons()) + self.addons.add(options, *builtins.default_addons()) def __setattr__(self, name, value): self.__dict__[name] = value diff --git a/mitmproxy/ctx.py b/mitmproxy/ctx.py index fcfdfd0b..5d2905fa 100644 --- a/mitmproxy/ctx.py +++ b/mitmproxy/ctx.py @@ -1,4 +1,4 @@ from typing import Callable # noqa master = None # type: "mitmproxy.flow.FlowMaster" -log = None # type: Callable[[str], None] +log = None # type: "mitmproxy.controller.Log" diff --git a/mitmproxy/dump.py b/mitmproxy/dump.py index 4f34ab95..83f44d87 100644 --- a/mitmproxy/dump.py +++ b/mitmproxy/dump.py @@ -42,8 +42,8 @@ class DumpMaster(flow.FlowMaster): def __init__(self, server, options): flow.FlowMaster.__init__(self, options, server, flow.State()) self.has_errored = False - self.addons.add(*builtins.default_addons()) - self.addons.add(dumper.Dumper()) + self.addons.add(options, *builtins.default_addons()) + self.addons.add(options, dumper.Dumper()) # This line is just for type hinting self.options = self.options # type: Options self.replay_ignore_params = options.replay_ignore_params diff --git a/mitmproxy/filt.py b/mitmproxy/filt.py index 8b647b22..fe817757 100644 --- a/mitmproxy/filt.py +++ b/mitmproxy/filt.py @@ -39,9 +39,12 @@ import functools from mitmproxy.models.http import HTTPFlow from mitmproxy.models.tcp import TCPFlow +from mitmproxy.models.flow import Flow + from netlib import strutils import pyparsing as pp +from typing import Callable def only(*types): @@ -471,7 +474,11 @@ def _make(): bnf = _make() +TFilter = Callable[[Flow], bool] + + def parse(s): + # type: (str) -> TFilter try: filt = bnf.parseString(s, parseAll=True)[0] filt.pattern = s diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py index e94ef51d..140c7ca8 100644 --- a/mitmproxy/optmanager.py +++ b/mitmproxy/optmanager.py @@ -35,7 +35,7 @@ class OptManager(object): self.__dict__["_initialized"] = True @contextlib.contextmanager - def rollback(self): + def rollback(self, updated): old = self._opts.copy() try: yield @@ -44,7 +44,7 @@ class OptManager(object): self.errored.send(self, exc=e) # Rollback self.__dict__["_opts"] = old - self.changed.send(self) + self.changed.send(self, updated=updated) def __eq__(self, other): return self._opts == other._opts @@ -62,22 +62,22 @@ class OptManager(object): if not self._initialized: self._opts[attr] = value return - if attr not in self._opts: - raise KeyError("No such option: %s" % attr) - with self.rollback(): - self._opts[attr] = value - self.changed.send(self) + self.update(**{attr: value}) + + def keys(self): + return set(self._opts.keys()) def get(self, k, d=None): return self._opts.get(k, d) def update(self, **kwargs): + updated = set(kwargs.keys()) for k in kwargs: if k not in self._opts: raise KeyError("No such option: %s" % k) - with self.rollback(): + with self.rollback(updated): self._opts.update(kwargs) - self.changed.send(self) + self.changed.send(self, updated=updated) def setter(self, attr): """ diff --git a/mitmproxy/protocol/http2.py b/mitmproxy/protocol/http2.py index 1285e10e..8308f44d 100644 --- a/mitmproxy/protocol/http2.py +++ b/mitmproxy/protocol/http2.py @@ -584,6 +584,8 @@ class Http2SingleStreamLayer(http._HttpTransmissionLayer, basethread.BaseThread) except exceptions.ProtocolException as e: # pragma: no cover self.log(repr(e), "info") self.log(traceback.format_exc(), "debug") + except exceptions.Kill: + self.log("Connection killed", "info") if not self.zombie: self.zombie = time.time() diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py index 7aa4c736..a74ba7e2 100644 --- a/mitmproxy/proxy/config.py +++ b/mitmproxy/proxy/config.py @@ -79,10 +79,10 @@ class ProxyConfig: self.certstore = None self.clientcerts = None self.openssl_verification_mode_server = None - self.configure(options) + self.configure(options, set(options.keys())) options.changed.connect(self.configure) - def configure(self, options): + def configure(self, options, updated): conflict = all( [ options.add_upstream_certs_to_client_chain, diff --git a/mitmproxy/web/master.py b/mitmproxy/web/master.py index 3d384612..9ddb61d4 100644 --- a/mitmproxy/web/master.py +++ b/mitmproxy/web/master.py @@ -136,7 +136,7 @@ class WebMaster(flow.FlowMaster): def __init__(self, server, options): super(WebMaster, self).__init__(options, server, WebState()) - self.addons.add(*builtins.default_addons()) + self.addons.add(options, *builtins.default_addons()) self.app = app.Application( self, self.options.wdebug, self.options.wauthenticator ) diff --git a/netlib/strutils.py b/netlib/strutils.py index 32e77927..96c8b10f 100644 --- a/netlib/strutils.py +++ b/netlib/strutils.py @@ -51,8 +51,7 @@ else: def escape_control_characters(text, keep_spacing=True): """ - Replace all unicode C1 control characters from the given text with their respective control pictures. - For example, a null byte is replaced with the unicode character "\u2400". + Replace all unicode C1 control characters from the given text with a single "." Args: keep_spacing: If True, tabs and newlines will not be replaced. diff --git a/test/mitmproxy/builtins/test_anticache.py b/test/mitmproxy/builtins/test_anticache.py index 5a00af03..ac321e26 100644 --- a/test/mitmproxy/builtins/test_anticache.py +++ b/test/mitmproxy/builtins/test_anticache.py @@ -8,9 +8,10 @@ from mitmproxy import options class TestAntiCache(mastertest.MasterTest): def test_simple(self): s = state.State() - m = master.FlowMaster(options.Options(anticache = True), None, s) + o = options.Options(anticache = True) + m = master.FlowMaster(o, None, s) sa = anticache.AntiCache() - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow(resp=True) self.invoke(m, "request", f) diff --git a/test/mitmproxy/builtins/test_anticomp.py b/test/mitmproxy/builtins/test_anticomp.py index 6bfd54bb..a5f5a270 100644 --- a/test/mitmproxy/builtins/test_anticomp.py +++ b/test/mitmproxy/builtins/test_anticomp.py @@ -8,9 +8,10 @@ from mitmproxy import options class TestAntiComp(mastertest.MasterTest): def test_simple(self): s = state.State() - m = master.FlowMaster(options.Options(anticomp = True), None, s) + o = options.Options(anticomp = True) + m = master.FlowMaster(o, None, s) sa = anticomp.AntiComp() - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow(resp=True) self.invoke(m, "request", f) diff --git a/test/mitmproxy/builtins/test_dumper.py b/test/mitmproxy/builtins/test_dumper.py index 57e3d036..6287fe86 100644 --- a/test/mitmproxy/builtins/test_dumper.py +++ b/test/mitmproxy/builtins/test_dumper.py @@ -15,26 +15,27 @@ class TestDumper(mastertest.MasterTest): d = dumper.Dumper() sio = StringIO() - d.configure(dump.Options(tfile = sio, flow_detail = 0)) + updated = set(["tfile", "flow_detail"]) + d.configure(dump.Options(tfile = sio, flow_detail = 0), updated) d.response(tutils.tflow()) assert not sio.getvalue() - d.configure(dump.Options(tfile = sio, flow_detail = 4)) + d.configure(dump.Options(tfile = sio, flow_detail = 4), updated) d.response(tutils.tflow()) assert sio.getvalue() sio = StringIO() - d.configure(dump.Options(tfile = sio, flow_detail = 4)) + d.configure(dump.Options(tfile = sio, flow_detail = 4), updated) d.response(tutils.tflow(resp=True)) assert "<<" in sio.getvalue() sio = StringIO() - d.configure(dump.Options(tfile = sio, flow_detail = 4)) + d.configure(dump.Options(tfile = sio, flow_detail = 4), updated) d.response(tutils.tflow(err=True)) assert "<<" in sio.getvalue() sio = StringIO() - d.configure(dump.Options(tfile = sio, flow_detail = 4)) + d.configure(dump.Options(tfile = sio, flow_detail = 4), updated) flow = tutils.tflow() flow.request = netlib.tutils.treq() flow.request.stickycookie = True @@ -47,7 +48,7 @@ class TestDumper(mastertest.MasterTest): assert sio.getvalue() sio = StringIO() - d.configure(dump.Options(tfile = sio, flow_detail = 4)) + d.configure(dump.Options(tfile = sio, flow_detail = 4), updated) flow = tutils.tflow(resp=netlib.tutils.tresp(content=b"{")) flow.response.headers["content-type"] = "application/json" flow.response.status_code = 400 @@ -55,7 +56,7 @@ class TestDumper(mastertest.MasterTest): assert sio.getvalue() sio = StringIO() - d.configure(dump.Options(tfile = sio)) + d.configure(dump.Options(tfile = sio), updated) flow = tutils.tflow() flow.request.content = None flow.response = models.HTTPResponse.wrap(netlib.tutils.tresp()) @@ -72,15 +73,13 @@ class TestContentView(mastertest.MasterTest): s = state.State() sio = StringIO() - m = mastertest.RecordingMaster( - dump.Options( - flow_detail=4, - verbosity=3, - tfile=sio, - ), - None, s + o = dump.Options( + flow_detail=4, + verbosity=3, + tfile=sio, ) + m = mastertest.RecordingMaster(o, None, s) d = dumper.Dumper() - m.addons.add(d) + m.addons.add(o, d) self.invoke(m, "response", tutils.tflow()) assert "Content viewer failed" in m.event_log[0][1] diff --git a/test/mitmproxy/builtins/test_filestreamer.py b/test/mitmproxy/builtins/test_filestreamer.py index c1d5947f..0e69b340 100644 --- a/test/mitmproxy/builtins/test_filestreamer.py +++ b/test/mitmproxy/builtins/test_filestreamer.py @@ -20,16 +20,13 @@ class TestStream(mastertest.MasterTest): return list(r.stream()) s = state.State() - m = master.FlowMaster( - options.Options( - outfile = (p, "wb") - ), - None, - s + o = options.Options( + outfile = (p, "wb") ) + m = master.FlowMaster(o, None, s) sa = filestreamer.FileStreamer() - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow(resp=True) self.invoke(m, "request", f) self.invoke(m, "response", f) @@ -39,7 +36,7 @@ class TestStream(mastertest.MasterTest): m.options.outfile = (p, "ab") - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow() self.invoke(m, "request", f) m.addons.remove(sa) diff --git a/test/mitmproxy/builtins/test_replace.py b/test/mitmproxy/builtins/test_replace.py index a0b4b722..5e70ce56 100644 --- a/test/mitmproxy/builtins/test_replace.py +++ b/test/mitmproxy/builtins/test_replace.py @@ -8,38 +8,38 @@ from mitmproxy import options class TestReplace(mastertest.MasterTest): def test_configure(self): r = replace.Replace() + updated = set(["replacements"]) r.configure(options.Options( replacements=[("one", "two", "three")] - )) + ), updated) tutils.raises( "invalid filter pattern", r.configure, options.Options( replacements=[("~b", "two", "three")] - ) + ), + updated ) tutils.raises( "invalid regular expression", r.configure, options.Options( replacements=[("foo", "+", "three")] - ) + ), + updated ) def test_simple(self): s = state.State() - m = master.FlowMaster( - options.Options( - replacements = [ - ("~q", "foo", "bar"), - ("~s", "foo", "bar"), - ] - ), - None, - s + o = options.Options( + replacements = [ + ("~q", "foo", "bar"), + ("~s", "foo", "bar"), + ] ) + m = master.FlowMaster(o, None, s) sa = replace.Replace() - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow() f.request.content = b"foo" diff --git a/test/mitmproxy/builtins/test_script.py b/test/mitmproxy/builtins/test_script.py index f37c7f94..2870fd17 100644 --- a/test/mitmproxy/builtins/test_script.py +++ b/test/mitmproxy/builtins/test_script.py @@ -48,39 +48,41 @@ def test_load_script(): "data/addonscripts/recorder.py" ), [] ) - assert ns["configure"] + assert ns.start class TestScript(mastertest.MasterTest): def test_simple(self): s = state.State() - m = master.FlowMaster(options.Options(), None, s) + o = options.Options() + m = master.FlowMaster(o, None, s) sc = script.Script( tutils.test_data.path( "data/addonscripts/recorder.py" ) ) - m.addons.add(sc) - assert sc.ns["call_log"] == [ + m.addons.add(o, sc) + assert sc.ns.call_log == [ ("solo", "start", (), {}), - ("solo", "configure", (options.Options(),), {}) + ("solo", "configure", (o, o.keys()), {}) ] - sc.ns["call_log"] = [] + sc.ns.call_log = [] f = tutils.tflow(resp=True) self.invoke(m, "request", f) - recf = sc.ns["call_log"][0] + recf = sc.ns.call_log[0] assert recf[1] == "request" def test_reload(self): s = state.State() - m = mastertest.RecordingMaster(options.Options(), None, s) + o = options.Options() + m = mastertest.RecordingMaster(o, None, s) with tutils.tmpdir(): with open("foo.py", "w"): pass sc = script.Script("foo.py") - m.addons.add(sc) + m.addons.add(o, sc) for _ in range(100): with open("foo.py", "a") as f: @@ -93,19 +95,22 @@ class TestScript(mastertest.MasterTest): def test_exception(self): s = state.State() - m = mastertest.RecordingMaster(options.Options(), None, s) + o = options.Options() + m = mastertest.RecordingMaster(o, None, s) sc = script.Script( tutils.test_data.path("data/addonscripts/error.py") ) - m.addons.add(sc) + m.addons.add(o, sc) f = tutils.tflow(resp=True) self.invoke(m, "request", f) assert m.event_log[0][0] == "error" def test_duplicate_flow(self): s = state.State() - fm = master.FlowMaster(None, None, s) + o = options.Options() + fm = master.FlowMaster(o, None, s) fm.addons.add( + o, script.Script( tutils.test_data.path("data/addonscripts/duplicate_flow.py") ) @@ -116,6 +121,20 @@ class TestScript(mastertest.MasterTest): assert not fm.state.view[0].request.is_replay assert fm.state.view[1].request.is_replay + def test_addon(self): + s = state.State() + o = options.Options() + m = master.FlowMaster(o, None, s) + sc = script.Script( + tutils.test_data.path( + "data/addonscripts/addon.py" + ) + ) + m.addons.add(o, sc) + assert sc.ns.event_log == [ + 'scriptstart', 'addonstart', 'addonconfigure' + ] + class TestScriptLoader(mastertest.MasterTest): def test_simple(self): @@ -123,7 +142,7 @@ class TestScriptLoader(mastertest.MasterTest): o = options.Options(scripts=[]) m = master.FlowMaster(o, None, s) sc = script.ScriptLoader() - m.addons.add(sc) + m.addons.add(o, sc) assert len(m.addons) == 1 o.update( scripts = [ @@ -139,7 +158,7 @@ class TestScriptLoader(mastertest.MasterTest): o = options.Options(scripts=["one", "one"]) m = master.FlowMaster(o, None, s) sc = script.ScriptLoader() - tutils.raises(exceptions.OptionsError, m.addons.add, sc) + tutils.raises(exceptions.OptionsError, m.addons.add, o, sc) def test_order(self): rec = tutils.test_data.path("data/addonscripts/recorder.py") @@ -154,7 +173,7 @@ class TestScriptLoader(mastertest.MasterTest): ) m = mastertest.RecordingMaster(o, None, s) sc = script.ScriptLoader() - m.addons.add(sc) + m.addons.add(o, sc) debug = [(i[0], i[1]) for i in m.event_log if i[0] == "debug"] assert debug == [ diff --git a/test/mitmproxy/builtins/test_setheaders.py b/test/mitmproxy/builtins/test_setheaders.py index 4465719d..41c18360 100644 --- a/test/mitmproxy/builtins/test_setheaders.py +++ b/test/mitmproxy/builtins/test_setheaders.py @@ -8,19 +8,20 @@ from mitmproxy import options class TestSetHeaders(mastertest.MasterTest): def mkmaster(self, **opts): s = state.State() - m = mastertest.RecordingMaster(options.Options(**opts), None, s) + o = options.Options(**opts) + m = mastertest.RecordingMaster(o, None, s) sh = setheaders.SetHeaders() - m.addons.add(sh) + m.addons.add(o, sh) return m, sh def test_configure(self): sh = setheaders.SetHeaders() + o = options.Options( + setheaders = [("~b", "one", "two")] + ) tutils.raises( "invalid setheader filter pattern", - sh.configure, - options.Options( - setheaders = [("~b", "one", "two")] - ) + sh.configure, o, o.keys() ) def test_setheaders(self): diff --git a/test/mitmproxy/builtins/test_stickyauth.py b/test/mitmproxy/builtins/test_stickyauth.py index 9233f435..5757fb2d 100644 --- a/test/mitmproxy/builtins/test_stickyauth.py +++ b/test/mitmproxy/builtins/test_stickyauth.py @@ -8,9 +8,10 @@ from mitmproxy import options class TestStickyAuth(mastertest.MasterTest): def test_simple(self): s = state.State() - m = master.FlowMaster(options.Options(stickyauth = ".*"), None, s) + o = options.Options(stickyauth = ".*") + m = master.FlowMaster(o, None, s) sa = stickyauth.StickyAuth() - m.addons.add(sa) + m.addons.add(o, sa) f = tutils.tflow(resp=True) f.request.headers["authorization"] = "foo" diff --git a/test/mitmproxy/builtins/test_stickycookie.py b/test/mitmproxy/builtins/test_stickycookie.py index 81b540db..e9d92c83 100644 --- a/test/mitmproxy/builtins/test_stickycookie.py +++ b/test/mitmproxy/builtins/test_stickycookie.py @@ -14,22 +14,23 @@ def test_domain_match(): class TestStickyCookie(mastertest.MasterTest): def mk(self): s = state.State() - m = master.FlowMaster(options.Options(stickycookie = ".*"), None, s) + o = options.Options(stickycookie = ".*") + m = master.FlowMaster(o, None, s) sc = stickycookie.StickyCookie() - m.addons.add(sc) + m.addons.add(o, sc) return s, m, sc def test_config(self): sc = stickycookie.StickyCookie() + o = options.Options(stickycookie = "~b") tutils.raises( "invalid filter", - sc.configure, - options.Options(stickycookie = "~b") + sc.configure, o, o.keys() ) def test_simple(self): s, m, sc = self.mk() - m.addons.add(sc) + m.addons.add(m.options, sc) f = tutils.tflow(resp=True) f.response.headers["set-cookie"] = "foo=bar" diff --git a/test/mitmproxy/data/addonscripts/addon.py b/test/mitmproxy/data/addonscripts/addon.py new file mode 100644 index 00000000..84173cb6 --- /dev/null +++ b/test/mitmproxy/data/addonscripts/addon.py @@ -0,0 +1,22 @@ +event_log = [] + + +class Addon: + @property + def event_log(self): + return event_log + + def start(self): + event_log.append("addonstart") + + def configure(self, options, updated): + event_log.append("addonconfigure") + + +def configure(options, updated): + event_log.append("addonconfigure") + + +def start(): + event_log.append("scriptstart") + return Addon() diff --git a/test/mitmproxy/data/addonscripts/recorder.py b/test/mitmproxy/data/addonscripts/recorder.py index b6ac8d89..890e6f4e 100644 --- a/test/mitmproxy/data/addonscripts/recorder.py +++ b/test/mitmproxy/data/addonscripts/recorder.py @@ -2,24 +2,24 @@ from mitmproxy import controller from mitmproxy import ctx import sys -call_log = [] -if len(sys.argv) > 1: - name = sys.argv[1] -else: - name = "solo" +class CallLogger: + call_log = [] -# Keep a log of all possible event calls -evts = list(controller.Events) + ["configure"] -for i in evts: - def mkprox(): - evt = i + def __init__(self, name = "solo"): + self.name = name - def prox(*args, **kwargs): - lg = (name, evt, args, kwargs) - if evt != "log": - ctx.log.info(str(lg)) - call_log.append(lg) - ctx.log.debug("%s %s" % (name, evt)) - return prox - globals()[i] = mkprox() + def __getattr__(self, attr): + if attr in controller.Events: + def prox(*args, **kwargs): + lg = (self.name, attr, args, kwargs) + if attr != "log": + ctx.log.info(str(lg)) + self.call_log.append(lg) + ctx.log.debug("%s %s" % (self.name, attr)) + return prox + raise AttributeError + + +def start(): + return CallLogger(*sys.argv[1:]) diff --git a/test/mitmproxy/data/dumpfile-011 b/test/mitmproxy/data/dumpfile-011 Binary files differindex 2534ad89..936ac0cc 100644 --- a/test/mitmproxy/data/dumpfile-011 +++ b/test/mitmproxy/data/dumpfile-011 diff --git a/test/mitmproxy/script/test_concurrent.py b/test/mitmproxy/script/test_concurrent.py index 080746e8..a5f76994 100644 --- a/test/mitmproxy/script/test_concurrent.py +++ b/test/mitmproxy/script/test_concurrent.py @@ -23,7 +23,7 @@ class TestConcurrent(mastertest.MasterTest): "data/addonscripts/concurrent_decorator.py" ) ) - m.addons.add(sc) + m.addons.add(m.options, sc) f1, f2 = tutils.tflow(), tutils.tflow() self.invoke(m, "request", f1) self.invoke(m, "request", f2) diff --git a/test/mitmproxy/test_addons.py b/test/mitmproxy/test_addons.py index 1861d4ac..a5085ea0 100644 --- a/test/mitmproxy/test_addons.py +++ b/test/mitmproxy/test_addons.py @@ -13,8 +13,9 @@ class TAddon: def test_simple(): - m = controller.Master(options.Options()) + o = options.Options() + m = controller.Master(o) a = addons.Addons(m) - a.add(TAddon("one")) + a.add(o, TAddon("one")) assert a.has_addon("one") assert not a.has_addon("two") diff --git a/test/mitmproxy/test_examples.py b/test/mitmproxy/test_examples.py index 0ec85f52..34fcc261 100644 --- a/test/mitmproxy/test_examples.py +++ b/test/mitmproxy/test_examples.py @@ -27,10 +27,11 @@ class RaiseMaster(master.FlowMaster): def tscript(cmd, args=""): + o = options.Options() cmd = example_dir.path(cmd) + " " + args - m = RaiseMaster(options.Options(), None, state.State()) + m = RaiseMaster(o, None, state.State()) sc = script.Script(cmd) - m.addons.add(sc) + m.addons.add(o, sc) return m, sc diff --git a/test/mitmproxy/test_optmanager.py b/test/mitmproxy/test_optmanager.py index 67f76ecd..8414e6b5 100644 --- a/test/mitmproxy/test_optmanager.py +++ b/test/mitmproxy/test_optmanager.py @@ -15,6 +15,8 @@ class TO(optmanager.OptManager): def test_options(): o = TO(two="three") + assert o.keys() == set(["one", "two"]) + assert o.one is None assert o.two == "three" o.one = "one" @@ -29,7 +31,7 @@ def test_options(): rec = [] - def sub(opts): + def sub(opts, updated): rec.append(copy.copy(opts)) o.changed.connect(sub) @@ -68,7 +70,7 @@ def test_rollback(): rec = [] - def sub(opts): + def sub(opts, updated): rec.append(copy.copy(opts)) recerr = [] @@ -76,7 +78,7 @@ def test_rollback(): def errsub(opts, **kwargs): recerr.append(kwargs) - def err(opts): + def err(opts, updated): if opts.one == "ten": raise exceptions.OptionsError() diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py index afbffb67..aa096a72 100644 --- a/test/mitmproxy/test_protocol_http2.py +++ b/test/mitmproxy/test_protocol_http2.py @@ -30,7 +30,7 @@ logging.getLogger("PIL.PngImagePlugin").setLevel(logging.WARNING) requires_alpn = pytest.mark.skipif( not netlib.tcp.HAS_ALPN, - reason="requires OpenSSL with ALPN support") + reason='requires OpenSSL with ALPN support') class _Http2ServerBase(netlib_tservers.ServerTestBase): @@ -80,7 +80,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase): print(traceback.format_exc()) break - def handle_server_event(self, h2_conn, rfile, wfile): + def handle_server_event(self, event, h2_conn, rfile, wfile): raise NotImplementedError() @@ -88,7 +88,6 @@ class _Http2TestBase(object): @classmethod def setup_class(cls): - cls.masteroptions = options.Options() opts = cls.get_options() cls.config = ProxyConfig(opts) @@ -145,12 +144,14 @@ class _Http2TestBase(object): wfile, h2_conn, stream_id=1, - headers=[], + headers=None, body=b'', end_stream=None, priority_exclusive=None, priority_depends_on=None, priority_weight=None): + if headers is None: + headers = [] if end_stream is None: end_stream = (len(body) == 0) @@ -172,12 +173,12 @@ class _Http2TestBase(object): class _Http2Test(_Http2TestBase, _Http2ServerBase): @classmethod - def setup_class(self): + def setup_class(cls): _Http2TestBase.setup_class() _Http2ServerBase.setup_class() @classmethod - def teardown_class(self): + def teardown_class(cls): _Http2TestBase.teardown_class() _Http2ServerBase.teardown_class() @@ -187,7 +188,7 @@ class TestSimple(_Http2Test): request_body_buffer = b'' @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.RequestReceived): @@ -214,7 +215,7 @@ class TestSimple(_Http2Test): wfile.write(h2_conn.data_to_send()) wfile.flush() elif isinstance(event, h2.events.DataReceived): - self.request_body_buffer += event.data + cls.request_body_buffer += event.data return True def test_simple(self): @@ -225,7 +226,7 @@ class TestSimple(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -269,7 +270,7 @@ class TestSimple(_Http2Test): class TestRequestWithPriority(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.RequestReceived): @@ -301,14 +302,14 @@ class TestRequestWithPriority(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), ], - priority_exclusive = True, - priority_depends_on = 42424242, - priority_weight = 42, + priority_exclusive=True, + priority_depends_on=42424242, + priority_weight=42, ) done = False @@ -343,7 +344,7 @@ class TestRequestWithPriority(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -381,11 +382,11 @@ class TestPriority(_Http2Test): priority_data = None @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.PriorityUpdated): - self.priority_data = (event.exclusive, event.depends_on, event.weight) + cls.priority_data = (event.exclusive, event.depends_on, event.weight) elif isinstance(event, h2.events.RequestReceived): import warnings with warnings.catch_warnings(): @@ -415,7 +416,7 @@ class TestPriority(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -451,11 +452,11 @@ class TestPriorityWithExistingStream(_Http2Test): priority_data = [] @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.PriorityUpdated): - self.priority_data.append((event.exclusive, event.depends_on, event.weight)) + cls.priority_data.append((event.exclusive, event.depends_on, event.weight)) elif isinstance(event, h2.events.RequestReceived): assert not event.priority_updated @@ -486,7 +487,7 @@ class TestPriorityWithExistingStream(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -527,7 +528,7 @@ class TestPriorityWithExistingStream(_Http2Test): class TestStreamResetFromServer(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.RequestReceived): @@ -543,7 +544,7 @@ class TestStreamResetFromServer(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -578,7 +579,7 @@ class TestStreamResetFromServer(_Http2Test): class TestBodySizeLimit(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False return True @@ -592,7 +593,7 @@ class TestBodySizeLimit(_Http2Test): client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -627,7 +628,7 @@ class TestBodySizeLimit(_Http2Test): class TestPushPromise(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.RequestReceived): @@ -637,14 +638,14 @@ class TestPushPromise(_Http2Test): h2_conn.send_headers(1, [(':status', '200')]) h2_conn.push_stream(1, 2, [ - (':authority', "127.0.0.1:%s" % self.port), + (':authority', "127.0.0.1:{}".format(cls.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/pushed_stream_foo'), ('foo', 'bar') ]) h2_conn.push_stream(1, 4, [ - (':authority', "127.0.0.1:%s" % self.port), + (':authority', "127.0.0.1:{}".format(cls.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/pushed_stream_bar'), @@ -675,7 +676,7 @@ class TestPushPromise(_Http2Test): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, stream_id=1, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -728,7 +729,7 @@ class TestPushPromise(_Http2Test): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, stream_id=1, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -780,7 +781,7 @@ class TestPushPromise(_Http2Test): class TestConnectionLost(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.RequestReceived): h2_conn.send_headers(1, [(':status', '200')]) wfile.write(h2_conn.data_to_send()) @@ -791,7 +792,7 @@ class TestConnectionLost(_Http2Test): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, stream_id=1, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -822,12 +823,12 @@ class TestConnectionLost(_Http2Test): class TestMaxConcurrentStreams(_Http2Test): @classmethod - def setup_class(self): + def setup_class(cls): _Http2TestBase.setup_class() _Http2ServerBase.setup_class(h2_server_settings={h2.settings.MAX_CONCURRENT_STREAMS: 2}) @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.ConnectionTerminated): return False elif isinstance(event, h2.events.RequestReceived): @@ -848,7 +849,7 @@ class TestMaxConcurrentStreams(_Http2Test): # this will exceed MAX_CONCURRENT_STREAMS on the server connection # and cause mitmproxy to throttle stream creation to the server self._send_request(client.wfile, h2_conn, stream_id=id, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), @@ -883,7 +884,7 @@ class TestMaxConcurrentStreams(_Http2Test): class TestConnectionTerminated(_Http2Test): @classmethod - def handle_server_event(self, event, h2_conn, rfile, wfile): + def handle_server_event(cls, event, h2_conn, rfile, wfile): if isinstance(event, h2.events.RequestReceived): h2_conn.close_connection(error_code=5, last_stream_id=42, additional_data=b'foobar') wfile.write(h2_conn.data_to_send()) @@ -894,7 +895,7 @@ class TestConnectionTerminated(_Http2Test): client, h2_conn = self._setup_connection() self._send_request(client.wfile, h2_conn, headers=[ - (':authority', "127.0.0.1:%s" % self.server.server.address.port), + (':authority', "127.0.0.1:{}".format(self.server.server.address.port)), (':method', 'GET'), (':scheme', 'https'), (':path', '/'), diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py index 233af597..6230fc1f 100644 --- a/test/mitmproxy/test_server.py +++ b/test/mitmproxy/test_server.py @@ -291,7 +291,7 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin): s = script.Script( tutils.test_data.path("data/addonscripts/stream_modify.py") ) - self.master.addons.add(s) + self.master.addons.add(self.master.options, s) d = self.pathod('200:b"foo"') assert d.content == b"bar" self.master.addons.remove(s) @@ -523,7 +523,7 @@ class TestTransparent(tservers.TransparentProxyTest, CommonMixin, TcpMixin): s = script.Script( tutils.test_data.path("data/addonscripts/tcp_stream_modify.py") ) - self.master.addons.add(s) + self.master.addons.add(self.master.options, s) self._tcpproxy_on() d = self.pathod('200:b"foo"') self._tcpproxy_off() diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py index f5119166..d364162c 100644 --- a/test/mitmproxy/tservers.py +++ b/test/mitmproxy/tservers.py @@ -34,7 +34,7 @@ class TestMaster(flow.FlowMaster): s = ProxyServer(config) state = flow.State() flow.FlowMaster.__init__(self, opts, s, state) - self.addons.add(*builtins.default_addons()) + self.addons.add(opts, *builtins.default_addons()) self.apps.add(testapp, "testapp", 80) self.apps.add(errapp, "errapp", 80) self.clear_log() |