diff options
-rw-r--r-- | mitmproxy/cmdline.py | 43 | ||||
-rw-r--r-- | mitmproxy/console/master.py | 3 | ||||
-rw-r--r-- | mitmproxy/dump.py | 3 | ||||
-rw-r--r-- | mitmproxy/flow/__init__.py | 3 | ||||
-rw-r--r-- | mitmproxy/flow/options.py | 124 | ||||
-rw-r--r-- | mitmproxy/options.py | 239 | ||||
-rw-r--r-- | mitmproxy/optmanager.py | 108 | ||||
-rw-r--r-- | mitmproxy/web/master.py | 3 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_anticache.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_anticomp.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_filestreamer.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_replace.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_script.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_setheaders.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_stickyauth.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_stickycookie.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/test_flow.py | 7 | ||||
-rw-r--r-- | test/mitmproxy/test_optmanager.py (renamed from test/mitmproxy/test_options.py) | 8 | ||||
-rw-r--r-- | test/mitmproxy/test_protocol_http2.py | 5 | ||||
-rw-r--r-- | test/mitmproxy/test_proxy.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/tservers.py | 10 |
21 files changed, 287 insertions, 287 deletions
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py index b3d3e6dd..a6844241 100644 --- a/mitmproxy/cmdline.py +++ b/mitmproxy/cmdline.py @@ -8,26 +8,11 @@ import configargparse from mitmproxy import exceptions from mitmproxy import filt from mitmproxy import platform +from mitmproxy import options from netlib import human from netlib import tcp from netlib import version -APP_HOST = "mitm.it" -APP_PORT = 80 -CA_DIR = "~/.mitmproxy" - -# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default. -# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old -DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:" \ - "ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" \ - "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:" \ - "ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:" \ - "DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:" \ - "DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" \ - "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" \ - "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \ - "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" - class ParseException(Exception): pass @@ -310,8 +295,8 @@ def basic_options(parser): ) parser.add_argument( "--cadir", - action="store", type=str, dest="cadir", default=CA_DIR, - help="Location of the default mitmproxy CA files. (%s)" % CA_DIR + action="store", type=str, dest="cadir", default=options.CA_DIR, + help="Location of the default mitmproxy CA files. (%s)" % options.CA_DIR ) parser.add_argument( "--host", @@ -462,7 +447,7 @@ def proxy_options(parser): ) group.add_argument( "-p", "--port", - action="store", type=int, dest="port", default=8080, + action="store", type=int, dest="port", default=options.LISTEN_PORT, help="Proxy service port." ) group.add_argument( @@ -509,7 +494,7 @@ def proxy_ssl_options(parser): 'as the first entry. Can be passed multiple times.') group.add_argument( "--ciphers-client", action="store", - type=str, dest="ciphers_client", default=DEFAULT_CLIENT_CIPHERS, + type=str, dest="ciphers_client", default=options.DEFAULT_CLIENT_CIPHERS, help="Set supported ciphers for client connections. (OpenSSL Syntax)" ) group.add_argument( @@ -575,18 +560,18 @@ def onboarding_app(parser): ) group.add_argument( "--app-host", - action="store", dest="app_host", default=APP_HOST, metavar="host", + action="store", dest="app_host", default=options.APP_HOST, metavar="host", help=""" Domain to serve the onboarding app from. For transparent mode, use an IP when a DNS entry for the app domain is not present. Default: %s - """ % APP_HOST + """ % options.APP_HOST ) group.add_argument( "--app-port", action="store", dest="app_port", - default=APP_PORT, + default=options.APP_PORT, type=int, metavar="80", help="Port to serve the onboarding app from." @@ -764,8 +749,8 @@ def mitmproxy(): usage="%(prog)s [options]", args_for_setting_config_path=["--conf"], default_config_files=[ - os.path.join(CA_DIR, "common.conf"), - os.path.join(CA_DIR, "mitmproxy.conf") + os.path.join(options.CA_DIR, "common.conf"), + os.path.join(options.CA_DIR, "mitmproxy.conf") ], add_config_file_help=True, add_env_var_help=True @@ -819,8 +804,8 @@ def mitmdump(): usage="%(prog)s [options] [filter]", args_for_setting_config_path=["--conf"], default_config_files=[ - os.path.join(CA_DIR, "common.conf"), - os.path.join(CA_DIR, "mitmdump.conf") + os.path.join(options.CA_DIR, "common.conf"), + os.path.join(options.CA_DIR, "mitmdump.conf") ], add_config_file_help=True, add_env_var_help=True @@ -849,8 +834,8 @@ def mitmweb(): usage="%(prog)s [options]", args_for_setting_config_path=["--conf"], default_config_files=[ - os.path.join(CA_DIR, "common.conf"), - os.path.join(CA_DIR, "mitmweb.conf") + os.path.join(options.CA_DIR, "common.conf"), + os.path.join(options.CA_DIR, "mitmweb.conf") ], add_config_file_help=True, add_env_var_help=True diff --git a/mitmproxy/console/master.py b/mitmproxy/console/master.py index 86e889cc..59d07456 100644 --- a/mitmproxy/console/master.py +++ b/mitmproxy/console/master.py @@ -23,6 +23,7 @@ from mitmproxy import exceptions from mitmproxy import flow from mitmproxy import script from mitmproxy import utils +import mitmproxy.options from mitmproxy.console import flowlist from mitmproxy.console import flowview from mitmproxy.console import grideditor @@ -178,7 +179,7 @@ class ConsoleState(flow.State): self.add_flow_setting(flow, "marked", marked) -class Options(flow.options.Options): +class Options(mitmproxy.options.Options): def __init__( self, eventlog=False, # type: bool diff --git a/mitmproxy/dump.py b/mitmproxy/dump.py index 78dd2578..4f34ab95 100644 --- a/mitmproxy/dump.py +++ b/mitmproxy/dump.py @@ -12,6 +12,7 @@ from mitmproxy import exceptions from mitmproxy import flow from mitmproxy import builtins from mitmproxy import utils +from mitmproxy import options from mitmproxy.builtins import dumper from netlib import tcp @@ -20,7 +21,7 @@ class DumpError(Exception): pass -class Options(flow.options.Options): +class Options(options.Options): def __init__( self, keepserving=False, # type: bool diff --git a/mitmproxy/flow/__init__.py b/mitmproxy/flow/__init__.py index b2ab74c6..8a64180e 100644 --- a/mitmproxy/flow/__init__.py +++ b/mitmproxy/flow/__init__.py @@ -7,7 +7,6 @@ from mitmproxy.flow.modules import ( AppRegistry, StreamLargeBodies, ClientPlaybackState, ServerPlaybackState ) from mitmproxy.flow.state import State, FlowView -from mitmproxy.flow import options # TODO: We may want to remove the imports from .modules and just expose "modules" @@ -16,5 +15,5 @@ __all__ = [ "FlowWriter", "FilteredFlowWriter", "FlowReader", "read_flows_from_paths", "FlowMaster", "AppRegistry", "StreamLargeBodies", "ClientPlaybackState", - "ServerPlaybackState", "State", "FlowView", "options", + "ServerPlaybackState", "State", "FlowView", ] diff --git a/mitmproxy/flow/options.py b/mitmproxy/flow/options.py deleted file mode 100644 index 726952e2..00000000 --- a/mitmproxy/flow/options.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import absolute_import, print_function, division -from mitmproxy import options -from typing import Tuple, Optional, Sequence # noqa -from mitmproxy import cmdline - -APP_HOST = "mitm.it" -APP_PORT = 80 - - -class Options(options.Options): - def __init__( - self, - # TODO: rename to onboarding_app_* - app=True, # type: bool - app_host=APP_HOST, # type: str - app_port=APP_PORT, # type: int - anticache=False, # type: bool - anticomp=False, # type: bool - client_replay=None, # type: Optional[str] - kill=False, # type: bool - no_server=False, # type: bool - nopop=False, # type: bool - refresh_server_playback=False, # type: bool - rfile=None, # type: Optional[str] - scripts=(), # type: Sequence[str] - showhost=False, # type: bool - replacements=(), # type: Sequence[Tuple[str, str, str]] - rheaders=(), # type: Sequence[str] - setheaders=(), # type: Sequence[Tuple[str, str, str]] - server_replay=None, # type: Optional[str] - stickycookie=None, # type: Optional[str] - stickyauth=None, # type: Optional[str] - stream_large_bodies=None, # type: Optional[str] - verbosity=2, # type: int - outfile=None, # type: Tuple[str, str] - replay_ignore_content=False, # type: bool - replay_ignore_params=(), # type: Sequence[str] - replay_ignore_payload_params=(), # type: Sequence[str] - replay_ignore_host=False, # type: bool - - # Proxy options - auth_nonanonymous=False, # type: bool - auth_singleuser=None, # type: Optional[str] - auth_htpasswd=None, # type: Optional[str] - add_upstream_certs_to_client_chain=False, # type: bool - body_size_limit=None, # type: Optional[int] - cadir = cmdline.CA_DIR, # type: str - certs = (), # type: Sequence[Tuple[str, str]] - ciphers_client = cmdline.DEFAULT_CLIENT_CIPHERS, # type: str - ciphers_server = None, # type: Optional[str] - clientcerts = None, # type: Optional[str] - http2 = True, # type: bool - ignore_hosts = (), # type: Sequence[str] - listen_host = "", # type: str - listen_port = 8080, # type: int - mode = "regular", # type: str - no_upstream_cert = False, # type: bool - rawtcp = False, # type: bool - upstream_server = "", # type: str - upstream_auth = "", # type: str - ssl_version_client="secure", # type: str - ssl_version_server="secure", # type: str - ssl_verify_upstream_cert=False, # type: bool - ssl_verify_upstream_trusted_cadir=None, # type: str - ssl_verify_upstream_trusted_ca=None, # type: str - tcp_hosts = (), # type: Sequence[str] - ): - # We could replace all assignments with clever metaprogramming, - # but type hints are a much more valueable asset. - - self.app = app - self.app_host = app_host - self.app_port = app_port - self.anticache = anticache - self.anticomp = anticomp - self.client_replay = client_replay - self.kill = kill - self.no_server = no_server - self.nopop = nopop - self.refresh_server_playback = refresh_server_playback - self.rfile = rfile - self.scripts = scripts - self.showhost = showhost - self.replacements = replacements - self.rheaders = rheaders - self.setheaders = setheaders - self.server_replay = server_replay - self.stickycookie = stickycookie - self.stickyauth = stickyauth - self.stream_large_bodies = stream_large_bodies - self.verbosity = verbosity - self.outfile = outfile - self.replay_ignore_content = replay_ignore_content - self.replay_ignore_params = replay_ignore_params - self.replay_ignore_payload_params = replay_ignore_payload_params - self.replay_ignore_host = replay_ignore_host - - # Proxy options - self.auth_nonanonymous = auth_nonanonymous - self.auth_singleuser = auth_singleuser - self.auth_htpasswd = auth_htpasswd - self.add_upstream_certs_to_client_chain = add_upstream_certs_to_client_chain - self.body_size_limit = body_size_limit - self.cadir = cadir - self.certs = certs - self.ciphers_client = ciphers_client - self.ciphers_server = ciphers_server - self.clientcerts = clientcerts - self.http2 = http2 - self.ignore_hosts = ignore_hosts - self.listen_host = listen_host - self.listen_port = listen_port - self.mode = mode - self.no_upstream_cert = no_upstream_cert - self.rawtcp = rawtcp - self.upstream_server = upstream_server - self.upstream_auth = upstream_auth - self.ssl_version_client = ssl_version_client - self.ssl_version_server = ssl_version_server - self.ssl_verify_upstream_cert = ssl_verify_upstream_cert - self.ssl_verify_upstream_trusted_cadir = ssl_verify_upstream_trusted_cadir - self.ssl_verify_upstream_trusted_ca = ssl_verify_upstream_trusted_ca - self.tcp_hosts = tcp_hosts - super(Options, self).__init__() diff --git a/mitmproxy/options.py b/mitmproxy/options.py index 94e5d573..bdc0db4e 100644 --- a/mitmproxy/options.py +++ b/mitmproxy/options.py @@ -1,104 +1,137 @@ from __future__ import absolute_import, print_function, division - -import contextlib -import blinker -import pprint - -from mitmproxy import exceptions - - -class Options(object): - """ - .changed is a blinker Signal that triggers whenever options are - updated. If any handler in the chain raises an exceptions.OptionsError - exception, all changes are rolled back, the exception is suppressed, - and the .errored signal is notified. - """ - _initialized = False - attributes = [] - - def __new__(cls, *args, **kwargs): - # Initialize instance._opts before __init__ is called. - # This allows us to call super().__init__() last, which then sets - # ._initialized = True as the final operation. - instance = super(Options, cls).__new__(cls) - instance.__dict__["_opts"] = {} - return instance - - def __init__(self): - self.__dict__["changed"] = blinker.Signal() - self.__dict__["errored"] = blinker.Signal() - self.__dict__["_initialized"] = True - - @contextlib.contextmanager - def rollback(self): - old = self._opts.copy() - try: - yield - except exceptions.OptionsError as e: - # Notify error handlers - self.errored.send(self, exc=e) - # Rollback - self.__dict__["_opts"] = old - self.changed.send(self) - - def __eq__(self, other): - return self._opts == other._opts - - def __copy__(self): - return self.__class__(**self._opts) - - def __getattr__(self, attr): - if attr in self._opts: - return self._opts[attr] - else: - raise AttributeError("No such option: %s" % attr) - - def __setattr__(self, attr, value): - if not self._initialized: - self._opts[attr] = value - return - if attr not in self._opts: - raise KeyError("No such option: %s" % attr) - with self.rollback(): - self._opts[attr] = value - self.changed.send(self) - - def get(self, k, d=None): - return self._opts.get(k, d) - - def update(self, **kwargs): - for k in kwargs: - if k not in self._opts: - raise KeyError("No such option: %s" % k) - with self.rollback(): - self._opts.update(kwargs) - self.changed.send(self) - - def setter(self, attr): - """ - Generate a setter for a given attribute. This returns a callable - taking a single argument. - """ - if attr not in self._opts: - raise KeyError("No such option: %s" % attr) - return lambda x: self.__setattr__(attr, x) - - def toggler(self, attr): - """ - Generate a toggler for a boolean attribute. This returns a callable - that takes no arguments. - """ - if attr not in self._opts: - raise KeyError("No such option: %s" % attr) - return lambda: self.__setattr__(attr, not getattr(self, attr)) - - def __repr__(self): - options = pprint.pformat(self._opts, indent=4).strip(" {}") - if "\n" in options: - options = "\n " + options + "\n" - return "{mod}.{cls}({{{options}}})".format( - mod=type(self).__module__, - cls=type(self).__name__, - options=options - ) +from mitmproxy import optmanager +from typing import Tuple, Optional, Sequence # noqa + +APP_HOST = "mitm.it" +APP_PORT = 80 +CA_DIR = "~/.mitmproxy" +LISTEN_PORT = 8080 + +# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default. +# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old +DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" \ + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:" \ + "ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:" \ + "DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:" \ + "DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" \ + "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" \ + "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \ + "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" + + +class Options(optmanager.OptManager): + def __init__( + self, + # TODO: rename to onboarding_app_* + app=True, # type: bool + app_host=APP_HOST, # type: str + app_port=APP_PORT, # type: int + anticache=False, # type: bool + anticomp=False, # type: bool + client_replay=None, # type: Optional[str] + kill=False, # type: bool + no_server=False, # type: bool + nopop=False, # type: bool + refresh_server_playback=False, # type: bool + rfile=None, # type: Optional[str] + scripts=(), # type: Sequence[str] + showhost=False, # type: bool + replacements=(), # type: Sequence[Tuple[str, str, str]] + rheaders=(), # type: Sequence[str] + setheaders=(), # type: Sequence[Tuple[str, str, str]] + server_replay=None, # type: Optional[str] + stickycookie=None, # type: Optional[str] + stickyauth=None, # type: Optional[str] + stream_large_bodies=None, # type: Optional[str] + verbosity=2, # type: int + outfile=None, # type: Tuple[str, str] + replay_ignore_content=False, # type: bool + replay_ignore_params=(), # type: Sequence[str] + replay_ignore_payload_params=(), # type: Sequence[str] + replay_ignore_host=False, # type: bool + + # Proxy options + auth_nonanonymous=False, # type: bool + auth_singleuser=None, # type: Optional[str] + auth_htpasswd=None, # type: Optional[str] + add_upstream_certs_to_client_chain=False, # type: bool + body_size_limit=None, # type: Optional[int] + cadir = CA_DIR, # type: str + certs = (), # type: Sequence[Tuple[str, str]] + ciphers_client = DEFAULT_CLIENT_CIPHERS, # type: str + ciphers_server = None, # type: Optional[str] + clientcerts = None, # type: Optional[str] + http2 = True, # type: bool + ignore_hosts = (), # type: Sequence[str] + listen_host = "", # type: str + listen_port = LISTEN_PORT, # type: int + mode = "regular", # type: str + no_upstream_cert = False, # type: bool + rawtcp = False, # type: bool + upstream_server = "", # type: str + upstream_auth = "", # type: str + ssl_version_client="secure", # type: str + ssl_version_server="secure", # type: str + ssl_verify_upstream_cert=False, # type: bool + ssl_verify_upstream_trusted_cadir=None, # type: str + ssl_verify_upstream_trusted_ca=None, # type: str + tcp_hosts = (), # type: Sequence[str] + ): + # We could replace all assignments with clever metaprogramming, + # but type hints are a much more valueable asset. + + self.app = app + self.app_host = app_host + self.app_port = app_port + self.anticache = anticache + self.anticomp = anticomp + self.client_replay = client_replay + self.kill = kill + self.no_server = no_server + self.nopop = nopop + self.refresh_server_playback = refresh_server_playback + self.rfile = rfile + self.scripts = scripts + self.showhost = showhost + self.replacements = replacements + self.rheaders = rheaders + self.setheaders = setheaders + self.server_replay = server_replay + self.stickycookie = stickycookie + self.stickyauth = stickyauth + self.stream_large_bodies = stream_large_bodies + self.verbosity = verbosity + self.outfile = outfile + self.replay_ignore_content = replay_ignore_content + self.replay_ignore_params = replay_ignore_params + self.replay_ignore_payload_params = replay_ignore_payload_params + self.replay_ignore_host = replay_ignore_host + + # Proxy options + self.auth_nonanonymous = auth_nonanonymous + self.auth_singleuser = auth_singleuser + self.auth_htpasswd = auth_htpasswd + self.add_upstream_certs_to_client_chain = add_upstream_certs_to_client_chain + self.body_size_limit = body_size_limit + self.cadir = cadir + self.certs = certs + self.ciphers_client = ciphers_client + self.ciphers_server = ciphers_server + self.clientcerts = clientcerts + self.http2 = http2 + self.ignore_hosts = ignore_hosts + self.listen_host = listen_host + self.listen_port = listen_port + self.mode = mode + self.no_upstream_cert = no_upstream_cert + self.rawtcp = rawtcp + self.upstream_server = upstream_server + self.upstream_auth = upstream_auth + self.ssl_version_client = ssl_version_client + self.ssl_version_server = ssl_version_server + self.ssl_verify_upstream_cert = ssl_verify_upstream_cert + self.ssl_verify_upstream_trusted_cadir = ssl_verify_upstream_trusted_cadir + self.ssl_verify_upstream_trusted_ca = ssl_verify_upstream_trusted_ca + self.tcp_hosts = tcp_hosts + super(Options, self).__init__() diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py new file mode 100644 index 00000000..e94ef51d --- /dev/null +++ b/mitmproxy/optmanager.py @@ -0,0 +1,108 @@ +from __future__ import absolute_import, print_function, division + +import contextlib +import blinker +import pprint + +from mitmproxy import exceptions + +""" + The base implementation for Options. +""" + + +class OptManager(object): + """ + .changed is a blinker Signal that triggers whenever options are + updated. If any handler in the chain raises an exceptions.OptionsError + exception, all changes are rolled back, the exception is suppressed, + and the .errored signal is notified. + """ + _initialized = False + attributes = [] + + def __new__(cls, *args, **kwargs): + # Initialize instance._opts before __init__ is called. + # This allows us to call super().__init__() last, which then sets + # ._initialized = True as the final operation. + instance = super(OptManager, cls).__new__(cls) + instance.__dict__["_opts"] = {} + return instance + + def __init__(self): + self.__dict__["changed"] = blinker.Signal() + self.__dict__["errored"] = blinker.Signal() + self.__dict__["_initialized"] = True + + @contextlib.contextmanager + def rollback(self): + old = self._opts.copy() + try: + yield + except exceptions.OptionsError as e: + # Notify error handlers + self.errored.send(self, exc=e) + # Rollback + self.__dict__["_opts"] = old + self.changed.send(self) + + def __eq__(self, other): + return self._opts == other._opts + + def __copy__(self): + return self.__class__(**self._opts) + + def __getattr__(self, attr): + if attr in self._opts: + return self._opts[attr] + else: + raise AttributeError("No such option: %s" % attr) + + def __setattr__(self, attr, value): + if not self._initialized: + self._opts[attr] = value + return + if attr not in self._opts: + raise KeyError("No such option: %s" % attr) + with self.rollback(): + self._opts[attr] = value + self.changed.send(self) + + def get(self, k, d=None): + return self._opts.get(k, d) + + def update(self, **kwargs): + for k in kwargs: + if k not in self._opts: + raise KeyError("No such option: %s" % k) + with self.rollback(): + self._opts.update(kwargs) + self.changed.send(self) + + def setter(self, attr): + """ + Generate a setter for a given attribute. This returns a callable + taking a single argument. + """ + if attr not in self._opts: + raise KeyError("No such option: %s" % attr) + return lambda x: self.__setattr__(attr, x) + + def toggler(self, attr): + """ + Generate a toggler for a boolean attribute. This returns a callable + that takes no arguments. + """ + if attr not in self._opts: + raise KeyError("No such option: %s" % attr) + return lambda: self.__setattr__(attr, not getattr(self, attr)) + + def __repr__(self): + options = pprint.pformat(self._opts, indent=4).strip(" {}") + if "\n" in options: + options = "\n " + options + "\n" + return "{mod}.{cls}({{{options}}})".format( + mod=type(self).__module__, + cls=type(self).__name__, + options=options + ) diff --git a/mitmproxy/web/master.py b/mitmproxy/web/master.py index a0d68191..3d384612 100644 --- a/mitmproxy/web/master.py +++ b/mitmproxy/web/master.py @@ -12,6 +12,7 @@ from mitmproxy import builtins from mitmproxy import controller from mitmproxy import exceptions from mitmproxy import flow +from mitmproxy import options from mitmproxy.web import app from netlib.http import authentication @@ -91,7 +92,7 @@ class WebState(flow.State): ) -class Options(flow.options.Options): +class Options(options.Options): def __init__( self, intercept=None, # type: Optional[str] diff --git a/test/mitmproxy/builtins/test_anticache.py b/test/mitmproxy/builtins/test_anticache.py index 127e1c1a..5a00af03 100644 --- a/test/mitmproxy/builtins/test_anticache.py +++ b/test/mitmproxy/builtins/test_anticache.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import anticache from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestAntiCache(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_anticomp.py b/test/mitmproxy/builtins/test_anticomp.py index 601e56c8..6bfd54bb 100644 --- a/test/mitmproxy/builtins/test_anticomp.py +++ b/test/mitmproxy/builtins/test_anticomp.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import anticomp from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestAntiComp(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_filestreamer.py b/test/mitmproxy/builtins/test_filestreamer.py index 002006b7..c1d5947f 100644 --- a/test/mitmproxy/builtins/test_filestreamer.py +++ b/test/mitmproxy/builtins/test_filestreamer.py @@ -7,7 +7,7 @@ import os.path from mitmproxy.builtins import filestreamer from mitmproxy.flow import master, FlowReader from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestStream(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_replace.py b/test/mitmproxy/builtins/test_replace.py index f8010bec..a0b4b722 100644 --- a/test/mitmproxy/builtins/test_replace.py +++ b/test/mitmproxy/builtins/test_replace.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import replace from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestReplace(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_script.py b/test/mitmproxy/builtins/test_script.py index c9616249..f37c7f94 100644 --- a/test/mitmproxy/builtins/test_script.py +++ b/test/mitmproxy/builtins/test_script.py @@ -4,7 +4,7 @@ from mitmproxy.builtins import script from mitmproxy import exceptions from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options from .. import tutils, mastertest diff --git a/test/mitmproxy/builtins/test_setheaders.py b/test/mitmproxy/builtins/test_setheaders.py index 1a8d048c..4465719d 100644 --- a/test/mitmproxy/builtins/test_setheaders.py +++ b/test/mitmproxy/builtins/test_setheaders.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import setheaders from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestSetHeaders(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_stickyauth.py b/test/mitmproxy/builtins/test_stickyauth.py index 1e617402..9233f435 100644 --- a/test/mitmproxy/builtins/test_stickyauth.py +++ b/test/mitmproxy/builtins/test_stickyauth.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import stickyauth from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options class TestStickyAuth(mastertest.MasterTest): diff --git a/test/mitmproxy/builtins/test_stickycookie.py b/test/mitmproxy/builtins/test_stickycookie.py index b8d703bd..81b540db 100644 --- a/test/mitmproxy/builtins/test_stickycookie.py +++ b/test/mitmproxy/builtins/test_stickycookie.py @@ -2,7 +2,7 @@ from .. import tutils, mastertest from mitmproxy.builtins import stickycookie from mitmproxy.flow import master from mitmproxy.flow import state -from mitmproxy.flow import options +from mitmproxy import options from netlib import tutils as ntutils diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index e17a125c..36b212a7 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -3,8 +3,7 @@ import io import netlib.utils from netlib.http import Headers -from mitmproxy import filt, controller, flow -from mitmproxy.flow import options +from mitmproxy import filt, controller, flow, options from mitmproxy.contrib import tnetstring from mitmproxy.exceptions import FlowReadException from mitmproxy.models import Error @@ -745,7 +744,7 @@ class TestFlowMaster: f = tutils.tflow(resp=True) pb = [tutils.tflow(resp=True), f] fm = flow.FlowMaster( - flow.options.Options(), + options.Options(), DummyServer(ProxyConfig(options.Options())), s ) @@ -776,7 +775,7 @@ class TestFlowMaster: f.response = HTTPResponse.wrap(netlib.tutils.tresp(content=f.request)) pb = [f] - fm = flow.FlowMaster(flow.options.Options(), None, s) + fm = flow.FlowMaster(options.Options(), None, s) fm.refresh_server_playback = True assert not fm.do_server_playback(tutils.tflow()) diff --git a/test/mitmproxy/test_options.py b/test/mitmproxy/test_optmanager.py index af619b27..67f76ecd 100644 --- a/test/mitmproxy/test_options.py +++ b/test/mitmproxy/test_optmanager.py @@ -1,12 +1,12 @@ from __future__ import absolute_import, print_function, division import copy -from mitmproxy import options +from mitmproxy import optmanager from mitmproxy import exceptions from netlib import tutils -class TO(options.Options): +class TO(optmanager.OptManager): def __init__(self, one=None, two=None): self.one = one self.two = two @@ -93,8 +93,8 @@ def test_rollback(): def test_repr(): - assert repr(TO()) == "test.mitmproxy.test_options.TO({'one': None, 'two': None})" - assert repr(TO(one='x' * 60)) == """test.mitmproxy.test_options.TO({ + assert repr(TO()) == "test.mitmproxy.test_optmanager.TO({'one': None, 'two': None})" + assert repr(TO(one='x' * 60)) == """test.mitmproxy.test_optmanager.TO({ 'one': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'two': None })""" diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py index d11570d9..afbffb67 100644 --- a/test/mitmproxy/test_protocol_http2.py +++ b/test/mitmproxy/test_protocol_http2.py @@ -9,9 +9,8 @@ import traceback import h2 -from mitmproxy.flow import options +from mitmproxy import options from mitmproxy.proxy.config import ProxyConfig -from mitmproxy.cmdline import APP_HOST, APP_PORT import netlib from ..netlib import tservers as netlib_tservers @@ -94,7 +93,7 @@ class _Http2TestBase(object): cls.config = ProxyConfig(opts) tmaster = tservers.TestMaster(opts, cls.config) - tmaster.start_app(APP_HOST, APP_PORT) + tmaster.start_app(options.APP_HOST, options.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 7095d9d2..6e790e28 100644 --- a/test/mitmproxy/test_proxy.py +++ b/test/mitmproxy/test_proxy.py @@ -3,10 +3,10 @@ import mock from OpenSSL import SSL from mitmproxy import cmdline +from mitmproxy import options from mitmproxy.proxy import ProxyConfig from mitmproxy.models.connections import ServerConnection from mitmproxy.proxy.server import DummyServer, ProxyServer, ConnectionHandler -from mitmproxy.flow import options from mitmproxy.proxy import config from netlib.exceptions import TcpDisconnect from pathod import test diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py index 30980e40..f5119166 100644 --- a/test/mitmproxy/tservers.py +++ b/test/mitmproxy/tservers.py @@ -8,9 +8,7 @@ from mitmproxy.proxy.config import ProxyConfig from mitmproxy.proxy.server import ProxyServer import pathod.test import pathod.pathoc -from mitmproxy import flow, controller -from mitmproxy.flow import options -from mitmproxy.cmdline import APP_HOST, APP_PORT +from mitmproxy import flow, controller, options from mitmproxy import builtins testapp = flask.Flask(__name__) @@ -93,7 +91,7 @@ class ProxyTestBase(object): opts = cls.get_options() cls.config = ProxyConfig(opts) tmaster = cls.masterclass(opts, cls.config) - tmaster.start_app(APP_HOST, APP_PORT) + tmaster.start_app(options.APP_HOST, options.APP_PORT) cls.proxy = ProxyThread(tmaster) cls.proxy.start() @@ -160,11 +158,11 @@ class HTTPProxyTest(ProxyTestBase): p = pathod.pathoc.Pathoc( ("127.0.0.1", self.proxy.port), True, fp=None ) - p.connect((APP_HOST, APP_PORT)) + p.connect((options.APP_HOST, options.APP_PORT)) return p.request("get:'%s'" % page) else: p = self.pathoc() - return p.request("get:'http://%s%s'" % (APP_HOST, page)) + return p.request("get:'http://%s%s'" % (options.APP_HOST, page)) class TResolver: |