aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/addonmanager.py58
-rw-r--r--mitmproxy/addons/__init__.py2
-rw-r--r--mitmproxy/addons/clientplayback.py6
-rw-r--r--mitmproxy/addons/keepserving.py7
-rw-r--r--mitmproxy/addons/readfile.py46
-rw-r--r--mitmproxy/addons/readstdin.py26
-rw-r--r--mitmproxy/addons/script.py6
-rw-r--r--mitmproxy/addons/serverplayback.py4
-rw-r--r--mitmproxy/addons/view.py3
-rw-r--r--mitmproxy/contentviews/__init__.py2
-rw-r--r--mitmproxy/contentviews/xml_html.py2
-rw-r--r--mitmproxy/controller.py3
-rw-r--r--mitmproxy/exceptions.py3
-rw-r--r--mitmproxy/master.py62
-rw-r--r--mitmproxy/net/http/request.py49
-rw-r--r--mitmproxy/options.py5
-rw-r--r--mitmproxy/test/taddons.py26
-rw-r--r--mitmproxy/tools/console/flowlist.py5
-rw-r--r--mitmproxy/tools/console/master.py20
-rw-r--r--mitmproxy/tools/dump.py17
-rw-r--r--mitmproxy/tools/web/app.py3
-rw-r--r--mitmproxy/tools/web/master.py9
-rw-r--r--release/README.md27
-rw-r--r--setup.py14
-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.py62
-rw-r--r--test/mitmproxy/addons/test_readstdin.py53
-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/contentviews/test_xml_html.py2
-rw-r--r--test/mitmproxy/net/http/test_request.py28
-rw-r--r--test/mitmproxy/script/test_concurrent.py2
-rw-r--r--test/mitmproxy/test_addonmanager.py16
-rw-r--r--test/mitmproxy/test_examples.py8
-rw-r--r--test/mitmproxy/test_flow.py55
-rw-r--r--test/mitmproxy/tools/console/test_master.py6
-rw-r--r--test/mitmproxy/tools/test_dump.py13
-rw-r--r--test/mitmproxy/tservers.py2
44 files changed, 471 insertions, 253 deletions
diff --git a/mitmproxy/addonmanager.py b/mitmproxy/addonmanager.py
index 43e76510..123f64b2 100644
--- a/mitmproxy/addonmanager.py
+++ b/mitmproxy/addonmanager.py
@@ -1,5 +1,6 @@
from mitmproxy import exceptions
from mitmproxy import eventsequence
+from . import ctx
import pprint
@@ -31,31 +32,27 @@ class AddonManager:
return i
def configure_all(self, options, updated):
- self.invoke_all_with_context("configure", options, updated)
-
- def startup(self, s):
- """
- Run startup events on addon.
- """
- self.invoke_with_context(s, "start", self.master.options)
+ self.trigger("configure", options, updated)
def add(self, *addons):
"""
Add addons to the end of the chain, and run their startup events.
"""
self.chain.extend(addons)
- for i in addons:
- self.startup(i)
+ with self.master.handlecontext():
+ for i in addons:
+ self.invoke_addon(i, "start", self.master.options)
def remove(self, addon):
"""
Remove an addon from the chain, and run its done events.
"""
self.chain = [i for i in self.chain if i is not addon]
- self.invoke_with_context(addon, "done")
+ with self.master.handlecontext():
+ self.invoke_addon(addon, "done")
def done(self):
- self.invoke_all_with_context("done")
+ self.trigger("done")
def __len__(self):
return len(self.chain)
@@ -63,18 +60,17 @@ class AddonManager:
def __str__(self):
return pprint.pformat([str(i) for i in self.chain])
- def invoke_with_context(self, addon, name, *args, **kwargs):
- with self.master.handlecontext():
- 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):
- if name not in eventsequence.Events: # prama: no cover
- raise NotImplementedError("Unknown event")
+ def invoke_addon(self, addon, name, *args, **kwargs):
+ """
+ Invoke an event on an addon. This method must run within an
+ established handler context.
+ """
+ if not ctx.master:
+ raise exceptions.AddonError(
+ "invoke_addon called without a handler context."
+ )
+ if name not in eventsequence.Events:
+ name = "event_" + name
func = getattr(addon, name, None)
if func:
if not callable(func):
@@ -83,9 +79,13 @@ class AddonManager:
)
func(*args, **kwargs)
- def __call__(self, name, *args, **kwargs):
- for i in self.chain:
- try:
- self.invoke(i, name, *args, **kwargs)
- except exceptions.AddonHalt:
- return
+ def trigger(self, name, *args, **kwargs):
+ """
+ Establish a handler context and trigger an event across all addons
+ """
+ with self.master.handlecontext():
+ for i in self.chain:
+ try:
+ self.invoke_addon(i, name, *args, **kwargs)
+ except exceptions.AddonHalt:
+ return
diff --git a/mitmproxy/addons/__init__.py b/mitmproxy/addons/__init__.py
index 7a45106c..b4367d78 100644
--- a/mitmproxy/addons/__init__.py
+++ b/mitmproxy/addons/__init__.py
@@ -8,6 +8,7 @@ from mitmproxy.addons import disable_h2c
from mitmproxy.addons import onboarding
from mitmproxy.addons import proxyauth
from mitmproxy.addons import replace
+from mitmproxy.addons import readfile
from mitmproxy.addons import script
from mitmproxy.addons import serverplayback
from mitmproxy.addons import setheaders
@@ -37,5 +38,6 @@ def default_addons():
stickycookie.StickyCookie(),
streambodies.StreamBodies(),
streamfile.StreamFile(),
+ readfile.ReadFile(),
upstream_auth.UpstreamAuth(),
]
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
new file mode 100644
index 00000000..03dcd084
--- /dev/null
+++ b/mitmproxy/addons/readfile.py
@@ -0,0 +1,46 @@
+import os.path
+
+from mitmproxy import ctx
+from mitmproxy import io
+from mitmproxy import exceptions
+
+
+class ReadFile:
+ """
+ An addon that handles reading from file on startup.
+ """
+ def __init__(self):
+ self.path = None
+
+ def load_flows_file(self, path: str) -> int:
+ path = os.path.expanduser(path)
+ cnt = 0
+ try:
+ with open(path, "rb") as f:
+ freader = io.FlowReader(f)
+ for i in freader.stream():
+ cnt += 1
+ ctx.master.load_flow(i)
+ return cnt
+ except (IOError, exceptions.FlowReadException) as v:
+ if cnt:
+ ctx.log.warn(
+ "Flow file corrupted - loaded %i flows." % cnt,
+ )
+ else:
+ ctx.log.error("Flow file corrupted.")
+ raise exceptions.FlowReadException(v)
+
+ def configure(self, options, updated):
+ if "rfile" in updated and options.rfile:
+ self.path = options.rfile
+
+ def running(self):
+ if self.path:
+ try:
+ self.load_flows_file(self.path)
+ except exceptions.FlowReadException as v:
+ raise exceptions.OptionsError(v)
+ finally:
+ self.path = None
+ ctx.master.addons.trigger("processing_complete")
diff --git a/mitmproxy/addons/readstdin.py b/mitmproxy/addons/readstdin.py
new file mode 100644
index 00000000..93a99f01
--- /dev/null
+++ b/mitmproxy/addons/readstdin.py
@@ -0,0 +1,26 @@
+from mitmproxy import ctx
+from mitmproxy import io
+from mitmproxy import exceptions
+import sys
+
+
+class ReadStdin:
+ """
+ An addon that reads from stdin if we're not attached to (someting like)
+ a tty.
+ """
+ def running(self, stdin = sys.stdin):
+ if not stdin.isatty():
+ ctx.log.info("Reading from stdin")
+ try:
+ stdin.buffer.read(0)
+ except Exception as e:
+ ctx.log.warn("Cannot read from stdin: {}".format(e))
+ return
+ freader = io.FlowReader(stdin.buffer)
+ try:
+ for i in freader.stream():
+ ctx.master.load_flow(i)
+ except exceptions.FlowReadException as e:
+ ctx.log.error("Error reading from stdin: %s" % e)
+ ctx.master.addons.trigger("processing_complete")
diff --git a/mitmproxy/addons/script.py b/mitmproxy/addons/script.py
index cfbe5284..4d893f1c 100644
--- a/mitmproxy/addons/script.py
+++ b/mitmproxy/addons/script.py
@@ -273,11 +273,11 @@ class ScriptLoader:
ctx.master.addons.chain = ochain[:pos + 1] + ordered + ochain[pos + 1:]
for s in newscripts:
- ctx.master.addons.startup(s)
+ ctx.master.addons.invoke_addon(s, "start", options)
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(
+ ctx.master.addons.invoke_addon(
s, "configure", options, options.keys()
)
- ctx.master.addons.invoke_with_context(s, "running")
+ ctx.master.addons.invoke_addon(s, "running")
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/addons/view.py b/mitmproxy/addons/view.py
index 3a0587b0..2218327c 100644
--- a/mitmproxy/addons/view.py
+++ b/mitmproxy/addons/view.py
@@ -241,7 +241,7 @@ class View(collections.Sequence):
self._refilter()
self.sig_store_refresh.send(self)
- def add(self, f: mitmproxy.flow.Flow) -> bool:
+ def add(self, f: mitmproxy.flow.Flow) -> None:
"""
Adds a flow to the state. If the flow already exists, it is
ignored.
@@ -371,6 +371,7 @@ class Focus:
def index(self) -> typing.Optional[int]:
if self.flow:
return self.view.index(self.flow)
+ return None
@index.setter
def index(self, idx):
diff --git a/mitmproxy/contentviews/__init__.py b/mitmproxy/contentviews/__init__.py
index c7db6690..f57b27c7 100644
--- a/mitmproxy/contentviews/__init__.py
+++ b/mitmproxy/contentviews/__init__.py
@@ -36,12 +36,14 @@ def get(name: str) -> Optional[View]:
for i in views:
if i.name.lower() == name.lower():
return i
+ return None
def get_by_shortcut(c: str) -> Optional[View]:
for i in views:
if i.prompt[1] == c:
return i
+ return None
def add(view: View) -> None:
diff --git a/mitmproxy/contentviews/xml_html.py b/mitmproxy/contentviews/xml_html.py
index 0f2ce57d..62fb939f 100644
--- a/mitmproxy/contentviews/xml_html.py
+++ b/mitmproxy/contentviews/xml_html.py
@@ -128,6 +128,7 @@ def is_inline_text(a: Token, b: Token, c: Token) -> bool:
if isinstance(a, Tag) and isinstance(b, Text) and isinstance(c, Tag):
if a.is_opening and "\n" not in b.data and c.is_closing and a.tag == c.tag:
return True
+ return False
def is_inline(prev2: Token, prev1: Token, t: Token, next1: Token, next2: Token) -> bool:
@@ -140,6 +141,7 @@ def is_inline(prev2: Token, prev1: Token, t: Token, next1: Token, next2: Token)
return True # <div></div> (start tag)
if isinstance(prev1, Tag) and prev1.is_opening and t.is_closing and prev1.tag == t.tag:
return True # <div></div> (end tag)
+ return False
class ElementStack:
diff --git a/mitmproxy/controller.py b/mitmproxy/controller.py
index 868d5841..aa4dcbbc 100644
--- a/mitmproxy/controller.py
+++ b/mitmproxy/controller.py
@@ -66,8 +66,7 @@ def handler(f):
with master.handlecontext():
ret = f(master, message)
- if handling:
- master.addons(f.__name__, message)
+ master.addons.trigger(f.__name__, message)
# Reset the handled flag - it's common for us to feed the same object
# through handlers repeatedly, so we don't want this to persist across
diff --git a/mitmproxy/exceptions.py b/mitmproxy/exceptions.py
index 309b8189..9b6328ac 100644
--- a/mitmproxy/exceptions.py
+++ b/mitmproxy/exceptions.py
@@ -102,6 +102,9 @@ class AddonError(MitmproxyException):
class AddonHalt(MitmproxyException):
+ """
+ Raised by addons to signal that no further handlers should handle this event.
+ """
pass
diff --git a/mitmproxy/master.py b/mitmproxy/master.py
index 79747a97..887a9240 100644
--- a/mitmproxy/master.py
+++ b/mitmproxy/master.py
@@ -1,8 +1,6 @@
-import os
import threading
import contextlib
import queue
-import sys
from mitmproxy import addonmanager
from mitmproxy import options
@@ -12,10 +10,8 @@ from mitmproxy import exceptions
from mitmproxy import connections
from mitmproxy import http
from mitmproxy import log
-from mitmproxy import io
from mitmproxy.proxy.protocol import http_replay
from mitmproxy.types import basethread
-import mitmproxy.net.http
from . import ctx as mitmproxy_ctx
@@ -68,8 +64,7 @@ class Master:
"""
level: debug, info, warn, error
"""
- with self.handlecontext():
- self.addons("log", log.LogEntry(e, level))
+ self.addons.trigger("log", log.LogEntry(e, level))
def start(self):
self.should_exit.clear()
@@ -89,9 +84,8 @@ class Master:
def tick(self, timeout):
if self.first_tick:
self.first_tick = False
- self.addons.invoke_all_with_context("running")
- with self.handlecontext():
- self.addons("tick")
+ self.addons.trigger("running")
+ self.addons.trigger("tick")
changed = False
try:
mtype, obj = self.event_queue.get(timeout=timeout)
@@ -122,27 +116,18 @@ class Master:
self.should_exit.set()
self.addons.done()
- def create_request(self, method, scheme, host, port, path):
+ def create_request(self, method, url):
"""
- this method creates a new artificial and minimalist request also adds it to flowlist
+ Create a new artificial and minimalist request also adds it to flowlist.
+
+ Raises:
+ ValueError, if the url is malformed.
"""
+ req = http.HTTPRequest.make(method, url)
c = connections.ClientConnection.make_dummy(("", 0))
- s = connections.ServerConnection.make_dummy((host, port))
+ s = connections.ServerConnection.make_dummy((req.host, req.port))
f = http.HTTPFlow(c, s)
- headers = mitmproxy.net.http.Headers()
-
- req = http.HTTPRequest(
- "absolute",
- method,
- scheme,
- host,
- port,
- path,
- b"HTTP/1.1",
- headers,
- b""
- )
f.request = req
self.load_flow(f)
return f
@@ -160,33 +145,6 @@ class Master:
for e, o in eventsequence.iterate(f):
getattr(self, e)(o)
- def load_flows(self, fr: io.FlowReader) -> int:
- """
- Load flows from a FlowReader object.
- """
- cnt = 0
- for i in fr.stream():
- cnt += 1
- self.load_flow(i)
- return cnt
-
- def load_flows_file(self, path: str) -> int:
- path = os.path.expanduser(path)
- try:
- if path == "-":
- try:
- sys.stdin.buffer.read(0)
- except Exception as e:
- raise IOError("Cannot read from stdin: {}".format(e))
- freader = io.FlowReader(sys.stdin.buffer)
- return self.load_flows(freader)
- else:
- with open(path, "rb") as f:
- freader = io.FlowReader(f)
- return self.load_flows(freader)
- except IOError as v:
- raise exceptions.FlowReadException(v.strerror)
-
def replay_request(
self,
f: http.HTTPFlow,
diff --git a/mitmproxy/net/http/request.py b/mitmproxy/net/http/request.py
index b961e1e4..90a1f924 100644
--- a/mitmproxy/net/http/request.py
+++ b/mitmproxy/net/http/request.py
@@ -1,6 +1,6 @@
import re
import urllib
-from typing import Optional
+from typing import Optional, AnyStr, Dict, Iterable, Tuple, Union
from mitmproxy.types import multidict
from mitmproxy.utils import strutils
@@ -77,6 +77,53 @@ class Request(message.Message):
self.method, hostport, path
)
+ @classmethod
+ def make(
+ cls,
+ method: str,
+ url: str,
+ content: AnyStr = b"",
+ headers: Union[Dict[AnyStr, AnyStr], Iterable[Tuple[bytes, bytes]]] = ()
+ ):
+ """
+ Simplified API for creating request objects.
+ """
+ req = cls(
+ "absolute",
+ method,
+ "",
+ "",
+ "",
+ "",
+ "HTTP/1.1",
+ (),
+ b""
+ )
+
+ req.url = url
+
+ # Headers can be list or dict, we differentiate here.
+ if isinstance(headers, dict):
+ req.headers = nheaders.Headers(**headers)
+ elif isinstance(headers, Iterable):
+ req.headers = nheaders.Headers(headers)
+ else:
+ raise TypeError("Expected headers to be an iterable or dict, but is {}.".format(
+ type(headers).__name__
+ ))
+
+ # Assign this manually to update the content-length header.
+ if isinstance(content, bytes):
+ req.content = content
+ elif isinstance(content, str):
+ req.text = content
+ else:
+ raise TypeError("Expected content to be str or bytes, but is {}.".format(
+ type(content).__name__
+ ))
+
+ return req
+
def replace(self, pattern, repl, flags=0, count=0):
"""
Replaces a regular expression pattern with repl in the headers, the
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/console/flowlist.py b/mitmproxy/tools/console/flowlist.py
index d2e28d35..5fe86975 100644
--- a/mitmproxy/tools/console/flowlist.py
+++ b/mitmproxy/tools/console/flowlist.py
@@ -1,6 +1,5 @@
import urwid
-import mitmproxy.net.http.url
from mitmproxy import exceptions
from mitmproxy.tools.console import common
from mitmproxy.tools.console import signals
@@ -339,12 +338,10 @@ class FlowListBox(urwid.ListBox):
def new_request(self, url, method):
try:
- parts = mitmproxy.net.http.url.parse(str(url))
+ f = self.master.create_request(method, url)
except ValueError as e:
signals.status_message.send(message = "Invalid URL: " + str(e))
return
- scheme, host, port, path = parts
- f = self.master.create_request(method, scheme, host, port, path)
self.master.view.focus.flow = f
def keypress(self, size, key):
diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py
index e75105cf..b6339817 100644
--- a/mitmproxy/tools/console/master.py
+++ b/mitmproxy/tools/console/master.py
@@ -256,19 +256,6 @@ class ConsoleMaster(master.Master):
)
self.ab = statusbar.ActionBar()
- if self.options.rfile:
- ret = self.load_flows_path(self.options.rfile)
- if ret and self.view.store_count():
- signals.add_log(
- "File truncated or corrupted. "
- "Loaded as many flows as possible.",
- "error"
- )
- elif ret and not self.view.store_count():
- self.shutdown()
- print("Could not load file: {}".format(ret), file=sys.stderr)
- sys.exit(1)
-
self.loop.set_alarm_in(0.01, self.ticker)
self.loop.set_alarm_in(
@@ -289,7 +276,10 @@ class ConsoleMaster(master.Master):
print("Shutting down...", file=sys.stderr)
finally:
sys.stderr.flush()
- self.shutdown()
+ super().shutdown()
+
+ def shutdown(self):
+ raise urwid.ExitMainLoop
def view_help(self, helpctx):
signals.push_view_state.send(
@@ -402,7 +392,7 @@ class ConsoleMaster(master.Master):
def quit(self, a):
if a != "n":
- raise urwid.ExitMainLoop
+ self.shutdown()
def clear_events(self):
self.logbuffer[:] = []
diff --git a/mitmproxy/tools/dump.py b/mitmproxy/tools/dump.py
index 4bfe2dc4..42930a7e 100644
--- a/mitmproxy/tools/dump.py
+++ b/mitmproxy/tools/dump.py
@@ -1,9 +1,8 @@
from mitmproxy import controller
-from mitmproxy import exceptions
from mitmproxy import addons
from mitmproxy import options
from mitmproxy import master
-from mitmproxy.addons import dumper, termlog, termstatus
+from mitmproxy.addons import dumper, termlog, termstatus, readstdin, keepserving
class DumpMaster(master.Master):
@@ -22,21 +21,9 @@ class DumpMaster(master.Master):
self.addons.add(*addons.default_addons())
if with_dumper:
self.addons.add(dumper.Dumper())
-
- if options.rfile:
- try:
- self.load_flows_file(options.rfile)
- except exceptions.FlowReadException as v:
- self.add_log("Flow file corrupted.", "error")
- raise exceptions.OptionsError(v)
+ self.addons.add(readstdin.ReadStdin(), keepserving.KeepServing())
@controller.handler
def log(self, e):
if e.level == "error":
self.has_errored = True
-
- def run(self): # pragma: no cover
- if self.options.rfile and not self.options.keepserving:
- self.addons.done()
- return
- super().run()
diff --git a/mitmproxy/tools/web/app.py b/mitmproxy/tools/web/app.py
index eddaa3e1..002513b9 100644
--- a/mitmproxy/tools/web/app.py
+++ b/mitmproxy/tools/web/app.py
@@ -230,7 +230,8 @@ class DumpFlows(RequestHandler):
def post(self):
self.view.clear()
bio = BytesIO(self.filecontents)
- self.master.load_flows(io.FlowReader(bio))
+ for i in io.FlowReader(bio).stream():
+ self.master.load_flow(i)
bio.close()
diff --git a/mitmproxy/tools/web/master.py b/mitmproxy/tools/web/master.py
index 8c7f579d..e28bd002 100644
--- a/mitmproxy/tools/web/master.py
+++ b/mitmproxy/tools/web/master.py
@@ -3,7 +3,6 @@ import webbrowser
import tornado.httpserver
import tornado.ioloop
from mitmproxy import addons
-from mitmproxy import exceptions
from mitmproxy import log
from mitmproxy import master
from mitmproxy.addons import eventstore
@@ -42,14 +41,6 @@ class WebMaster(master.Master):
)
# This line is just for type hinting
self.options = self.options # type: Options
- if options.rfile:
- try:
- self.load_flows_file(options.rfile)
- except exceptions.FlowReadException as v:
- self.add_log(
- "Could not read flow file: %s" % v,
- "error"
- )
def _sig_view_add(self, view, flow):
app.ClientConnection.broadcast(
diff --git a/release/README.md b/release/README.md
index 02fabfe9..a30221c8 100644
--- a/release/README.md
+++ b/release/README.md
@@ -1,14 +1,25 @@
# Release Checklist
+Make sure run all these steps on the correct branch you want to create a new release for!
+- Verify `mitmproxy/version.py`
- Update CHANGELOG
-- Verify that all CI tests pass for current master
-- Tag the release, and push to Github
+- Verify that all CI tests pass
+- Tag the release and push to Github
- Wait for tag CI to complete
-- Download assets from snapshots.mitmproxy.org
-- Create release notice on Github
-- Upload wheel to pypi (`twine upload wheelname`)
+
+## GitHub Release
+- Create release notice on Github [https://github.com/mitmproxy/mitmproxy/releases/new](here)
+- Attach all files from the new release folder on https://snapshots.mitmproxy.org
+
+## PyPi
+- Upload wheel to pypi: `twine upload <mitmproxy-...-.whl`
+
+## Docker
- Update docker-releases repo
- Create a new branch based of master for major versions.
- - Add a commit that pins dependencies like so: https://github.com/mitmproxy/docker-releases/commit/3d6a9989fde068ad0aea257823ac3d7986ff1613. The requirements can be obtained by creating a fresh venv, pip-installing the new wheel in there, and then running `pip freeze`.
-- Update `latest` tag on https://hub.docker.com/r/mitmproxy/mitmproxy/~/settings/automated-builds/
-- Bump the version in https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/version.py and update https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/io_compat.py in the next commit
+ - Update the dependencies in [alpine/requirements.txt](https://github.com/mitmproxy/docker-releases/commit/3d6a9989fde068ad0aea257823ac3d7986ff1613#diff-9b7e0eea8ae74688b1ac13ea080549ba)
+ * Creating a fresh venv, pip-installing the new wheel in there, and then export all packages:
+ * `virtualenv -ppython3.5 venv && source venv/bin/activate && pip install mitmproxy && pip freeze`
+- Update `latest` tag [https://hub.docker.com/r/mitmproxy/mitmproxy/~/settings/automated-builds/](here)
+
+After everything is done, you might want to bump the version on master in [https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/version.py](mitmproxy/version.py) if you just created a major release.
diff --git a/setup.py b/setup.py
index bbbac95e..9ea69f67 100644
--- a/setup.py
+++ b/setup.py
@@ -64,17 +64,17 @@ setup(
"click>=6.2, <7",
"certifi>=2015.11.20.1", # no semver here - this should always be on the last release!
"construct>=2.8, <2.9",
- "cryptography>=1.3, <1.8",
+ "cryptography>=1.3, <1.9",
"cssutils>=1.0.1, <1.1",
- "h2>=2.5.1, <3",
+ "h2>=2.6.1, <3",
"html2text>=2016.1.8, <=2016.9.19",
- "hyperframe>=4.0.1, <5",
+ "hyperframe>=4.0.2, <6",
"jsbeautifier>=1.6.3, <1.7",
"kaitaistruct>=0.6, <0.7",
"passlib>=1.6.5, <1.8",
"pyasn1>=0.1.9, <0.3",
"pyOpenSSL>=16.0, <17.0",
- "pyparsing>=2.1.3, <2.2",
+ "pyparsing>=2.1.3, <2.3",
"pyperclip>=1.5.22, <1.6",
"requests>=2.9.1, <3",
"ruamel.yaml>=0.13.2, <0.14",
@@ -96,7 +96,7 @@ setup(
'dev': [
"Flask>=0.10.1, <0.13",
"flake8>=3.2.1, <3.4",
- "mypy>=0.471, <0.480",
+ "mypy>=0.471, <0.502",
"rstcheck>=2.2, <4.0",
"tox>=2.3, <3",
"pytest>=3, <3.1",
@@ -106,8 +106,8 @@ setup(
"pytest-faulthandler>=1.3.0, <2",
"sphinx>=1.3.5, <1.6",
"sphinx-autobuild>=0.5.2, <0.7",
- "sphinxcontrib-documentedlist>=0.5.0, <0.6",
- "sphinx_rtd_theme>=0.1.9, <0.2",
+ "sphinxcontrib-documentedlist>=0.5.0, <0.7",
+ "sphinx_rtd_theme>=0.1.9, <0.3",
],
'contentviews': [
],
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
new file mode 100644
index 00000000..b30c147b
--- /dev/null
+++ b/test/mitmproxy/addons/test_readfile.py
@@ -0,0 +1,62 @@
+from mitmproxy.addons import readfile
+from mitmproxy.test import taddons
+from mitmproxy.test import tflow
+from mitmproxy import io
+from mitmproxy import exceptions
+from unittest import mock
+
+import pytest
+
+
+def write_data(path, corrupt=False):
+ with open(path, "wb") as tf:
+ w = io.FlowWriter(tf)
+ for i in range(3):
+ f = tflow.tflow(resp=True)
+ w.add(f)
+ for i in range(3):
+ f = tflow.tflow(err=True)
+ w.add(f)
+ f = tflow.ttcpflow()
+ w.add(f)
+ f = tflow.ttcpflow(err=True)
+ w.add(f)
+ if corrupt:
+ tf.write(b"flibble")
+
+
+@mock.patch('mitmproxy.master.Master.load_flow')
+def test_configure(mck, tmpdir):
+
+ rf = readfile.ReadFile()
+ with taddons.context() as tctx:
+ tf = str(tmpdir.join("tfile"))
+ write_data(tf)
+ 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))
+ with pytest.raises(exceptions.OptionsError):
+ rf.running()
+
+
+@mock.patch('mitmproxy.master.Master.load_flow')
+def test_corruption(mck, tmpdir):
+
+ rf = readfile.ReadFile()
+ with taddons.context() as tctx:
+ with pytest.raises(exceptions.FlowReadException):
+ rf.load_flows_file("nonexistent")
+ assert not mck.called
+ assert len(tctx.master.logs) == 1
+
+ tfc = str(tmpdir.join("tfile"))
+ write_data(tfc, corrupt=True)
+
+ with pytest.raises(exceptions.FlowReadException):
+ rf.load_flows_file(tfc)
+ assert mck.called
+ assert len(tctx.master.logs) == 2
diff --git a/test/mitmproxy/addons/test_readstdin.py b/test/mitmproxy/addons/test_readstdin.py
new file mode 100644
index 00000000..76b01f4f
--- /dev/null
+++ b/test/mitmproxy/addons/test_readstdin.py
@@ -0,0 +1,53 @@
+
+import io
+from mitmproxy.addons import readstdin
+from mitmproxy.test import taddons
+from mitmproxy.test import tflow
+import mitmproxy.io
+from unittest import mock
+
+
+def gen_data(corrupt=False):
+ tf = io.BytesIO()
+ w = mitmproxy.io.FlowWriter(tf)
+ for i in range(3):
+ f = tflow.tflow(resp=True)
+ w.add(f)
+ for i in range(3):
+ f = tflow.tflow(err=True)
+ w.add(f)
+ f = tflow.ttcpflow()
+ w.add(f)
+ f = tflow.ttcpflow(err=True)
+ w.add(f)
+ if corrupt:
+ tf.write(b"flibble")
+ tf.seek(0)
+ return tf
+
+
+class mStdin:
+ def __init__(self, d):
+ self.buffer = d
+
+ def isatty(self):
+ return False
+
+
+@mock.patch('mitmproxy.master.Master.load_flow')
+def test_read(m, tmpdir):
+ rf = readstdin.ReadStdin()
+ with taddons.context() as tctx:
+ assert not m.called
+ rf.running(stdin=mStdin(gen_data()))
+ assert m.called
+
+ rf.running(stdin=mStdin(None))
+ 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.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/contentviews/test_xml_html.py b/test/mitmproxy/contentviews/test_xml_html.py
index 899ecfde..2b0aee4d 100644
--- a/test/mitmproxy/contentviews/test_xml_html.py
+++ b/test/mitmproxy/contentviews/test_xml_html.py
@@ -23,7 +23,7 @@ def test_format_xml(filename):
path = data.path(filename)
with open(path) as f:
input = f.read()
- with open(path.replace(".", "-formatted.")) as f:
+ with open("-formatted.".join(path.rsplit(".", 1))) as f:
expected = f.read()
tokens = xml_html.tokenize(input)
assert xml_html.format_xml(tokens) == expected
diff --git a/test/mitmproxy/net/http/test_request.py b/test/mitmproxy/net/http/test_request.py
index 90ec31fe..ce49002c 100644
--- a/test/mitmproxy/net/http/test_request.py
+++ b/test/mitmproxy/net/http/test_request.py
@@ -1,7 +1,7 @@
from unittest import mock
import pytest
-from mitmproxy.net.http import Headers
+from mitmproxy.net.http import Headers, Request
from mitmproxy.test.tutils import treq
from .test_message import _test_decoded_attr, _test_passthrough_attr
@@ -35,6 +35,32 @@ class TestRequestCore:
request.host = None
assert repr(request) == "Request(GET /path)"
+ def test_make(self):
+ r = Request.make("GET", "https://example.com/")
+ assert r.method == "GET"
+ assert r.scheme == "https"
+ assert r.host == "example.com"
+ assert r.port == 443
+ assert r.path == "/"
+
+ r = Request.make("GET", "https://example.com/", "content", {"Foo": "bar"})
+ assert r.content == b"content"
+ assert r.headers["content-length"] == "7"
+ assert r.headers["Foo"] == "bar"
+
+ Request.make("GET", "https://example.com/", content=b"content")
+ with pytest.raises(TypeError):
+ Request.make("GET", "https://example.com/", content=42)
+
+ r = Request.make("GET", "https://example.com/", headers=[(b"foo", b"bar")])
+ assert r.headers["foo"] == "bar"
+
+ r = Request.make("GET", "https://example.com/", headers=({"foo": "baz"}))
+ assert r.headers["foo"] == "baz"
+
+ with pytest.raises(TypeError):
+ Request.make("GET", "https://example.com/", headers=42)
+
def test_replace(self):
r = treq()
r.path = b"foobarfoo"
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 3e5f71c6..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")
@@ -30,6 +37,11 @@ def test_simple():
assert not a.chain
a.add(TAddon("one"))
- a("done")
+ a.trigger("done")
with pytest.raises(exceptions.AddonError):
- a("tick")
+ a.trigger("tick")
+
+ ta = TAddon("one")
+ a.add(ta)
+ a.trigger("custom")
+ assert ta.custom_called
diff --git a/test/mitmproxy/test_examples.py b/test/mitmproxy/test_examples.py
index f20e0c8c..56692364 100644
--- a/test/mitmproxy/test_examples.py
+++ b/test/mitmproxy/test_examples.py
@@ -145,7 +145,7 @@ class TestHARDump:
path = str(tmpdir.join("somefile"))
m, sc = tscript("complex/har_dump.py", shlex.quote(path))
- m.addons.invoke(m, "response", self.flow())
+ m.addons.trigger("response", self.flow())
m.addons.remove(sc)
with open(path, "r") as inp:
@@ -156,7 +156,9 @@ class TestHARDump:
path = str(tmpdir.join("somefile"))
m, sc = tscript("complex/har_dump.py", shlex.quote(path))
- m.addons.invoke(m, "response", self.flow(resp_content=b"foo" + b"\xFF" * 10))
+ m.addons.trigger(
+ "response", self.flow(resp_content=b"foo" + b"\xFF" * 10)
+ )
m.addons.remove(sc)
with open(path, "r") as inp:
@@ -194,7 +196,7 @@ class TestHARDump:
path = str(tmpdir.join("somefile"))
m, sc = tscript("complex/har_dump.py", shlex.quote(path))
- m.addons.invoke(m, "response", f)
+ m.addons.trigger("response", f)
m.addons.remove(sc)
with open(path, "r") as inp:
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index f4d32cbb..4f87a6ae 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -3,12 +3,13 @@ import pytest
from mitmproxy.test import tflow
import mitmproxy.io
-from mitmproxy import flowfilter, options
+from mitmproxy import flowfilter
+from mitmproxy import options
+from mitmproxy.proxy import config
from mitmproxy.contrib import tnetstring
from mitmproxy.exceptions import FlowReadException
from mitmproxy import flow
from mitmproxy import http
-from mitmproxy.proxy import ProxyConfig
from mitmproxy.proxy.server import DummyServer
from mitmproxy import master
from . import tservers
@@ -16,23 +17,6 @@ from . import tservers
class TestSerialize:
- def _treader(self):
- sio = io.BytesIO()
- w = mitmproxy.io.FlowWriter(sio)
- for i in range(3):
- f = tflow.tflow(resp=True)
- w.add(f)
- for i in range(3):
- f = tflow.tflow(err=True)
- w.add(f)
- f = tflow.ttcpflow()
- w.add(f)
- f = tflow.ttcpflow(err=True)
- w.add(f)
-
- sio.seek(0)
- return mitmproxy.io.FlowReader(sio)
-
def test_roundtrip(self):
sio = io.BytesIO()
f = tflow.tflow()
@@ -51,26 +35,6 @@ class TestSerialize:
assert f2.request == f.request
assert f2.marked
- def test_load_flows(self):
- r = self._treader()
- s = tservers.TestState()
- fm = master.Master(None, DummyServer())
- fm.addons.add(s)
- fm.load_flows(r)
- assert len(s.flows) == 6
-
- def test_load_flows_reverse(self):
- r = self._treader()
- s = tservers.TestState()
- opts = options.Options(
- mode="reverse:https://use-this-domain"
- )
- conf = ProxyConfig(opts)
- fm = master.Master(opts, DummyServer(conf))
- fm.addons.add(s)
- fm.load_flows(r)
- assert s.flows[0].request.host == "use-this-domain"
-
def test_filter(self):
sio = io.BytesIO()
flt = flowfilter.parse("~c 200")
@@ -122,6 +86,17 @@ class TestSerialize:
class TestFlowMaster:
+ def test_load_flow_reverse(self):
+ s = tservers.TestState()
+ opts = options.Options(
+ mode="reverse:https://use-this-domain"
+ )
+ conf = config.ProxyConfig(opts)
+ fm = master.Master(opts, DummyServer(conf))
+ fm.addons.add(s)
+ f = tflow.tflow(resp=True)
+ fm.load_flow(f)
+ assert s.flows[0].request.host == "use-this-domain"
def test_replay(self):
fm = master.Master(None, DummyServer())
@@ -140,7 +115,7 @@ class TestFlowMaster:
def test_create_flow(self):
fm = master.Master(None, DummyServer())
- assert fm.create_request("GET", "http", "example.com", 80, "/")
+ assert fm.create_request("GET", "http://example.com/")
def test_all(self):
s = tservers.TestState()
diff --git a/test/mitmproxy/tools/console/test_master.py b/test/mitmproxy/tools/console/test_master.py
index 6c716ad1..45908450 100644
--- a/test/mitmproxy/tools/console/test_master.py
+++ b/test/mitmproxy/tools/console/test_master.py
@@ -5,6 +5,7 @@ from mitmproxy import proxy
from mitmproxy import options
from mitmproxy.tools.console import common
from ... import tservers
+import urwid
def test_format_keyvals():
@@ -35,7 +36,10 @@ class TestMaster(tservers.MasterTest):
def test_basic(self):
m = self.mkmaster()
for i in (1, 2, 3):
- self.dummy_cycle(m, 1, b"")
+ try:
+ self.dummy_cycle(m, 1, b"")
+ except urwid.ExitMainLoop:
+ pass
assert len(m.view) == i
def test_run_script_once(self):
diff --git a/test/mitmproxy/tools/test_dump.py b/test/mitmproxy/tools/test_dump.py
index a15bf583..624bf08f 100644
--- a/test/mitmproxy/tools/test_dump.py
+++ b/test/mitmproxy/tools/test_dump.py
@@ -2,7 +2,6 @@ import pytest
from unittest import mock
from mitmproxy import proxy
-from mitmproxy import exceptions
from mitmproxy import log
from mitmproxy import controller
from mitmproxy import options
@@ -17,18 +16,6 @@ class TestDumpMaster(tservers.MasterTest):
m = dump.DumpMaster(o, proxy.DummyServer(), with_termlog=False, with_dumper=False)
return m
- def test_read(self, tmpdir):
- p = str(tmpdir.join("read"))
- self.flowfile(p)
- self.dummy_cycle(
- self.mkmaster(None, rfile=p),
- 1, b"",
- )
- with pytest.raises(exceptions.OptionsError):
- self.mkmaster(None, rfile="/nonexistent")
- with pytest.raises(exceptions.OptionsError):
- self.mkmaster(None, rfile="test_dump.py")
-
def test_has_error(self):
m = self.mkmaster(None)
ent = log.LogEntry("foo", "error")
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index c47411ee..0f34e37e 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -80,7 +80,7 @@ class TestMaster(master.Master):
self.addons.add(self.state)
self.addons.add(*addons)
self.addons.configure_all(self.options, self.options.keys())
- self.addons.invoke_all_with_context("running")
+ self.addons.trigger("running")
def clear_log(self):
self.tlog = []