From 9bc1514aefe524a0c6afceec8e2d2afe15acdf66 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Mon, 18 Jul 2016 12:05:10 +1200 Subject: Unify proxy config and options This is step 1 in a gradual "boiling frog" strategy. Decorate ProxyConfig with an Options object. --- mitmproxy/main.py | 68 +++++++++++++-------------- mitmproxy/proxy/config.py | 87 ++++++++++++++++++----------------- test/mitmproxy/test_flow.py | 4 +- test/mitmproxy/test_protocol_http2.py | 6 ++- test/mitmproxy/test_proxy.py | 7 ++- test/mitmproxy/tservers.py | 16 ++++--- 6 files changed, 100 insertions(+), 88 deletions(-) diff --git a/mitmproxy/main.py b/mitmproxy/main.py index 316db91a..9b62b63d 100644 --- a/mitmproxy/main.py +++ b/mitmproxy/main.py @@ -41,14 +41,14 @@ def get_server(dummy_server, options): sys.exit(1) -def process_options(parser, options): - if options.sysinfo: +def process_options(parser, options, args): + if args.sysinfo: print(debug.sysinfo()) sys.exit(0) - if options.quiet: - options.verbose = 0 + if args.quiet: + args.verbose = 0 debug.register_info_dumpers() - return config.process_proxy_options(parser, options) + return config.process_proxy_options(parser, options, args) def mitmproxy(args=None): # pragma: no cover @@ -62,18 +62,18 @@ def mitmproxy(args=None): # pragma: no cover assert_utf8_env() parser = cmdline.mitmproxy() - options = parser.parse_args(args) - proxy_config = process_options(parser, options) - - console_options = console.master.Options(**cmdline.get_common_options(options)) - console_options.palette = options.palette - console_options.palette_transparent = options.palette_transparent - console_options.eventlog = options.eventlog - console_options.follow = options.follow - console_options.intercept = options.intercept - console_options.limit = options.limit - console_options.no_mouse = options.no_mouse - + args = parser.parse_args(args) + + console_options = console.master.Options(**cmdline.get_common_options(args)) + console_options.palette = args.palette + console_options.palette_transparent = args.palette_transparent + console_options.eventlog = args.eventlog + console_options.follow = args.follow + console_options.intercept = args.intercept + console_options.limit = args.limit + console_options.no_mouse = args.no_mouse + + proxy_config = process_options(parser, console_options, args) server = get_server(console_options.no_server, proxy_config) try: @@ -93,16 +93,16 @@ def mitmdump(args=None): # pragma: no cover version_check.check_pyopenssl_version() parser = cmdline.mitmdump() - options = parser.parse_args(args) - proxy_config = process_options(parser, options) - if options.quiet: - options.flow_detail = 0 + args = parser.parse_args(args) + if args.quiet: + args.flow_detail = 0 - dump_options = dump.Options(**cmdline.get_common_options(options)) - dump_options.flow_detail = options.flow_detail - dump_options.keepserving = options.keepserving - dump_options.filtstr = " ".join(options.args) if options.args else None + dump_options = dump.Options(**cmdline.get_common_options(args)) + dump_options.flow_detail = args.flow_detail + dump_options.keepserving = args.keepserving + dump_options.filtstr = " ".join(args.args) if args.args else None + proxy_config = process_options(parser, dump_options, args) server = get_server(dump_options.no_server, proxy_config) try: @@ -130,18 +130,18 @@ def mitmweb(args=None): # pragma: no cover parser = cmdline.mitmweb() - options = parser.parse_args(args) - proxy_config = process_options(parser, options) + args = parser.parse_args(args) - web_options = web.master.Options(**cmdline.get_common_options(options)) - web_options.intercept = options.intercept - web_options.wdebug = options.wdebug - web_options.wiface = options.wiface - web_options.wport = options.wport - web_options.wsingleuser = options.wsingleuser - web_options.whtpasswd = options.whtpasswd + web_options = web.master.Options(**cmdline.get_common_options(args)) + web_options.intercept = args.intercept + web_options.wdebug = args.wdebug + web_options.wiface = args.wiface + web_options.wport = args.wport + web_options.wsingleuser = args.wsingleuser + web_options.whtpasswd = args.whtpasswd web_options.process_web_options(parser) + proxy_config = process_options(parser, web_options, args) server = get_server(web_options.no_server, proxy_config) try: diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py index 32d881b0..3aa16174 100644 --- a/mitmproxy/proxy/config.py +++ b/mitmproxy/proxy/config.py @@ -59,6 +59,7 @@ class ProxyConfig: def __init__( self, + options, host='', port=8080, cadir=CA_DIR, @@ -83,6 +84,7 @@ class ProxyConfig: ssl_verify_upstream_trusted_ca=None, add_upstream_certs_to_client_chain=False, ): + self.options = options self.host = host self.port = port self.ciphers_client = ciphers_client @@ -125,79 +127,79 @@ class ProxyConfig: self.add_upstream_certs_to_client_chain = add_upstream_certs_to_client_chain -def process_proxy_options(parser, options): - body_size_limit = options.body_size_limit +def process_proxy_options(parser, options, args): + body_size_limit = args.body_size_limit if body_size_limit: body_size_limit = human.parse_size(body_size_limit) c = 0 mode, upstream_server, upstream_auth = "regular", None, None - if options.transparent_proxy: + if args.transparent_proxy: c += 1 if not platform.resolver: return parser.error("Transparent mode not supported on this platform.") mode = "transparent" - if options.socks_proxy: + if args.socks_proxy: c += 1 mode = "socks5" - if options.reverse_proxy: + if args.reverse_proxy: c += 1 mode = "reverse" - upstream_server = options.reverse_proxy - if options.upstream_proxy: + upstream_server = args.reverse_proxy + if args.upstream_proxy: c += 1 mode = "upstream" - upstream_server = options.upstream_proxy - upstream_auth = options.upstream_auth + upstream_server = args.upstream_proxy + upstream_auth = args.upstream_auth if c > 1: return parser.error( "Transparent, SOCKS5, reverse and upstream proxy mode " "are mutually exclusive. Read the docs on proxy modes to understand why." ) - if options.add_upstream_certs_to_client_chain and options.no_upstream_cert: + if args.add_upstream_certs_to_client_chain and args.no_upstream_cert: return parser.error( "The no-upstream-cert and add-upstream-certs-to-client-chain " "options are mutually exclusive. If no-upstream-cert is enabled " "then the upstream certificate is not retrieved before generating " "the client certificate chain." ) - if options.add_upstream_certs_to_client_chain and options.ssl_verify_upstream_cert: + if args.add_upstream_certs_to_client_chain and args.ssl_verify_upstream_cert: return parser.error( "The verify-upstream-cert and add-upstream-certs-to-client-chain " "options are mutually exclusive. If upstream certificates are verified " "then extra upstream certificates are not available for inclusion " "to the client chain." ) - if options.clientcerts: - options.clientcerts = os.path.expanduser(options.clientcerts) - if not os.path.exists(options.clientcerts): + if args.clientcerts: + args.clientcerts = os.path.expanduser(args.clientcerts) + if not os.path.exists(args.clientcerts): return parser.error( - "Client certificate path does not exist: %s" % options.clientcerts + "Client certificate path does not exist: %s" % args.clientcerts ) - if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd: + if args.auth_nonanonymous or args.auth_singleuser or args.auth_htpasswd: - if options.transparent_proxy: + if args.transparent_proxy: return parser.error("Proxy Authentication not supported in transparent mode.") - if options.socks_proxy: + if args.socks_proxy: return parser.error( "Proxy Authentication not supported in SOCKS mode. " "https://github.com/mitmproxy/mitmproxy/issues/738" ) - if options.auth_singleuser: - if len(options.auth_singleuser.split(':')) != 2: + if args.auth_singleuser: + if len(args.auth_singleuser.split(':')) != 2: return parser.error( "Invalid single-user specification. Please use the format username:password" ) - username, password = options.auth_singleuser.split(':') + username, password = args.auth_singleuser.split(':') password_manager = authentication.PassManSingleUser(username, password) - elif options.auth_nonanonymous: + elif args.auth_nonanonymous: password_manager = authentication.PassManNonAnon() - elif options.auth_htpasswd: + elif args.auth_htpasswd: try: password_manager = authentication.PassManHtpasswd( - options.auth_htpasswd) + args.auth_htpasswd) except ValueError as v: return parser.error(v) authenticator = authentication.BasicProxyAuth(password_manager, "mitmproxy") @@ -205,7 +207,7 @@ def process_proxy_options(parser, options): authenticator = authentication.NullProxyAuth(None) certs = [] - for i in options.certs: + for i in args.certs: parts = i.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] @@ -215,27 +217,28 @@ def process_proxy_options(parser, options): certs.append(parts) return ProxyConfig( - host=options.addr, - port=options.port, - cadir=options.cadir, - clientcerts=options.clientcerts, - no_upstream_cert=options.no_upstream_cert, + options, + host=args.addr, + port=args.port, + cadir=args.cadir, + clientcerts=args.clientcerts, + no_upstream_cert=args.no_upstream_cert, body_size_limit=body_size_limit, mode=mode, upstream_server=upstream_server, upstream_auth=upstream_auth, - ignore_hosts=options.ignore_hosts, - tcp_hosts=options.tcp_hosts, - http2=options.http2, - rawtcp=options.rawtcp, + ignore_hosts=args.ignore_hosts, + tcp_hosts=args.tcp_hosts, + http2=args.http2, + rawtcp=args.rawtcp, authenticator=authenticator, - ciphers_client=options.ciphers_client, - ciphers_server=options.ciphers_server, + ciphers_client=args.ciphers_client, + ciphers_server=args.ciphers_server, certs=tuple(certs), - ssl_version_client=options.ssl_version_client, - ssl_version_server=options.ssl_version_server, - ssl_verify_upstream_cert=options.ssl_verify_upstream_cert, - ssl_verify_upstream_trusted_cadir=options.ssl_verify_upstream_trusted_cadir, - ssl_verify_upstream_trusted_ca=options.ssl_verify_upstream_trusted_ca, - add_upstream_certs_to_client_chain=options.add_upstream_certs_to_client_chain, + ssl_version_client=args.ssl_version_client, + ssl_version_server=args.ssl_version_server, + ssl_verify_upstream_cert=args.ssl_verify_upstream_cert, + ssl_verify_upstream_trusted_cadir=args.ssl_verify_upstream_trusted_cadir, + ssl_verify_upstream_trusted_ca=args.ssl_verify_upstream_trusted_ca, + add_upstream_certs_to_client_chain=args.add_upstream_certs_to_client_chain, ) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index 90f7f915..a44353e7 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -4,6 +4,7 @@ import io import netlib.utils from netlib.http import Headers from mitmproxy import filt, controller, flow +from mitmproxy.flow import options from mitmproxy.contrib import tnetstring from mitmproxy.exceptions import FlowReadException from mitmproxy.models import Error @@ -640,6 +641,7 @@ class TestSerialize: r = self._treader() s = flow.State() conf = ProxyConfig( + options.Options(), mode="reverse", upstream_server=("https", ("use-this-domain", 80)) ) @@ -753,7 +755,7 @@ class TestFlowMaster: pb = [tutils.tflow(resp=True), f] fm = flow.FlowMaster( flow.options.Options(), - DummyServer(ProxyConfig()), + DummyServer(ProxyConfig(options.Options())), s ) assert not fm.start_server_playback( diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py index b8f724bd..04ca94bf 100644 --- a/test/mitmproxy/test_protocol_http2.py +++ b/test/mitmproxy/test_protocol_http2.py @@ -9,6 +9,7 @@ import traceback import h2 +from mitmproxy.flow import options from mitmproxy.proxy.config import ProxyConfig from mitmproxy.cmdline import APP_HOST, APP_PORT @@ -88,9 +89,10 @@ class _Http2TestBase(object): @classmethod def setup_class(cls): - cls.config = ProxyConfig(**cls.get_proxy_config()) + cls.masteroptions = options.Options() + cls.config = ProxyConfig(cls.masteroptions, **cls.get_proxy_config()) - tmaster = tservers.TestMaster(cls.config) + tmaster = tservers.TestMaster(cls.masteroptions, cls.config) tmaster.start_app(APP_HOST, APP_PORT) cls.proxy = tservers.ProxyThread(tmaster) cls.proxy.start() diff --git a/test/mitmproxy/test_proxy.py b/test/mitmproxy/test_proxy.py index cd24fc9f..f4f73cf5 100644 --- a/test/mitmproxy/test_proxy.py +++ b/test/mitmproxy/test_proxy.py @@ -7,6 +7,7 @@ from mitmproxy.proxy import ProxyConfig from mitmproxy.proxy.config import process_proxy_options from mitmproxy.models.connections import ServerConnection from mitmproxy.proxy.server import DummyServer, ProxyServer, ConnectionHandler +from mitmproxy.flow import options from netlib.exceptions import TcpDisconnect from pathod import test from netlib.http import http1 @@ -58,8 +59,8 @@ class TestProcessProxyOptions: def p(self, *args): parser = tutils.MockParser() cmdline.common_options(parser) - opts = parser.parse_args(args=args) - return parser, process_proxy_options(parser, opts) + args = parser.parse_args(args=args) + return parser, process_proxy_options(parser, options.Options(), args) def assert_err(self, err, *args): tutils.raises(err, self.p, *args) @@ -159,12 +160,14 @@ class TestProxyServer: @tutils.skip_windows def test_err(self): conf = ProxyConfig( + options.Options(), port=1 ) tutils.raises("error starting proxy server", ProxyServer, conf) def test_err_2(self): conf = ProxyConfig( + options.Options(), host="invalidhost" ) tutils.raises("error starting proxy server", ProxyServer, conf) diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py index 9b830b2d..e236656e 100644 --- a/test/mitmproxy/tservers.py +++ b/test/mitmproxy/tservers.py @@ -32,11 +32,11 @@ def errapp(environ, start_response): class TestMaster(flow.FlowMaster): - def __init__(self, config): + def __init__(self, opts, config): config.port = 0 s = ProxyServer(config) state = flow.State() - flow.FlowMaster.__init__(self, options.Options(), s, state) + flow.FlowMaster.__init__(self, opts, s, state) self.addons.add(*builtins.default_addons()) self.apps.add(testapp, "testapp", 80) self.apps.add(errapp, "errapp", 80) @@ -80,6 +80,7 @@ class ProxyTestBase(object): no_upstream_cert = False authenticator = None masterclass = TestMaster + masteroptions = options.Options() add_upstream_certs_to_client_chain = False @classmethod @@ -91,9 +92,8 @@ class ProxyTestBase(object): ssl=cls.ssl, ssloptions=cls.ssloptions) - cls.config = ProxyConfig(**cls.get_proxy_config()) - - tmaster = cls.masterclass(cls.config) + cls.config = ProxyConfig(cls.masteroptions, **cls.get_proxy_config()) + tmaster = cls.masterclass(cls.masteroptions, cls.config) tmaster.start_app(APP_HOST, APP_PORT) cls.proxy = ProxyThread(tmaster) cls.proxy.start() @@ -284,17 +284,19 @@ class ChainProxyTest(ProxyTestBase): @classmethod def setup_class(cls): + cls.masteroptions = options.Options() cls.chain = [] super(ChainProxyTest, cls).setup_class() for _ in range(cls.n): - config = ProxyConfig(**cls.get_proxy_config()) - tmaster = cls.masterclass(config) + config = ProxyConfig(cls.masteroptions, **cls.get_proxy_config()) + tmaster = cls.masterclass(cls.masteroptions, config) proxy = ProxyThread(tmaster) proxy.start() cls.chain.insert(0, proxy) # Patch the orginal proxy to upstream mode cls.config = cls.proxy.tmaster.config = cls.proxy.tmaster.server.config = ProxyConfig( + cls.masteroptions, **cls.get_proxy_config()) @classmethod -- cgit v1.2.3