diff options
author | Aldo Cortesi <aldo@nullcube.com> | 2016-07-20 11:10:15 +1200 |
---|---|---|
committer | Aldo Cortesi <aldo@nullcube.com> | 2016-07-20 11:28:53 +1200 |
commit | 8317772377baa5eaa9c4d05014f952337f4d03cd (patch) | |
tree | a2aa4f84facb7af17a91a948b7f30074fb64c655 | |
parent | aa3b866e1d82b7a08d497695eb0fa2c1ddd2c2cf (diff) | |
download | mitmproxy-8317772377baa5eaa9c4d05014f952337f4d03cd.tar.gz mitmproxy-8317772377baa5eaa9c4d05014f952337f4d03cd.tar.bz2 mitmproxy-8317772377baa5eaa9c4d05014f952337f4d03cd.zip |
mitproxy.options -> mitmproxy.optmanager, mitmproxy.flow.options -> mitmproxy.options
It might be slightly more felicitous to move optmanager into netlib at some
point, especially if we can also use it in pathod.
This also consolidates our constants in mitmproxy.options, removing some duplicates.
-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: |