aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--mitmproxy/addons/core_option_validation.py6
-rw-r--r--mitmproxy/addons/proxyauth.py2
-rw-r--r--mitmproxy/addons/script.py5
-rw-r--r--mitmproxy/certs.py8
-rw-r--r--mitmproxy/log.py5
-rw-r--r--mitmproxy/master.py36
-rw-r--r--mitmproxy/options.py25
-rw-r--r--mitmproxy/optmanager.py11
-rw-r--r--mitmproxy/proxy/config.py11
-rw-r--r--mitmproxy/proxy/protocol/http1.py5
-rw-r--r--mitmproxy/proxy/protocol/http2.py3
-rw-r--r--mitmproxy/proxy/protocol/http_replay.py32
-rw-r--r--mitmproxy/proxy/root_context.py11
-rw-r--r--mitmproxy/test/taddons.py10
-rw-r--r--mitmproxy/tools/cmdline.py1
-rw-r--r--mitmproxy/tools/console/consoleaddons.py6
-rw-r--r--mitmproxy/tools/console/defaultkeys.py2
-rw-r--r--mitmproxy/tools/console/master.py24
-rw-r--r--mitmproxy/tools/console/statusbar.py8
-rw-r--r--mitmproxy/tools/dump.py3
-rw-r--r--mitmproxy/tools/main.py50
-rw-r--r--mitmproxy/tools/web/master.py9
-rw-r--r--mitmproxy/tools/web/webaddons.py18
-rw-r--r--mitmproxy/utils/arg_check.py148
-rw-r--r--mitmproxy/utils/human.py11
-rw-r--r--setup.py6
-rw-r--r--test/filename_matching.py2
-rw-r--r--test/mitmproxy/addons/test_core_option_validation.py1
-rw-r--r--test/mitmproxy/addons/test_proxyauth.py421
-rw-r--r--test/mitmproxy/addons/test_script.py27
-rw-r--r--test/mitmproxy/addons/test_termstatus.py2
-rw-r--r--test/mitmproxy/data/addonscripts/load_error.py2
-rw-r--r--test/mitmproxy/proxy/protocol/test_http2.py14
-rw-r--r--test/mitmproxy/proxy/protocol/test_websocket.py7
-rw-r--r--test/mitmproxy/proxy/test_server.py37
-rw-r--r--test/mitmproxy/test_addonmanager.py9
-rw-r--r--test/mitmproxy/test_controller.py3
-rw-r--r--test/mitmproxy/test_flow.py10
-rw-r--r--test/mitmproxy/test_log.py5
-rw-r--r--test/mitmproxy/test_optmanager.py6
-rw-r--r--test/mitmproxy/tools/console/conftest.py9
-rw-r--r--test/mitmproxy/tools/console/test_master.py3
-rw-r--r--test/mitmproxy/tools/console/test_statusbar.py4
-rw-r--r--test/mitmproxy/tools/test_cmdline.py8
-rw-r--r--test/mitmproxy/tools/test_dump.py7
-rw-r--r--test/mitmproxy/tools/web/test_app.py5
-rw-r--r--test/mitmproxy/tools/web/test_master.py5
-rw-r--r--test/mitmproxy/tservers.py21
-rw-r--r--test/mitmproxy/utils/test_arg_check.py36
-rw-r--r--test/mitmproxy/utils/test_human.py1
51 files changed, 706 insertions, 399 deletions
diff --git a/.travis.yml b/.travis.yml
index 51c16aa5..63202320 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@ git:
matrix:
fast_finish: true
include:
- - python: 3.5
+ - python: 3.6
env: TOXENV=lint
- os: osx
osx_image: xcode7.3
@@ -28,7 +28,7 @@ matrix:
env: TOXENV=py36
- python: 3.6
env: TOXENV=individual_coverage
- - python: 3.5
+ - python: 3.6
env: TOXENV=docs
- language: node_js
node_js: "node"
diff --git a/mitmproxy/addons/core_option_validation.py b/mitmproxy/addons/core_option_validation.py
index baeee764..42da0b74 100644
--- a/mitmproxy/addons/core_option_validation.py
+++ b/mitmproxy/addons/core_option_validation.py
@@ -19,11 +19,9 @@ class CoreOptionValidation:
"then the upstream certificate is not retrieved before generating "
"the client certificate chain."
)
- if "body_size_limit" in updated and opts.body_size_limit:
+ if "body_size_limit" in updated:
try:
- opts._processed["body_size_limit"] = human.parse_size(
- opts.body_size_limit
- )
+ human.parse_size(opts.body_size_limit)
except ValueError as e:
raise exceptions.OptionsError(
"Invalid body size limit specification: %s" %
diff --git a/mitmproxy/addons/proxyauth.py b/mitmproxy/addons/proxyauth.py
index 5f884b55..64233e88 100644
--- a/mitmproxy/addons/proxyauth.py
+++ b/mitmproxy/addons/proxyauth.py
@@ -61,7 +61,7 @@ class ProxyAuth:
- True, if authentication is done as if mitmproxy is a proxy
- False, if authentication is done as if mitmproxy is a HTTP server
"""
- return ctx.options.mode in ("regular", "upstream")
+ return ctx.options.mode == "regular" or ctx.options.mode.startswith("upstream:")
def which_auth_header(self) -> str:
if self.is_proxy_auth():
diff --git a/mitmproxy/addons/script.py b/mitmproxy/addons/script.py
index 58e8cdcd..2d030321 100644
--- a/mitmproxy/addons/script.py
+++ b/mitmproxy/addons/script.py
@@ -72,11 +72,12 @@ class Script:
ctx.master.addons.remove(self.ns)
self.ns = None
with addonmanager.safecall():
- self.ns = load_script(self.fullpath)
+ ns = load_script(self.fullpath)
+ ctx.master.addons.register(ns)
+ self.ns = ns
if self.ns:
# We're already running, so we have to explicitly register and
# configure the addon
- ctx.master.addons.register(self.ns)
ctx.master.addons.invoke_addon(self.ns, "running")
ctx.master.addons.invoke_addon(
self.ns,
diff --git a/mitmproxy/certs.py b/mitmproxy/certs.py
index 0e441efe..c5f930e1 100644
--- a/mitmproxy/certs.py
+++ b/mitmproxy/certs.py
@@ -480,10 +480,8 @@ class SSLCert(serializable.Serializable):
except PyAsn1Error:
continue
for i in dec[0]:
- if i[0] is None and isinstance(i[1], univ.OctetString) and not isinstance(i[1], char.IA5String):
- # This would give back the IP address: b'.'.join([str(e).encode() for e in i[1].asNumbers()])
- continue
- else:
+ if i[0].hasValue():
e = i[0].asOctets()
- altnames.append(e)
+ altnames.append(e)
+
return altnames
diff --git a/mitmproxy/log.py b/mitmproxy/log.py
index 886b1449..3083a000 100644
--- a/mitmproxy/log.py
+++ b/mitmproxy/log.py
@@ -4,6 +4,11 @@ class LogEntry:
self.msg = msg
self.level = level
+ def __eq__(self, other):
+ if isinstance(other, LogEntry):
+ return self.__dict__ == other.__dict__
+ return False
+
def __repr__(self):
return "LogEntry({}, {})".format(self.msg, self.level)
diff --git a/mitmproxy/master.py b/mitmproxy/master.py
index 2bc78f4b..b41e2a8d 100644
--- a/mitmproxy/master.py
+++ b/mitmproxy/master.py
@@ -10,6 +10,7 @@ from mitmproxy import exceptions
from mitmproxy import command
from mitmproxy import http
from mitmproxy import log
+from mitmproxy.net import server_spec
from mitmproxy.proxy.protocol import http_replay
from mitmproxy.types import basethread
@@ -32,16 +33,25 @@ class Master:
"""
The master handles mitmproxy's main event loop.
"""
- def __init__(self, opts, server):
- self.options = opts or options.Options()
+ def __init__(self, opts):
+ self.options = opts or options.Options() # type: options.Options
self.commands = command.CommandManager(self)
self.addons = addonmanager.AddonManager(self)
self.event_queue = queue.Queue()
self.should_exit = threading.Event()
- self.server = server
+ self._server = None
self.first_tick = True
- channel = controller.Channel(self.event_queue, self.should_exit)
- server.set_channel(channel)
+
+ @property
+ def server(self):
+ return self._server
+
+ @server.setter
+ def server(self, server):
+ server.set_channel(
+ controller.Channel(self.event_queue, self.should_exit)
+ )
+ self._server = server
@contextlib.contextmanager
def handlecontext(self):
@@ -71,7 +81,8 @@ class Master:
def start(self):
self.should_exit.clear()
- ServerThread(self.server).start()
+ if self.server:
+ ServerThread(self.server).start()
def run(self):
self.start()
@@ -101,7 +112,8 @@ class Master:
return changed
def shutdown(self):
- self.server.shutdown()
+ if self.server:
+ self.server.shutdown()
self.should_exit.set()
self.addons.trigger("done")
@@ -110,10 +122,10 @@ class Master:
Loads a flow
"""
if isinstance(f, http.HTTPFlow):
- if self.server and self.options.mode.startswith("reverse:"):
- f.request.host = self.server.config.upstream_server.address[0]
- f.request.port = self.server.config.upstream_server.address[1]
- f.request.scheme = self.server.config.upstream_server.scheme
+ if self.options.mode.startswith("reverse:"):
+ _, upstream_spec = server_spec.parse_with_mode(self.options.mode)
+ f.request.host, f.request.port = upstream_spec.address
+ f.request.scheme = upstream_spec.scheme
f.reply = controller.DummyReply()
for e, o in eventsequence.iterate(f):
self.addons.handle_lifecycle(e, o)
@@ -168,7 +180,7 @@ class Master:
f.request.headers.insert(0, "host", host)
rt = http_replay.RequestReplayThread(
- self.server.config,
+ self.options,
f,
self.event_queue,
self.should_exit
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index 10aaee12..1ecdd6a6 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -173,7 +173,7 @@ class Options(optmanager.OptManager):
)
self.add_option(
"server", bool, True,
- "Start a proxy server."
+ "Start a proxy server. Enabled by default."
)
self.add_option(
"server_replay_nopop", bool, False,
@@ -406,8 +406,9 @@ class Options(optmanager.OptManager):
)
self.add_option(
"rawtcp", bool, False,
- "Enable/disable experimental raw TCP support. "
- "Disabled by default. "
+ "Enable/disable experimental raw TCP support. TCP connections starting with non-ascii "
+ "bytes are treated as if they would match tcp_hosts. The heuristic is very rough, use "
+ "with caution. Disabled by default. "
)
self.add_option(
@@ -516,24 +517,6 @@ class Options(optmanager.OptManager):
"Limit which flows are displayed."
)
- # Web options
- self.add_option(
- "web_open_browser", bool, True,
- "Start a browser."
- )
- self.add_option(
- "web_debug", bool, False,
- "Mitmweb debugging."
- )
- self.add_option(
- "web_port", int, 8081,
- "Mitmweb port."
- )
- self.add_option(
- "web_iface", str, "127.0.0.1",
- "Mitmweb interface."
- )
-
# Dump options
self.add_option(
"flow_detail", int, 1,
diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py
index 84c8d2ea..01d97af3 100644
--- a/mitmproxy/optmanager.py
+++ b/mitmproxy/optmanager.py
@@ -94,7 +94,6 @@ class OptManager:
self.__dict__["_options"] = {}
self.__dict__["changed"] = blinker.Signal()
self.__dict__["errored"] = blinker.Signal()
- self.__dict__["_processed"] = {}
def add_option(
self,
@@ -151,13 +150,17 @@ class OptManager:
self.changed.connect(_call, weak=False)
def __eq__(self, other):
- return self._options == other._options
+ if isinstance(other, OptManager):
+ return self._options == other._options
+ return False
- def __copy__(self):
+ def __deepcopy__(self, memodict = None):
o = OptManager()
- o.__dict__["_options"] = copy.deepcopy(self._options)
+ o.__dict__["_options"] = copy.deepcopy(self._options, memodict)
return o
+ __copy__ = __deepcopy__
+
def __getattr__(self, attr):
if attr in self._options:
return self._options[attr].current()
diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py
index b809d89a..9458cd42 100644
--- a/mitmproxy/proxy/config.py
+++ b/mitmproxy/proxy/config.py
@@ -1,6 +1,6 @@
import os
import re
-from typing import Any
+import typing
from OpenSSL import SSL, crypto
@@ -42,10 +42,11 @@ class ProxyConfig:
self.certstore = None # type: certs.CertStore
self.client_certs = None # type: str
self.openssl_verification_mode_server = None # type: int
+ self.upstream_server = None # type: typing.Optional[server_spec.ServerSpec]
self.configure(options, set(options.keys()))
options.changed.connect(self.configure)
- def configure(self, options: moptions.Options, updated: Any) -> None:
+ def configure(self, options: moptions.Options, updated: typing.Any) -> None:
if options.add_upstream_certs_to_client_chain and not options.ssl_insecure:
raise exceptions.OptionsError(
"The verify-upstream-cert requires certificate verification to be disabled. "
@@ -58,8 +59,10 @@ class ProxyConfig:
else:
self.openssl_verification_mode_server = SSL.VERIFY_PEER
- self.check_ignore = HostMatcher(options.ignore_hosts)
- self.check_tcp = HostMatcher(options.tcp_hosts)
+ if "ignore_hosts" in updated:
+ self.check_ignore = HostMatcher(options.ignore_hosts)
+ if "tcp_hosts" in updated:
+ self.check_tcp = HostMatcher(options.tcp_hosts)
self.openssl_method_client, self.openssl_options_client = \
tcp.sslversion_choices[options.ssl_version_client]
diff --git a/mitmproxy/proxy/protocol/http1.py b/mitmproxy/proxy/protocol/http1.py
index 84cd6324..91f1e9b7 100644
--- a/mitmproxy/proxy/protocol/http1.py
+++ b/mitmproxy/proxy/protocol/http1.py
@@ -1,6 +1,7 @@
from mitmproxy import http
from mitmproxy.proxy.protocol import http as httpbase
from mitmproxy.net.http import http1
+from mitmproxy.utils import human
class Http1Layer(httpbase._HttpTransmissionLayer):
@@ -19,7 +20,7 @@ class Http1Layer(httpbase._HttpTransmissionLayer):
return http1.read_body(
self.client_conn.rfile,
expected_size,
- self.config.options._processed.get("body_size_limit")
+ human.parse_size(self.config.options.body_size_limit)
)
def send_request_headers(self, request):
@@ -45,7 +46,7 @@ class Http1Layer(httpbase._HttpTransmissionLayer):
return http1.read_body(
self.server_conn.rfile,
expected_size,
- self.config.options._processed.get("body_size_limit")
+ human.parse_size(self.config.options.body_size_limit)
)
def send_response_headers(self, response):
diff --git a/mitmproxy/proxy/protocol/http2.py b/mitmproxy/proxy/protocol/http2.py
index eab5292f..cf021291 100644
--- a/mitmproxy/proxy/protocol/http2.py
+++ b/mitmproxy/proxy/protocol/http2.py
@@ -17,6 +17,7 @@ import mitmproxy.net.http
from mitmproxy.net import tcp
from mitmproxy.types import basethread
from mitmproxy.net.http import http2, headers
+from mitmproxy.utils import human
class SafeH2Connection(connection.H2Connection):
@@ -183,7 +184,7 @@ class Http2Layer(base.Layer):
return True
def _handle_data_received(self, eid, event, source_conn):
- bsl = self.config.options._processed.get("body_size_limit")
+ bsl = human.parse_size(self.config.options.body_size_limit)
if bsl and self.streams[eid].queued_data_length > bsl:
self.streams[eid].kill()
self.connections[source_conn].safe_reset_stream(
diff --git a/mitmproxy/proxy/protocol/http_replay.py b/mitmproxy/proxy/protocol/http_replay.py
index 1aa91847..fd673a6f 100644
--- a/mitmproxy/proxy/protocol/http_replay.py
+++ b/mitmproxy/proxy/protocol/http_replay.py
@@ -1,11 +1,18 @@
+import queue
+import threading
+import typing
+
from mitmproxy import log
from mitmproxy import controller
from mitmproxy import exceptions
from mitmproxy import http
from mitmproxy import flow
+from mitmproxy import options
from mitmproxy import connections
+from mitmproxy.net import server_spec
from mitmproxy.net.http import http1
from mitmproxy.types import basethread
+from mitmproxy.utils import human
# TODO: Doesn't really belong into mitmproxy.proxy.protocol...
@@ -14,12 +21,19 @@ from mitmproxy.types import basethread
class RequestReplayThread(basethread.BaseThread):
name = "RequestReplayThread"
- def __init__(self, config, f, event_queue, should_exit):
+ def __init__(
+ self,
+ opts: options.Options,
+ f: http.HTTPFlow,
+ event_queue: typing.Optional[queue.Queue],
+ should_exit: threading.Event
+ ) -> None:
"""
event_queue can be a queue or None, if no scripthooks should be
processed.
"""
- self.config, self.f = config, f
+ self.options = opts
+ self.f = f
f.live = True
if event_queue:
self.channel = controller.Channel(event_queue, should_exit)
@@ -31,7 +45,7 @@ class RequestReplayThread(basethread.BaseThread):
def run(self):
r = self.f.request
- bsl = self.config.options._processed.get("body_size_limit")
+ bsl = human.parse_size(self.options.body_size_limit)
first_line_format_backup = r.first_line_format
server = None
try:
@@ -45,9 +59,9 @@ class RequestReplayThread(basethread.BaseThread):
if not self.f.response:
# In all modes, we directly connect to the server displayed
- if self.config.options.mode.startswith("upstream:"):
- server_address = self.config.upstream_server.address
- server = connections.ServerConnection(server_address, (self.config.options.listen_host, 0))
+ if self.options.mode.startswith("upstream:"):
+ server_address = server_spec.parse_with_mode(self.options.mode)[1].address
+ server = connections.ServerConnection(server_address, (self.options.listen_host, 0))
server.connect()
if r.scheme == "https":
connect_request = http.make_connect_request((r.data.host, r.port))
@@ -61,7 +75,7 @@ class RequestReplayThread(basethread.BaseThread):
if resp.status_code != 200:
raise exceptions.ReplayException("Upstream server refuses CONNECT request")
server.establish_ssl(
- self.config.client_certs,
+ self.options.client_certs,
sni=self.f.server_conn.sni
)
r.first_line_format = "relative"
@@ -71,12 +85,12 @@ class RequestReplayThread(basethread.BaseThread):
server_address = (r.host, r.port)
server = connections.ServerConnection(
server_address,
- (self.config.options.listen_host, 0)
+ (self.options.listen_host, 0)
)
server.connect()
if r.scheme == "https":
server.establish_ssl(
- self.config.client_certs,
+ self.options.client_certs,
sni=self.f.server_conn.sni
)
r.first_line_format = "relative"
diff --git a/mitmproxy/proxy/root_context.py b/mitmproxy/proxy/root_context.py
index 3d21b13c..c0ec64c9 100644
--- a/mitmproxy/proxy/root_context.py
+++ b/mitmproxy/proxy/root_context.py
@@ -104,7 +104,16 @@ class RootContext:
if alpn == b'http/1.1':
return protocol.Http1Layer(top_layer, http.HTTPMode.transparent)
- # 6. Assume HTTP1 by default
+ # 6. Check for raw tcp mode
+ is_ascii = (
+ len(d) == 3 and
+ # expect A-Za-z
+ all(65 <= x <= 90 or 97 <= x <= 122 for x in d)
+ )
+ if self.config.options.rawtcp and not is_ascii:
+ return protocol.RawTCPLayer(top_layer)
+
+ # 7. Assume HTTP1 by default
return protocol.Http1Layer(top_layer, http.HTTPMode.transparent)
def log(self, msg, level, subs=()):
diff --git a/mitmproxy/test/taddons.py b/mitmproxy/test/taddons.py
index 49142871..6160746a 100644
--- a/mitmproxy/test/taddons.py
+++ b/mitmproxy/test/taddons.py
@@ -1,12 +1,11 @@
-import sys
import contextlib
+import sys
import mitmproxy.master
import mitmproxy.options
-from mitmproxy import proxy
from mitmproxy import addonmanager
-from mitmproxy import eventsequence
from mitmproxy import command
+from mitmproxy import eventsequence
from mitmproxy.addons import script
@@ -59,10 +58,11 @@ class context:
handlers can run as they would within mitmproxy. The context also
provides a number of helper methods for common testing scenarios.
"""
- def __init__(self, master = None, options = None):
+
+ def __init__(self, master=None, options=None):
options = options or mitmproxy.options.Options()
self.master = master or RecordingMaster(
- options, proxy.DummyServer(options)
+ options
)
self.options = self.master.options
self.wrapped = None
diff --git a/mitmproxy/tools/cmdline.py b/mitmproxy/tools/cmdline.py
index 68ddc2c8..93ce6f24 100644
--- a/mitmproxy/tools/cmdline.py
+++ b/mitmproxy/tools/cmdline.py
@@ -73,6 +73,7 @@ def common_options(parser, opts):
opts.make_parser(group, "upstream_auth", metavar="USER:PASS")
opts.make_parser(group, "proxyauth", metavar="SPEC")
opts.make_parser(group, "rawtcp")
+ opts.make_parser(group, "http2")
# Proxy SSL options
group = parser.add_argument_group("SSL")
diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py
index 0b0993c8..853447cf 100644
--- a/mitmproxy/tools/console/consoleaddons.py
+++ b/mitmproxy/tools/console/consoleaddons.py
@@ -68,12 +68,14 @@ class ConsoleAddon:
"""
return ["single", "vertical", "horizontal"]
- @command.command("intercept_toggle")
+ @command.command("console.intercept.toggle")
def intercept_toggle(self) -> None:
"""
Toggles interception on/off leaving intercept filters intact.
"""
- ctx.options.intercept_active = not ctx.options.intercept_active
+ ctx.options.update(
+ intercept_active = not ctx.options.intercept_active
+ )
@command.command("console.layout.cycle")
def layout_cycle(self) -> None:
diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py
index 4634b1e2..8139569e 100644
--- a/mitmproxy/tools/console/defaultkeys.py
+++ b/mitmproxy/tools/console/defaultkeys.py
@@ -24,7 +24,7 @@ def map(km):
km.add("ctrl f", "console.nav.pagedown", ["global"], "Page down")
km.add("ctrl b", "console.nav.pageup", ["global"], "Page up")
- km.add("I", "console.command intercept_toggle", ["global"], "Toggle intercept")
+ km.add("I", "console.intercept.toggle", ["global"], "Toggle intercept")
km.add("i", "console.command set intercept=", ["global"], "Set intercept")
km.add("W", "console.command set save_stream_file=", ["global"], "Stream to file")
km.add("A", "flow.resume @all", ["flowlist", "flowview"], "Resume all intercepted flows")
diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py
index 8c8cfe61..0d9dee9b 100644
--- a/mitmproxy/tools/console/master.py
+++ b/mitmproxy/tools/console/master.py
@@ -28,18 +28,11 @@ from mitmproxy.tools.console import window
class ConsoleMaster(master.Master):
- def __init__(self, options, server):
- super().__init__(options, server)
-
- if not sys.stdout.isatty():
- print("Error: mitmproxy's console interface requires a tty. "
- "Please run mitmproxy in an interactive shell environment.", file=sys.stderr)
- sys.exit(1)
+ def __init__(self, opts):
+ super().__init__(opts)
self.view = view.View() # type: view.View
self.stream_path = None
- # This line is just for type hinting
- self.options = self.options # type: Options
self.keymap = keymap.Keymap(self)
defaultkeys.map(self.keymap)
self.options.errored.connect(self.options_error)
@@ -66,7 +59,7 @@ class ConsoleMaster(master.Master):
self.window = None
def __setattr__(self, name, value):
- self.__dict__[name] = value
+ super().__setattr__(name, value)
signals.update_settings.send(self)
def options_error(self, opts, exc):
@@ -160,10 +153,10 @@ class ConsoleMaster(master.Master):
self.ui.start()
os.unlink(name)
- def set_palette(self, options, updated):
+ def set_palette(self, opts, updated):
self.ui.register_palette(
- palettes.palettes[options.console_palette].palette(
- options.console_palette_transparent
+ palettes.palettes[opts.console_palette].palette(
+ opts.console_palette_transparent
)
)
self.ui.clear()
@@ -178,6 +171,11 @@ class ConsoleMaster(master.Master):
self.loop.process_input([key])
def run(self):
+ if not sys.stdout.isatty():
+ print("Error: mitmproxy's console interface requires a tty. "
+ "Please run mitmproxy in an interactive shell environment.", file=sys.stderr)
+ sys.exit(1)
+
self.ui = urwid.raw_display.Screen()
self.ui.set_terminal_properties(256)
self.set_palette(self.options, None)
diff --git a/mitmproxy/tools/console/statusbar.py b/mitmproxy/tools/console/statusbar.py
index 7628c475..5bfc611c 100644
--- a/mitmproxy/tools/console/statusbar.py
+++ b/mitmproxy/tools/console/statusbar.py
@@ -257,11 +257,11 @@ class StatusBar(urwid.WidgetWrap):
('heading', ("%s %s [%s/%s]" % (arrow, marked, offset, fc)).ljust(11)),
]
- if self.master.server.bound:
- host = self.master.server.address[0]
- if host == "0.0.0.0":
+ if self.master.options.server:
+ host = self.master.options.listen_host
+ if host == "0.0.0.0" or host == "":
host = "*"
- boundaddr = "[%s:%s]" % (host, self.master.server.address[1])
+ boundaddr = "[%s:%s]" % (host, self.master.options.listen_port)
else:
boundaddr = ""
t.extend(self.get_status())
diff --git a/mitmproxy/tools/dump.py b/mitmproxy/tools/dump.py
index 4d0ccf4b..af04f8a3 100644
--- a/mitmproxy/tools/dump.py
+++ b/mitmproxy/tools/dump.py
@@ -18,11 +18,10 @@ class DumpMaster(master.Master):
def __init__(
self,
options: options.Options,
- server,
with_termlog=True,
with_dumper=True,
) -> None:
- master.Master.__init__(self, options, server)
+ super().__init__(options)
self.errorcheck = ErrorCheck()
if with_termlog:
self.addons.add(termlog.TermLog(), termstatus.TermStatus())
diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py
index 1575de98..3735cbf4 100644
--- a/mitmproxy/tools/main.py
+++ b/mitmproxy/tools/main.py
@@ -1,23 +1,26 @@
from __future__ import print_function # this is here for the version check to work on Python 2.
+
import sys
-# This must be at the very top, before importing anything else that might break!
-# Keep all other imports below with the 'noqa' magic comment.
if sys.version_info < (3, 5):
+ # This must be before any mitmproxy imports, as they already break!
+ # Keep all other imports below with the 'noqa' magic comment.
print("#" * 49, file=sys.stderr)
print("# mitmproxy only supports Python 3.5 and above! #", file=sys.stderr)
print("#" * 49, file=sys.stderr)
+import argparse # noqa
import os # noqa
import signal # noqa
+import typing # noqa
from mitmproxy.tools import cmdline # noqa
-from mitmproxy import exceptions # noqa
+from mitmproxy import exceptions, master # noqa
from mitmproxy import options # noqa
from mitmproxy import optmanager # noqa
from mitmproxy import proxy # noqa
-from mitmproxy import log # noqa
-from mitmproxy.utils import debug # noqa
+from mitmproxy import log # noqa
+from mitmproxy.utils import debug, arg_check # noqa
def assert_utf8_env():
@@ -53,7 +56,12 @@ def process_options(parser, opts, args):
return proxy.config.ProxyConfig(opts)
-def run(MasterKlass, args, extra=None): # pragma: no cover
+def run(
+ master_cls: typing.Type[master.Master],
+ make_parser: typing.Callable[[options.Options], argparse.ArgumentParser],
+ arguments: typing.Sequence[str],
+ extra: typing.Callable[[typing.Any], dict] = None
+): # pragma: no cover
"""
extra: Extra argument processing callable which returns a dict of
options.
@@ -61,12 +69,24 @@ def run(MasterKlass, args, extra=None): # pragma: no cover
debug.register_info_dumpers()
opts = options.Options()
- parser = cmdline.mitmdump(opts)
- args = parser.parse_args(args)
- master = None
+ master = master_cls(opts)
+
+ parser = make_parser(opts)
+
+ # To make migration from 2.x to 3.0 bearable.
+ if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"):
+ print("-R is used for specifying replacements.\n"
+ "To use mitmproxy in reverse mode please use --mode reverse:SPEC instead")
+
+ try:
+ args = parser.parse_args(arguments)
+ except SystemExit:
+ arg_check.check()
+ sys.exit(1)
try:
unknown = optmanager.load_paths(opts, args.conf)
pconf = process_options(parser, opts, args)
+ server = None # type: typing.Any
if pconf.options.server:
try:
server = proxy.server.ProxyServer(pconf)
@@ -76,7 +96,7 @@ def run(MasterKlass, args, extra=None): # pragma: no cover
else:
server = proxy.server.DummyServer(pconf)
- master = MasterKlass(opts, server)
+ master.server = server
master.addons.trigger("configure", opts.keys())
master.addons.trigger("tick")
remaining = opts.update_known(**unknown)
@@ -114,7 +134,7 @@ def mitmproxy(args=None): # pragma: no cover
assert_utf8_env()
from mitmproxy.tools import console
- run(console.master.ConsoleMaster, args)
+ run(console.master.ConsoleMaster, cmdline.mitmproxy, args)
def mitmdump(args=None): # pragma: no cover
@@ -124,16 +144,16 @@ def mitmdump(args=None): # pragma: no cover
if args.filter_args:
v = " ".join(args.filter_args)
return dict(
- view_filter = v,
- save_stream_filter = v,
+ view_filter=v,
+ save_stream_filter=v,
)
return {}
- m = run(dump.DumpMaster, args, extra)
+ m = run(dump.DumpMaster, cmdline.mitmdump, args, extra)
if m and m.errorcheck.has_errored:
sys.exit(1)
def mitmweb(args=None): # pragma: no cover
from mitmproxy.tools import web
- run(web.master.WebMaster, args)
+ run(web.master.WebMaster, cmdline.mitmweb, args)
diff --git a/mitmproxy/tools/web/master.py b/mitmproxy/tools/web/master.py
index dc5b2627..b13aeff9 100644
--- a/mitmproxy/tools/web/master.py
+++ b/mitmproxy/tools/web/master.py
@@ -13,12 +13,12 @@ from mitmproxy.addons import termlog
from mitmproxy.addons import view
from mitmproxy.addons import termstatus
from mitmproxy.options import Options # noqa
-from mitmproxy.tools.web import app
+from mitmproxy.tools.web import app, webaddons
class WebMaster(master.Master):
- def __init__(self, options, server, with_termlog=True):
- super().__init__(options, server)
+ def __init__(self, options, with_termlog=True):
+ super().__init__(options)
self.view = view.View()
self.view.sig_view_add.connect(self._sig_view_add)
self.view.sig_view_remove.connect(self._sig_view_remove)
@@ -34,6 +34,7 @@ class WebMaster(master.Master):
self.addons.add(*addons.default_addons())
self.addons.add(
+ webaddons.WebAddon(),
intercept.Intercept(),
readfile.ReadFile(),
self.view,
@@ -44,8 +45,6 @@ class WebMaster(master.Master):
self.app = app.Application(
self, self.options.web_debug
)
- # This line is just for type hinting
- self.options = self.options # type: Options
def _sig_view_add(self, view, flow):
app.ClientConnection.broadcast(
diff --git a/mitmproxy/tools/web/webaddons.py b/mitmproxy/tools/web/webaddons.py
new file mode 100644
index 00000000..6b52188c
--- /dev/null
+++ b/mitmproxy/tools/web/webaddons.py
@@ -0,0 +1,18 @@
+class WebAddon:
+ def load(self, loader):
+ loader.add_option(
+ "web_open_browser", bool, True,
+ "Start a browser."
+ )
+ loader.add_option(
+ "web_debug", bool, False,
+ "Enable mitmweb debugging."
+ )
+ loader.add_option(
+ "web_port", int, 8081,
+ "Web UI port."
+ )
+ loader.add_option(
+ "web_iface", str, "127.0.0.1",
+ "Web UI interface."
+ )
diff --git a/mitmproxy/utils/arg_check.py b/mitmproxy/utils/arg_check.py
new file mode 100644
index 00000000..73f7047c
--- /dev/null
+++ b/mitmproxy/utils/arg_check.py
@@ -0,0 +1,148 @@
+import sys
+
+DEPRECATED = """
+--cadir
+-Z
+--body-size-limit
+--stream
+--palette
+--palette-transparent
+--follow
+--order
+--no-mouse
+--reverse
+--socks
+--http2-priority
+--no-http2-priority
+--no-websocket
+--websocket
+--spoof-source-address
+--upstream-bind-address
+--ciphers-client
+--ciphers-server
+--client-certs
+--no-upstream-cert
+--add-upstream-certs-to-client-chain
+--upstream-trusted-cadir
+--upstream-trusted-ca
+--ssl-version-client
+--ssl-version-server
+--no-onboarding
+--onboarding-host
+--onboarding-port
+--server-replay-use-header
+--no-pop
+--replay-ignore-content
+--replay-ignore-payload-param
+--replay-ignore-param
+--replay-ignore-host
+--replace-from-file
+"""
+
+REPLACED = """
+-t
+-u
+--wfile
+-a
+--afile
+-z
+-b
+--bind-address
+--port
+-I
+--ignore
+--tcp
+--cert
+--insecure
+-c
+--replace
+-i
+-f
+--filter
+"""
+
+REPLACEMENTS = {
+ "--stream": "stream_large_bodies",
+ "--palette": "console_palette",
+ "--palette-transparent": "console_palette_transparent:",
+ "--follow": "console_focus_follow",
+ "--order": "console_order",
+ "--no-mouse": "console_mouse",
+ "--reverse": "console_order_reversed",
+ "--no-http2-priority": "http2_priority",
+ "--no-websocket": "websocket",
+ "--no-upstream-cert": "upstream_cert",
+ "--upstream-trusted-cadir": "ssl_verify_upstream_trusted_cadir",
+ "--upstream-trusted-ca": "ssl_verify_upstream_trusted_ca",
+ "--no-onboarding": "onboarding",
+ "--no-pop": "server_replay_nopop",
+ "--replay-ignore-content": "server_replay_ignore_content",
+ "--replay-ignore-payload-param": "server_replay_ignore_payload_params",
+ "--replay-ignore-param": "server_replay_ignore_params",
+ "--replay-ignore-host": "server_replay_ignore_host",
+ "--replace-from-file": "replacements (use @ to specify path)",
+ "-t": "--stickycookie",
+ "-u": "--stickyauth",
+ "--wfile": "--save-stream-file",
+ "-a": "-w Prefix path with + to append.",
+ "--afile": "-w Prefix path with + to append.",
+ "-z": "--anticomp",
+ "-b": "--listen-host",
+ "--bind-address": "--listen-host",
+ "--port": "--listen-port",
+ "-I": "--ignore-hosts",
+ "--ignore": "--ignore-hosts",
+ "--tcp": "--tcp-hosts",
+ "--cert": "--certs",
+ "--insecure": "--ssl-insecure",
+ "-c": "-C",
+ "--replace": "--replacements",
+ "-i": "--intercept",
+ "-f": "--view-filter",
+ "--filter": "--view-filter"
+}
+
+
+def check():
+ args = sys.argv[1:]
+ print()
+ if "-U" in args:
+ print("-U is deprecated, please use --mode upstream:SPEC instead")
+
+ if "-T" in args:
+ print("-T is deprecated, please use --mode transparent instead")
+
+ for option in ("-e", "--eventlog", "--norefresh"):
+ if option in args:
+ print("{} has been removed.".format(option))
+
+ for option in ("--nonanonymous", "--singleuser", "--htpasswd"):
+ if option in args:
+ print(
+ '{} is deprecated.\n'
+ 'Please use `--proxyauth SPEC` instead.\n'
+ 'SPEC Format: "username:pass", "any" to accept any user/pass combination,\n'
+ '"@path" to use an Apache htpasswd file, or\n'
+ '"ldap[s]:url_server_ldap:dn_auth:password:dn_subtree" '
+ 'for LDAP authentication.'.format(option))
+
+ for option in REPLACED.splitlines():
+ if option in args:
+ print(
+ "{} is deprecated.\n"
+ "Please use `{}` instead.".format(
+ option,
+ REPLACEMENTS.get(option)
+ )
+ )
+
+ for option in DEPRECATED.splitlines():
+ if option in args:
+ print(
+ "{} is deprecated.\n"
+ "Please use `--set {}=value` instead.\n"
+ "To show all options and their default values use --options".format(
+ option,
+ REPLACEMENTS.get(option, None) or option.lstrip("-").replace("-", "_")
+ )
+ )
diff --git a/mitmproxy/utils/human.py b/mitmproxy/utils/human.py
index d67fb310..e2e3142a 100644
--- a/mitmproxy/utils/human.py
+++ b/mitmproxy/utils/human.py
@@ -1,6 +1,8 @@
import datetime
import ipaddress
import time
+import functools
+import typing
SIZE_TABLE = [
("b", 1024 ** 0),
@@ -25,7 +27,14 @@ def pretty_size(size):
return "%s%s" % (size, SIZE_TABLE[0][0])
-def parse_size(s):
+@functools.lru_cache()
+def parse_size(s: typing.Optional[str]) -> typing.Optional[int]:
+ """
+ Parse a size with an optional k/m/... suffix.
+ Invalid values raise a ValueError. For added convenience, passing `None` returns `None`.
+ """
+ if s is None:
+ return None
try:
return int(s)
except ValueError:
diff --git a/setup.py b/setup.py
index 433635e4..739d1538 100644
--- a/setup.py
+++ b/setup.py
@@ -71,9 +71,9 @@ setup(
"hyperframe>=5.0, <6",
"jsbeautifier>=1.6.3, <1.7",
"kaitaistruct>=0.7, <0.8",
- "ldap3>=2.2.0, <2.3",
+ "ldap3>=2.2.0, <2.4",
"passlib>=1.6.5, <1.8",
- "pyasn1>=0.1.9, <0.3",
+ "pyasn1>=0.3.1, <0.4",
"pyOpenSSL>=17.2,<17.3",
"pyparsing>=2.1.3, <2.3",
"pyperclip>=1.5.22, <1.6",
@@ -88,7 +88,7 @@ setup(
"pydivert>=2.0.3, <2.1",
],
'dev': [
- "flake8>=3.2.1, <3.4",
+ "flake8>=3.2.1, <3.5",
"Flask>=0.10.1, <0.13",
"mypy>=0.521,<0.522",
"pytest-cov>=2.2.1, <3",
diff --git a/test/filename_matching.py b/test/filename_matching.py
index 51cedf03..e74848d4 100644
--- a/test/filename_matching.py
+++ b/test/filename_matching.py
@@ -22,7 +22,7 @@ def check_src_files_have_test():
def check_test_files_have_src():
unknown_test_files = []
- excluded = ['test/mitmproxy/data/', 'test/mitmproxy/net/data/', '/tservers.py']
+ excluded = ['test/mitmproxy/data/', 'test/mitmproxy/net/data/', '/tservers.py', '/conftest.py']
test_files = glob.glob('test/mitmproxy/**/*.py', recursive=True) + glob.glob('test/pathod/**/*.py', recursive=True)
test_files = [f for f in test_files if os.path.basename(f) != '__init__.py']
test_files = [f for f in test_files if not any(os.path.normpath(p) in f for p in excluded)]
diff --git a/test/mitmproxy/addons/test_core_option_validation.py b/test/mitmproxy/addons/test_core_option_validation.py
index 6d6d5ba4..cd5d4dfa 100644
--- a/test/mitmproxy/addons/test_core_option_validation.py
+++ b/test/mitmproxy/addons/test_core_option_validation.py
@@ -11,7 +11,6 @@ def test_simple():
with pytest.raises(exceptions.OptionsError):
tctx.configure(sa, body_size_limit = "invalid")
tctx.configure(sa, body_size_limit = "1m")
- assert tctx.master.options._processed["body_size_limit"]
with pytest.raises(exceptions.OptionsError, match="mutually exclusive"):
tctx.configure(
diff --git a/test/mitmproxy/addons/test_proxyauth.py b/test/mitmproxy/addons/test_proxyauth.py
index 40044bf0..1d05e137 100644
--- a/test/mitmproxy/addons/test_proxyauth.py
+++ b/test/mitmproxy/addons/test_proxyauth.py
@@ -10,197 +10,242 @@ from mitmproxy.test import tflow
from mitmproxy.test import tutils
-def test_parse_http_basic_auth():
- assert proxyauth.parse_http_basic_auth(
- proxyauth.mkauth("test", "test")
- ) == ("basic", "test", "test")
- with pytest.raises(ValueError):
- proxyauth.parse_http_basic_auth("")
- with pytest.raises(ValueError):
- proxyauth.parse_http_basic_auth("foo bar")
- with pytest.raises(ValueError):
- proxyauth.parse_http_basic_auth("basic abc")
- with pytest.raises(ValueError):
- v = "basic " + binascii.b2a_base64(b"foo").decode("ascii")
- proxyauth.parse_http_basic_auth(v)
-
-
-def test_configure():
- up = proxyauth.ProxyAuth()
- with taddons.context() as ctx:
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="foo")
-
- ctx.configure(up, proxyauth="foo:bar")
- assert up.singleuser == ["foo", "bar"]
-
- ctx.configure(up, proxyauth=None)
- assert up.singleuser is None
-
- ctx.configure(up, proxyauth="any")
- assert up.nonanonymous
- ctx.configure(up, proxyauth=None)
- assert not up.nonanonymous
-
- with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
- with mock.patch('ldap3.Connection', return_value="test"):
- ctx.configure(up, proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
- assert up.ldapserver
- ctx.configure(up, proxyauth="ldaps:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
- assert up.ldapserver
-
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="ldap:test:test:test")
-
- with pytest.raises(IndexError):
- ctx.configure(up, proxyauth="ldap:fake_serveruid=?dc=example,dc=com:person")
-
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="ldapssssssss:fake_server:dn:password:tree")
-
- with pytest.raises(exceptions.OptionsError):
+class TestMkauth:
+ def test_mkauth_scheme(self):
+ assert proxyauth.mkauth('username', 'password') == 'basic dXNlcm5hbWU6cGFzc3dvcmQ=\n'
+
+ @pytest.mark.parametrize('scheme, expected', [
+ ('', ' dXNlcm5hbWU6cGFzc3dvcmQ=\n'),
+ ('basic', 'basic dXNlcm5hbWU6cGFzc3dvcmQ=\n'),
+ ('foobar', 'foobar dXNlcm5hbWU6cGFzc3dvcmQ=\n'),
+ ])
+ def test_mkauth(self, scheme, expected):
+ assert proxyauth.mkauth('username', 'password', scheme) == expected
+
+
+class TestParseHttpBasicAuth:
+ @pytest.mark.parametrize('input', [
+ '',
+ 'foo bar',
+ 'basic abc',
+ 'basic ' + binascii.b2a_base64(b"foo").decode("ascii"),
+ ])
+ def test_parse_http_basic_auth_error(self, input):
+ with pytest.raises(ValueError):
+ proxyauth.parse_http_basic_auth(input)
+
+ def test_parse_http_basic_auth(self):
+ input = proxyauth.mkauth("test", "test")
+ assert proxyauth.parse_http_basic_auth(input) == ("basic", "test", "test")
+
+
+class TestProxyAuth:
+ @pytest.mark.parametrize('mode, expected', [
+ ('', False),
+ ('foobar', False),
+ ('regular', True),
+ ('upstream:', True),
+ ('upstream:foobar', True),
+ ])
+ def test_is_proxy_auth(self, mode, expected):
+ up = proxyauth.ProxyAuth()
+ with taddons.context() as ctx:
+ ctx.options.mode = mode
+ assert up.is_proxy_auth() is expected
+
+ @pytest.mark.parametrize('is_proxy_auth, expected', [
+ (True, 'Proxy-Authorization'),
+ (False, 'Authorization'),
+ ])
+ def test_which_auth_header(self, is_proxy_auth, expected):
+ up = proxyauth.ProxyAuth()
+ with mock.patch('mitmproxy.addons.proxyauth.ProxyAuth.is_proxy_auth', return_value=is_proxy_auth):
+ assert up.which_auth_header() == expected
+
+ @pytest.mark.parametrize('is_proxy_auth, expected_status_code, expected_header', [
+ (True, 407, 'Proxy-Authenticate'),
+ (False, 401, 'WWW-Authenticate'),
+ ])
+ def test_auth_required_response(self, is_proxy_auth, expected_status_code, expected_header):
+ up = proxyauth.ProxyAuth()
+ with mock.patch('mitmproxy.addons.proxyauth.ProxyAuth.is_proxy_auth', return_value=is_proxy_auth):
+ resp = up.auth_required_response()
+ assert resp.status_code == expected_status_code
+ assert expected_header in resp.headers.keys()
+
+ def test_check(self):
+ up = proxyauth.ProxyAuth()
+ with taddons.context() as ctx:
+ ctx.configure(up, proxyauth="any", mode="regular")
+ f = tflow.tflow()
+ assert not up.check(f)
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ assert up.check(f)
+
+ f.request.headers["Proxy-Authorization"] = "invalid"
+ assert not up.check(f)
+
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test", scheme="unknown"
+ )
+ assert not up.check(f)
+
+ ctx.configure(up, proxyauth="test:test")
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ assert up.check(f)
+ ctx.configure(up, proxyauth="test:foo")
+ assert not up.check(f)
+
ctx.configure(
up,
- proxyauth= "@" + tutils.test_data.path("mitmproxy/net/data/server.crt")
+ proxyauth="@" + tutils.test_data.path(
+ "mitmproxy/net/data/htpasswd"
+ )
)
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="@nonexistent")
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ assert up.check(f)
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "foo"
+ )
+ assert not up.check(f)
+
+ with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
+ with mock.patch('ldap3.Connection', search="test"):
+ with mock.patch('ldap3.Connection.search', return_value="test"):
+ ctx.configure(
+ up,
+ proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com"
+ )
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ assert up.check(f)
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "", ""
+ )
+ assert not up.check(f)
+
+ def test_authenticate(self):
+ up = proxyauth.ProxyAuth()
+ with taddons.context() as ctx:
+ ctx.configure(up, proxyauth="any", mode="regular")
+
+ f = tflow.tflow()
+ assert not f.response
+ up.authenticate(f)
+ assert f.response.status_code == 407
+
+ f = tflow.tflow()
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ up.authenticate(f)
+ assert not f.response
+ assert not f.request.headers.get("Proxy-Authorization")
+
+ f = tflow.tflow()
+ ctx.configure(up, mode="reverse")
+ assert not f.response
+ up.authenticate(f)
+ assert f.response.status_code == 401
+
+ f = tflow.tflow()
+ f.request.headers["Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ up.authenticate(f)
+ assert not f.response
+ assert not f.request.headers.get("Authorization")
+
+ def test_configure(self):
+ up = proxyauth.ProxyAuth()
+ with taddons.context() as ctx:
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="foo")
+
+ ctx.configure(up, proxyauth="foo:bar")
+ assert up.singleuser == ["foo", "bar"]
+
+ ctx.configure(up, proxyauth=None)
+ assert up.singleuser is None
+
+ ctx.configure(up, proxyauth="any")
+ assert up.nonanonymous
+ ctx.configure(up, proxyauth=None)
+ assert not up.nonanonymous
+
+ with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
+ with mock.patch('ldap3.Connection', return_value="test"):
+ ctx.configure(up, proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
+ assert up.ldapserver
+ ctx.configure(up, proxyauth="ldaps:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
+ assert up.ldapserver
+
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="ldap:test:test:test")
+
+ with pytest.raises(IndexError):
+ ctx.configure(up, proxyauth="ldap:fake_serveruid=?dc=example,dc=com:person")
+
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="ldapssssssss:fake_server:dn:password:tree")
+
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(
+ up,
+ proxyauth= "@" + tutils.test_data.path("mitmproxy/net/data/server.crt")
+ )
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="@nonexistent")
- ctx.configure(
- up,
- proxyauth= "@" + tutils.test_data.path(
- "mitmproxy/net/data/htpasswd"
+ ctx.configure(
+ up,
+ proxyauth= "@" + tutils.test_data.path(
+ "mitmproxy/net/data/htpasswd"
+ )
)
- )
- assert up.htpasswd
- assert up.htpasswd.check_password("test", "test")
- assert not up.htpasswd.check_password("test", "foo")
- ctx.configure(up, proxyauth=None)
- assert not up.htpasswd
-
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="any", mode="transparent")
- with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="any", mode="socks5")
-
-
-def test_check(monkeypatch):
- up = proxyauth.ProxyAuth()
- with taddons.context() as ctx:
- ctx.configure(up, proxyauth="any", mode="regular")
- f = tflow.tflow()
- assert not up.check(f)
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- assert up.check(f)
-
- f.request.headers["Proxy-Authorization"] = "invalid"
- assert not up.check(f)
-
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test", scheme="unknown"
- )
- assert not up.check(f)
-
- ctx.configure(up, proxyauth="test:test")
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- assert up.check(f)
- ctx.configure(up, proxyauth="test:foo")
- assert not up.check(f)
-
- ctx.configure(
- up,
- proxyauth="@" + tutils.test_data.path(
- "mitmproxy/net/data/htpasswd"
+ assert up.htpasswd
+ assert up.htpasswd.check_password("test", "test")
+ assert not up.htpasswd.check_password("test", "foo")
+ ctx.configure(up, proxyauth=None)
+ assert not up.htpasswd
+
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="any", mode="transparent")
+ with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="any", mode="socks5")
+
+ def test_handlers(self):
+ up = proxyauth.ProxyAuth()
+ with taddons.context() as ctx:
+ ctx.configure(up, proxyauth="any", mode="regular")
+
+ f = tflow.tflow()
+ assert not f.response
+ up.requestheaders(f)
+ assert f.response.status_code == 407
+
+ f = tflow.tflow()
+ f.request.method = "CONNECT"
+ assert not f.response
+ up.http_connect(f)
+ assert f.response.status_code == 407
+
+ f = tflow.tflow()
+ f.request.method = "CONNECT"
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
)
- )
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- assert up.check(f)
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "foo"
- )
- assert not up.check(f)
-
- with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
- with mock.patch('ldap3.Connection', search="test"):
- with mock.patch('ldap3.Connection.search', return_value="test"):
- ctx.configure(
- up,
- proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com"
- )
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- assert up.check(f)
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "", ""
- )
- assert not up.check(f)
-
-
-def test_authenticate():
- up = proxyauth.ProxyAuth()
- with taddons.context() as ctx:
- ctx.configure(up, proxyauth="any", mode="regular")
-
- f = tflow.tflow()
- assert not f.response
- up.authenticate(f)
- assert f.response.status_code == 407
-
- f = tflow.tflow()
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- up.authenticate(f)
- assert not f.response
- assert not f.request.headers.get("Proxy-Authorization")
-
- f = tflow.tflow()
- ctx.configure(up, mode="reverse")
- assert not f.response
- up.authenticate(f)
- assert f.response.status_code == 401
-
- f = tflow.tflow()
- f.request.headers["Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- up.authenticate(f)
- assert not f.response
- assert not f.request.headers.get("Authorization")
-
-
-def test_handlers():
- up = proxyauth.ProxyAuth()
- with taddons.context() as ctx:
- ctx.configure(up, proxyauth="any", mode="regular")
-
- f = tflow.tflow()
- assert not f.response
- up.requestheaders(f)
- assert f.response.status_code == 407
-
- f = tflow.tflow()
- f.request.method = "CONNECT"
- assert not f.response
- up.http_connect(f)
- assert f.response.status_code == 407
-
- f = tflow.tflow()
- f.request.method = "CONNECT"
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "test", "test"
- )
- up.http_connect(f)
- assert not f.response
-
- f2 = tflow.tflow(client_conn=f.client_conn)
- up.requestheaders(f2)
- assert not f2.response
- assert f2.metadata["proxyauth"] == ('test', 'test')
+ up.http_connect(f)
+ assert not f.response
+
+ f2 = tflow.tflow(client_conn=f.client_conn)
+ up.requestheaders(f2)
+ assert not f2.response
+ assert f2.metadata["proxyauth"] == ('test', 'test')
diff --git a/test/mitmproxy/addons/test_script.py b/test/mitmproxy/addons/test_script.py
index 64fd9505..aa7ca68e 100644
--- a/test/mitmproxy/addons/test_script.py
+++ b/test/mitmproxy/addons/test_script.py
@@ -1,15 +1,16 @@
-import traceback
-import sys
import os
+import sys
+import traceback
+from unittest import mock
+
import pytest
-from unittest import mock
-from mitmproxy.test import tflow
-from mitmproxy.test import tutils
-from mitmproxy.test import taddons
from mitmproxy import addonmanager
from mitmproxy import exceptions
from mitmproxy.addons import script
+from mitmproxy.test import taddons
+from mitmproxy.test import tflow
+from mitmproxy.test import tutils
def test_load_script():
@@ -216,6 +217,20 @@ class TestScriptLoader:
assert not tctx.options.scripts
assert not sl.addons
+ def test_load_err(self):
+ sc = script.ScriptLoader()
+ with taddons.context() as tctx:
+ tctx.configure(sc, scripts=[
+ tutils.test_data.path("mitmproxy/data/addonscripts/load_error.py")
+ ])
+ try:
+ tctx.invoke(sc, "tick")
+ except ValueError:
+ pass # this is expected and normally guarded.
+ # on the next tick we should not fail however.
+ tctx.invoke(sc, "tick")
+ assert len(tctx.master.addons) == 0
+
def test_order(self):
rec = tutils.test_data.path("mitmproxy/data/addonscripts/recorder")
sc = script.ScriptLoader()
diff --git a/test/mitmproxy/addons/test_termstatus.py b/test/mitmproxy/addons/test_termstatus.py
index 2debaff5..5f960a1c 100644
--- a/test/mitmproxy/addons/test_termstatus.py
+++ b/test/mitmproxy/addons/test_termstatus.py
@@ -1,3 +1,4 @@
+from mitmproxy import proxy
from mitmproxy.addons import termstatus
from mitmproxy.test import taddons
@@ -5,6 +6,7 @@ from mitmproxy.test import taddons
def test_configure():
ts = termstatus.TermStatus()
with taddons.context() as ctx:
+ ctx.master.server = proxy.DummyServer()
ctx.configure(ts, server=False)
ts.running()
assert not ctx.master.logs
diff --git a/test/mitmproxy/data/addonscripts/load_error.py b/test/mitmproxy/data/addonscripts/load_error.py
new file mode 100644
index 00000000..4c05e9ed
--- /dev/null
+++ b/test/mitmproxy/data/addonscripts/load_error.py
@@ -0,0 +1,2 @@
+def load(_):
+ raise ValueError()
diff --git a/test/mitmproxy/proxy/protocol/test_http2.py b/test/mitmproxy/proxy/protocol/test_http2.py
index 583e6e27..4f161ef5 100644
--- a/test/mitmproxy/proxy/protocol/test_http2.py
+++ b/test/mitmproxy/proxy/protocol/test_http2.py
@@ -8,7 +8,6 @@ import pytest
import h2
from mitmproxy import options
-from mitmproxy.proxy.config import ProxyConfig
import mitmproxy.net
from ...net import tservers as net_tservers
@@ -89,10 +88,8 @@ class _Http2TestBase:
@classmethod
def setup_class(cls):
- opts = cls.get_options()
- cls.config = ProxyConfig(opts)
-
- tmaster = tservers.TestMaster(opts, cls.config)
+ cls.options = cls.get_options()
+ tmaster = tservers.TestMaster(cls.options)
cls.proxy = tservers.ProxyThread(tmaster)
cls.proxy.start()
@@ -319,7 +316,7 @@ class TestRequestWithPriority(_Http2Test):
(False, (None, None, None), (None, None, None)),
])
def test_request_with_priority(self, http2_priority_enabled, priority, expected_priority):
- self.config.options.http2_priority = http2_priority_enabled
+ self.options.http2_priority = http2_priority_enabled
h2_conn = self.setup_connection()
@@ -397,7 +394,7 @@ class TestPriority(_Http2Test):
(False, (True, 42424242, 42), []),
])
def test_priority(self, prioritize_before, http2_priority_enabled, priority, expected_priority):
- self.config.options.http2_priority = http2_priority_enabled
+ self.options.http2_priority = http2_priority_enabled
self.__class__.priority_data = []
h2_conn = self.setup_connection()
@@ -508,8 +505,7 @@ class TestBodySizeLimit(_Http2Test):
return True
def test_body_size_limit(self):
- self.config.options.body_size_limit = "20"
- self.config.options._processed["body_size_limit"] = 20
+ self.options.body_size_limit = "20"
h2_conn = self.setup_connection()
diff --git a/test/mitmproxy/proxy/protocol/test_websocket.py b/test/mitmproxy/proxy/protocol/test_websocket.py
index 58857f92..460d85f8 100644
--- a/test/mitmproxy/proxy/protocol/test_websocket.py
+++ b/test/mitmproxy/proxy/protocol/test_websocket.py
@@ -7,7 +7,6 @@ from mitmproxy import options
from mitmproxy import exceptions
from mitmproxy.http import HTTPFlow
from mitmproxy.websocket import WebSocketFlow
-from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.net import tcp
from mitmproxy.net import http
@@ -49,10 +48,8 @@ class _WebSocketTestBase:
@classmethod
def setup_class(cls):
- opts = cls.get_options()
- cls.config = ProxyConfig(opts)
-
- tmaster = tservers.TestMaster(opts, cls.config)
+ cls.options = cls.get_options()
+ tmaster = tservers.TestMaster(cls.options)
cls.proxy = tservers.ProxyThread(tmaster)
cls.proxy.start()
diff --git a/test/mitmproxy/proxy/test_server.py b/test/mitmproxy/proxy/test_server.py
index 4cae756a..562f822c 100644
--- a/test/mitmproxy/proxy/test_server.py
+++ b/test/mitmproxy/proxy/test_server.py
@@ -117,13 +117,12 @@ class TcpMixin:
def _ignore_on(self):
assert not hasattr(self, "_ignore_backup")
- self._ignore_backup = self.config.check_ignore
- self.config.check_ignore = HostMatcher(
- [".+:%s" % self.server.port] + self.config.check_ignore.patterns)
+ self._ignore_backup = self.options.ignore_hosts
+ self.options.ignore_hosts = [".+:%s" % self.server.port] + self.options.ignore_hosts
def _ignore_off(self):
assert hasattr(self, "_ignore_backup")
- self.config.check_ignore = self._ignore_backup
+ self.options.ignore_hosts = self._ignore_backup
del self._ignore_backup
def test_ignore(self):
@@ -163,13 +162,12 @@ class TcpMixin:
def _tcpproxy_on(self):
assert not hasattr(self, "_tcpproxy_backup")
- self._tcpproxy_backup = self.config.check_tcp
- self.config.check_tcp = HostMatcher(
- [".+:%s" % self.server.port] + self.config.check_tcp.patterns)
+ self._tcpproxy_backup = self.options.tcp_hosts
+ self.options.tcp_hosts = [".+:%s" % self.server.port] + self.options.tcp_hosts
def _tcpproxy_off(self):
assert hasattr(self, "_tcpproxy_backup")
- self.config.check_tcp = self._tcpproxy_backup
+ self.options.tcp_hosts = self._tcpproxy_backup
del self._tcpproxy_backup
def test_tcp(self):
@@ -194,7 +192,8 @@ class TcpMixin:
i2_cert = certs.SSLCert(i2.sslinfo.certchain[0])
n_cert = certs.SSLCert(n.sslinfo.certchain[0])
- assert i_cert == i2_cert == n_cert
+ assert i_cert == i2_cert
+ assert i_cert != n_cert
# Make sure that TCP messages are in the event log.
# Re-enable and fix this when we start keeping TCPFlows in the state.
@@ -353,22 +352,22 @@ class TestHTTPS(tservers.HTTPProxyTest, CommonMixin, TcpMixin):
def test_clientcert_file(self):
try:
- self.config.client_certs = os.path.join(
+ self.options.client_certs = os.path.join(
tutils.test_data.path("mitmproxy/data/clientcert"), "client.pem")
f = self.pathod("304")
assert f.status_code == 304
assert self.server.last_log()["request"]["clientcert"]["keyinfo"]
finally:
- self.config.client_certs = None
+ self.options.client_certs = None
def test_clientcert_dir(self):
try:
- self.config.client_certs = tutils.test_data.path("mitmproxy/data/clientcert")
+ self.options.client_certs = tutils.test_data.path("mitmproxy/data/clientcert")
f = self.pathod("304")
assert f.status_code == 304
assert self.server.last_log()["request"]["clientcert"]["keyinfo"]
finally:
- self.config.client_certs = None
+ self.options.client_certs = None
def test_error_post_connect(self):
p = self.pathoc()
@@ -412,7 +411,7 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxyTest):
return p.request("get:/p/242")
def test_verification_w_cadir(self):
- self.config.options.update(
+ self.options.update(
ssl_insecure=False,
ssl_verify_upstream_trusted_cadir=tutils.test_data.path(
"mitmproxy/data/servercert/"
@@ -422,7 +421,7 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxyTest):
assert self._request().status_code == 242
def test_verification_w_pemfile(self):
- self.config.options.update(
+ self.options.update(
ssl_insecure=False,
ssl_verify_upstream_trusted_cadir=None,
ssl_verify_upstream_trusted_ca=tutils.test_data.path(
@@ -458,7 +457,7 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxyTest):
return opts
def test_no_verification_w_bad_cert(self):
- self.config.options.ssl_insecure = True
+ self.options.ssl_insecure = True
r = self._request()
assert r.status_code == 242
@@ -466,7 +465,7 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxyTest):
# We only test for a single invalid cert here.
# Actual testing of different root-causes (invalid hostname, expired, ...)
# is done in mitmproxy.net.
- self.config.options.ssl_insecure = False
+ self.options.ssl_insecure = False
r = self._request()
assert r.status_code == 502
assert b"Certificate Verification Error" in r.raw_content
@@ -493,7 +492,7 @@ class TestReverse(tservers.ReverseProxyTest, CommonMixin, TcpMixin):
reverse = True
def test_host_header(self):
- self.config.options.keep_host_header = True
+ self.options.keep_host_header = True
p = self.pathoc()
with p.connect():
resp = p.request("get:/p/200:h'Host'='example.com'")
@@ -503,7 +502,7 @@ class TestReverse(tservers.ReverseProxyTest, CommonMixin, TcpMixin):
assert req.host_header == "example.com"
def test_overridden_host_header(self):
- self.config.options.keep_host_header = False # default value
+ self.options.keep_host_header = False # default value
p = self.pathoc()
with p.connect():
resp = p.request("get:/p/200:h'Host'='example.com'")
diff --git a/test/mitmproxy/test_addonmanager.py b/test/mitmproxy/test_addonmanager.py
index fc048571..accf48e0 100644
--- a/test/mitmproxy/test_addonmanager.py
+++ b/test/mitmproxy/test_addonmanager.py
@@ -6,7 +6,6 @@ from mitmproxy import exceptions
from mitmproxy import options
from mitmproxy import command
from mitmproxy import master
-from mitmproxy import proxy
from mitmproxy.test import taddons
from mitmproxy.test import tflow
@@ -51,7 +50,7 @@ def test_command():
def test_halt():
o = options.Options()
- m = master.Master(o, proxy.DummyServer(o))
+ m = master.Master(o)
a = addonmanager.AddonManager(m)
halt = THalt()
end = TAddon("end")
@@ -68,7 +67,7 @@ def test_halt():
def test_lifecycle():
o = options.Options()
- m = master.Master(o, proxy.DummyServer(o))
+ m = master.Master(o)
a = addonmanager.AddonManager(m)
a.add(TAddon("one"))
@@ -141,7 +140,7 @@ def test_simple():
def test_load_option():
o = options.Options()
- m = master.Master(o, proxy.DummyServer(o))
+ m = master.Master(o)
a = addonmanager.AddonManager(m)
a.add(AOption())
assert "custom_option" in m.options._options
@@ -149,7 +148,7 @@ def test_load_option():
def test_nesting():
o = options.Options()
- m = master.Master(o, proxy.DummyServer(o))
+ m = master.Master(o)
a = addonmanager.AddonManager(m)
a.add(
diff --git a/test/mitmproxy/test_controller.py b/test/mitmproxy/test_controller.py
index 2e13d298..e840380a 100644
--- a/test/mitmproxy/test_controller.py
+++ b/test/mitmproxy/test_controller.py
@@ -30,7 +30,8 @@ class TestMaster:
assert ctx.master.should_exit.is_set()
def test_server_simple(self):
- m = master.Master(None, proxy.DummyServer(None))
+ m = master.Master(None)
+ m.server = proxy.DummyServer()
m.start()
m.shutdown()
m.start()
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index 0b04c4c1..7f9d577b 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -6,13 +6,11 @@ from mitmproxy.test import tflow, tutils
import mitmproxy.io
from mitmproxy import flowfilter
from mitmproxy import options
-from mitmproxy.proxy import config
from mitmproxy.io import tnetstring
from mitmproxy.exceptions import FlowReadException, ReplayException, ControlException
from mitmproxy import flow
from mitmproxy import http
from mitmproxy.net import http as net_http
-from mitmproxy.proxy.server import DummyServer
from mitmproxy import master
from . import tservers
@@ -93,8 +91,7 @@ class TestFlowMaster:
opts = options.Options(
mode="reverse:https://use-this-domain"
)
- conf = config.ProxyConfig(opts)
- fm = master.Master(opts, DummyServer(conf))
+ fm = master.Master(opts)
fm.addons.add(s)
f = tflow.tflow(resp=True)
fm.load_flow(f)
@@ -102,8 +99,7 @@ class TestFlowMaster:
def test_replay(self):
opts = options.Options()
- conf = config.ProxyConfig(opts)
- fm = master.Master(opts, DummyServer(conf))
+ fm = master.Master(opts)
f = tflow.tflow(resp=True)
f.request.content = None
with pytest.raises(ReplayException, match="missing"):
@@ -131,7 +127,7 @@ class TestFlowMaster:
def test_all(self):
s = tservers.TestState()
- fm = master.Master(None, DummyServer())
+ fm = master.Master(None)
fm.addons.add(s)
f = tflow.tflow(req=None)
fm.addons.handle_lifecycle("clientconnect", f.client_conn)
diff --git a/test/mitmproxy/test_log.py b/test/mitmproxy/test_log.py
index cde679ed..349e3ac8 100644
--- a/test/mitmproxy/test_log.py
+++ b/test/mitmproxy/test_log.py
@@ -4,3 +4,8 @@ from mitmproxy import log
def test_logentry():
e = log.LogEntry("foo", "info")
assert repr(e) == "LogEntry(foo, info)"
+
+ f = log.LogEntry("foo", "warning")
+ assert e == e
+ assert e != f
+ assert e != 42
diff --git a/test/mitmproxy/test_optmanager.py b/test/mitmproxy/test_optmanager.py
index fe72e6bb..d9b93227 100644
--- a/test/mitmproxy/test_optmanager.py
+++ b/test/mitmproxy/test_optmanager.py
@@ -73,6 +73,11 @@ def test_required_int():
o.parse_setval("required_int", None)
+def test_deepcopy():
+ o = TD()
+ copy.deepcopy(o)
+
+
def test_options():
o = TO()
assert o.keys() == {"bool", "one", "two", "required_int"}
@@ -244,6 +249,7 @@ def test_serialize():
o2 = TD2()
optmanager.load(o2, data)
assert o2 == o
+ assert not o == 42
t = """
unknown: foo
diff --git a/test/mitmproxy/tools/console/conftest.py b/test/mitmproxy/tools/console/conftest.py
deleted file mode 100644
index afd94c6a..00000000
--- a/test/mitmproxy/tools/console/conftest.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from unittest import mock
-
-import pytest
-
-
-@pytest.fixture(scope="module", autouse=True)
-def definitely_atty():
- with mock.patch("sys.stdout.isatty", lambda: True):
- yield
diff --git a/test/mitmproxy/tools/console/test_master.py b/test/mitmproxy/tools/console/test_master.py
index ef357c76..3aa0dc54 100644
--- a/test/mitmproxy/tools/console/test_master.py
+++ b/test/mitmproxy/tools/console/test_master.py
@@ -1,7 +1,6 @@
import urwid
from mitmproxy import options
-from mitmproxy import proxy
from mitmproxy.test import tflow
from mitmproxy.test import tutils
from mitmproxy.tools import console
@@ -30,7 +29,7 @@ class TestMaster(tservers.MasterTest):
if "verbosity" not in opts:
opts["verbosity"] = 'warn'
o = options.Options(**opts)
- m = console.master.ConsoleMaster(o, proxy.DummyServer())
+ m = console.master.ConsoleMaster(o)
m.addons.trigger("configure", o.keys())
return m
diff --git a/test/mitmproxy/tools/console/test_statusbar.py b/test/mitmproxy/tools/console/test_statusbar.py
index 55a3c4a0..2f7825c9 100644
--- a/test/mitmproxy/tools/console/test_statusbar.py
+++ b/test/mitmproxy/tools/console/test_statusbar.py
@@ -1,4 +1,4 @@
-from mitmproxy import options, proxy
+from mitmproxy import options
from mitmproxy.tools.console import statusbar, master
@@ -26,7 +26,7 @@ def test_statusbar(monkeypatch):
scripts=["nonexistent"],
save_stream_file="foo",
)
- m = master.ConsoleMaster(o, proxy.DummyServer())
+ m = master.ConsoleMaster(o)
monkeypatch.setattr(m.addons.get("clientplayback"), "count", lambda: 42)
monkeypatch.setattr(m.addons.get("serverplayback"), "count", lambda: 42)
diff --git a/test/mitmproxy/tools/test_cmdline.py b/test/mitmproxy/tools/test_cmdline.py
index 65cfeb07..e247dc1d 100644
--- a/test/mitmproxy/tools/test_cmdline.py
+++ b/test/mitmproxy/tools/test_cmdline.py
@@ -1,7 +1,8 @@
import argparse
-from mitmproxy.tools import cmdline
-from mitmproxy.tools import main
+
from mitmproxy import options
+from mitmproxy.tools import cmdline, web, dump, console
+from mitmproxy.tools import main
def test_common():
@@ -14,17 +15,20 @@ def test_common():
def test_mitmproxy():
opts = options.Options()
+ console.master.ConsoleMaster(opts)
ap = cmdline.mitmproxy(opts)
assert ap
def test_mitmdump():
opts = options.Options()
+ dump.DumpMaster(opts)
ap = cmdline.mitmdump(opts)
assert ap
def test_mitmweb():
opts = options.Options()
+ web.master.WebMaster(opts)
ap = cmdline.mitmweb(opts)
assert ap
diff --git a/test/mitmproxy/tools/test_dump.py b/test/mitmproxy/tools/test_dump.py
index 597333af..952c3f4f 100644
--- a/test/mitmproxy/tools/test_dump.py
+++ b/test/mitmproxy/tools/test_dump.py
@@ -1,7 +1,6 @@
import pytest
from unittest import mock
-from mitmproxy import proxy
from mitmproxy import log
from mitmproxy import controller
from mitmproxy import options
@@ -13,7 +12,7 @@ from .. import tservers
class TestDumpMaster(tservers.MasterTest):
def mkmaster(self, flt, **opts):
o = options.Options(view_filter=flt, verbosity='error', flow_detail=0, **opts)
- m = dump.DumpMaster(o, proxy.DummyServer(), with_termlog=False, with_dumper=False)
+ m = dump.DumpMaster(o, with_termlog=False, with_dumper=False)
return m
def test_has_error(self):
@@ -27,12 +26,12 @@ class TestDumpMaster(tservers.MasterTest):
def test_addons_termlog(self, termlog):
with mock.patch('sys.stdout'):
o = options.Options()
- m = dump.DumpMaster(o, proxy.DummyServer(), with_termlog=termlog)
+ m = dump.DumpMaster(o, with_termlog=termlog)
assert (m.addons.get('termlog') is not None) == termlog
@pytest.mark.parametrize("dumper", [False, True])
def test_addons_dumper(self, dumper):
with mock.patch('sys.stdout'):
o = options.Options()
- m = dump.DumpMaster(o, proxy.DummyServer(), with_dumper=dumper)
+ m = dump.DumpMaster(o, with_dumper=dumper)
assert (m.addons.get('dumper') is not None) == dumper
diff --git a/test/mitmproxy/tools/web/test_app.py b/test/mitmproxy/tools/web/test_app.py
index 4d290284..091ef5e8 100644
--- a/test/mitmproxy/tools/web/test_app.py
+++ b/test/mitmproxy/tools/web/test_app.py
@@ -7,7 +7,6 @@ from tornado import httpclient
from tornado import websocket
from mitmproxy import exceptions
-from mitmproxy import proxy
from mitmproxy import options
from mitmproxy.test import tflow
from mitmproxy.tools.web import app
@@ -21,7 +20,7 @@ def json(resp: httpclient.HTTPResponse):
class TestApp(tornado.testing.AsyncHTTPTestCase):
def get_app(self):
o = options.Options(http2=False)
- m = webmaster.WebMaster(o, proxy.DummyServer(), with_termlog=False)
+ m = webmaster.WebMaster(o, with_termlog=False)
f = tflow.tflow(resp=True)
f.id = "42"
m.view.add([f])
@@ -323,5 +322,5 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
web_root = os.path.join(here, os.pardir, os.pardir, os.pardir, os.pardir, 'web')
tflow_path = os.path.join(web_root, 'src/js/__tests__/ducks/_tflow.js')
content = """export default function(){{\n return {tflow_json}\n}}""".format(tflow_json=tflow_json)
- with open(tflow_path, 'w') as f:
+ with open(tflow_path, 'w', newline="\n") as f:
f.write(content)
diff --git a/test/mitmproxy/tools/web/test_master.py b/test/mitmproxy/tools/web/test_master.py
index 27f99a18..2bceb5ca 100644
--- a/test/mitmproxy/tools/web/test_master.py
+++ b/test/mitmproxy/tools/web/test_master.py
@@ -1,7 +1,5 @@
from mitmproxy.tools.web import master
-from mitmproxy import proxy
from mitmproxy import options
-from mitmproxy.proxy.config import ProxyConfig
from ... import tservers
@@ -9,8 +7,7 @@ from ... import tservers
class TestWebMaster(tservers.MasterTest):
def mkmaster(self, **opts):
o = options.Options(**opts)
- c = ProxyConfig(o)
- return master.WebMaster(o, proxy.DummyServer(c))
+ return master.WebMaster(o)
def test_basic(self):
m = self.mkmaster()
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index 9faaf20e..dd5bb327 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -69,9 +69,10 @@ class TestState:
class TestMaster(taddons.RecordingMaster):
- def __init__(self, opts, config):
- s = ProxyServer(config)
- super().__init__(opts, s)
+ def __init__(self, opts):
+ super().__init__(opts)
+ config = ProxyConfig(opts)
+ self.server = ProxyServer(config)
def clear_addons(self, addons):
self.addons.clear()
@@ -129,9 +130,8 @@ class ProxyTestBase:
ssl=cls.ssl,
ssloptions=cls.ssloptions)
- opts = cls.get_options()
- cls.config = ProxyConfig(opts)
- tmaster = cls.masterclass(opts, cls.config)
+ cls.options = cls.get_options()
+ tmaster = cls.masterclass(cls.options)
cls.proxy = ProxyThread(tmaster)
cls.proxy.start()
@@ -338,19 +338,16 @@ class ChainProxyTest(ProxyTestBase):
@classmethod
def setup_class(cls):
+ # We need to initialize the chain first so that the normal server gets a correct config.
cls.chain = []
- super().setup_class()
for _ in range(cls.n):
opts = cls.get_options()
- config = ProxyConfig(opts)
- tmaster = cls.masterclass(opts, config)
+ tmaster = cls.masterclass(opts)
proxy = ProxyThread(tmaster)
proxy.start()
cls.chain.insert(0, proxy)
- # Patch the orginal proxy to upstream mode
- opts = cls.get_options()
- cls.config = cls.proxy.tmaster.config = cls.proxy.tmaster.server.config = ProxyConfig(opts)
+ super().setup_class()
@classmethod
def teardown_class(cls):
diff --git a/test/mitmproxy/utils/test_arg_check.py b/test/mitmproxy/utils/test_arg_check.py
new file mode 100644
index 00000000..72913955
--- /dev/null
+++ b/test/mitmproxy/utils/test_arg_check.py
@@ -0,0 +1,36 @@
+import io
+import contextlib
+from unittest import mock
+
+import pytest
+
+from mitmproxy.utils import arg_check
+
+
+@pytest.mark.parametrize('arg, output', [
+ (["-T"], "-T is deprecated, please use --mode transparent instead"),
+ (["-U"], "-U is deprecated, please use --mode upstream:SPEC instead"),
+ (["--cadir"], "--cadir is deprecated.\n"
+ "Please use `--set cadir=value` instead.\n"
+ "To show all options and their default values use --options"),
+ (["--palette"], "--palette is deprecated.\n"
+ "Please use `--set console_palette=value` instead.\n"
+ "To show all options and their default values use --options"),
+ (["--wfile"], "--wfile is deprecated.\n"
+ "Please use `--save-stream-file` instead."),
+ (["--eventlog"], "--eventlog has been removed."),
+ (["--nonanonymous"], '--nonanonymous is deprecated.\n'
+ 'Please use `--proxyauth SPEC` instead.\n'
+ 'SPEC Format: "username:pass", "any" to accept any user/pass combination,\n'
+ '"@path" to use an Apache htpasswd file, or\n'
+ '"ldap[s]:url_server_ldap:dn_auth:password:dn_subtree" '
+ 'for LDAP authentication.')
+
+])
+def test_check_args(arg, output):
+ f = io.StringIO()
+ with contextlib.redirect_stdout(f):
+ with mock.patch('sys.argv') as m:
+ m.__getitem__.return_value = arg
+ arg_check.check()
+ assert f.getvalue().strip() == output
diff --git a/test/mitmproxy/utils/test_human.py b/test/mitmproxy/utils/test_human.py
index 76dc2f88..e8ffaad4 100644
--- a/test/mitmproxy/utils/test_human.py
+++ b/test/mitmproxy/utils/test_human.py
@@ -22,6 +22,7 @@ def test_parse_size():
human.parse_size("1f")
with pytest.raises(ValueError):
human.parse_size("ak")
+ assert human.parse_size(None) is None
def test_pretty_size():