aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/pathod/intro.rst6
-rw-r--r--mitmproxy/addons/__init__.py2
-rw-r--r--mitmproxy/addons/check_alpn.py17
-rw-r--r--mitmproxy/addons/view.py18
-rw-r--r--mitmproxy/net/tcp.py47
-rw-r--r--mitmproxy/optmanager.py1
-rw-r--r--mitmproxy/proxy/protocol/http.py23
-rw-r--r--mitmproxy/tools/main.py2
-rw-r--r--mitmproxy/tools/web/app.py12
-rw-r--r--mitmproxy/tools/web/master.py1
-rw-r--r--mitmproxy/utils/version_check.py42
-rw-r--r--pathod/language/generators.py2
-rw-r--r--pathod/pathoc.py8
-rw-r--r--pathod/pathoc_cmdline.py1
-rw-r--r--pathod/pathod_cmdline.py1
-rw-r--r--test/conftest.py13
-rw-r--r--test/mitmproxy/addons/test_check_alpn.py23
-rw-r--r--test/mitmproxy/addons/test_view.py4
-rw-r--r--test/mitmproxy/data/no_common_name.pem96
-rw-r--r--test/mitmproxy/net/test_tcp.py41
-rw-r--r--test/mitmproxy/proxy/protocol/test_http2.py12
-rw-r--r--test/mitmproxy/tools/console/conftest.py9
-rw-r--r--test/mitmproxy/tools/console/test_master.py14
-rw-r--r--test/mitmproxy/tools/console/test_statusbar.py34
-rw-r--r--test/mitmproxy/tools/web/test_app.py4
-rw-r--r--test/mitmproxy/utils/test_version_check.py25
-rw-r--r--test/pathod/protocols/test_http2.py4
-rw-r--r--test/pathod/test_pathoc.py15
-rw-r--r--test/pathod/test_pathod.py6
-rw-r--r--web/src/js/__tests__/components/Modal/OptionModalSpec.js54
-rw-r--r--web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap35
-rw-r--r--web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap61
-rw-r--r--web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap2
-rw-r--r--web/src/js/__tests__/ducks/optionsSpec.js15
-rw-r--r--web/src/js/components/Modal/Option.jsx2
-rw-r--r--web/src/js/components/Modal/OptionModal.jsx33
-rw-r--r--web/src/js/ducks/options.js4
37 files changed, 408 insertions, 281 deletions
diff --git a/docs/pathod/intro.rst b/docs/pathod/intro.rst
index bf0c531f..1c1ad60e 100644
--- a/docs/pathod/intro.rst
+++ b/docs/pathod/intro.rst
@@ -3,6 +3,7 @@
Pathology 101
=============
+.. _pathod:
pathod
------
@@ -83,15 +84,14 @@ distinguish them from crafted responses. For example, a request to:
.. _pathoc:
-
pathoc
------
Pathoc is a perverse HTTP daemon designed to let you craft almost any
conceivable HTTP request, including ones that creatively violate the standards.
HTTP requests are specified using a :ref:`small, terse language <language>`,
-which pathod shares with its server-side twin pathod. To view pathoc's complete
-range of options, use the command-line help:
+which pathoc shares with its server-side twin :ref:`pathod`. To view pathoc's
+complete range of options, use the command-line help:
>>> pathoc --help
diff --git a/mitmproxy/addons/__init__.py b/mitmproxy/addons/__init__.py
index 24cf2270..62135765 100644
--- a/mitmproxy/addons/__init__.py
+++ b/mitmproxy/addons/__init__.py
@@ -1,7 +1,6 @@
from mitmproxy.addons import allowremote
from mitmproxy.addons import anticache
from mitmproxy.addons import anticomp
-from mitmproxy.addons import check_alpn
from mitmproxy.addons import check_ca
from mitmproxy.addons import clientplayback
from mitmproxy.addons import core_option_validation
@@ -29,7 +28,6 @@ def default_addons():
allowremote.AllowRemote(),
anticache.AntiCache(),
anticomp.AntiComp(),
- check_alpn.CheckALPN(),
check_ca.CheckCA(),
clientplayback.ClientPlayback(),
cut.Cut(),
diff --git a/mitmproxy/addons/check_alpn.py b/mitmproxy/addons/check_alpn.py
deleted file mode 100644
index 193159b2..00000000
--- a/mitmproxy/addons/check_alpn.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import mitmproxy
-from mitmproxy.net import tcp
-from mitmproxy import ctx
-
-
-class CheckALPN:
- def __init__(self):
- self.failed = False
-
- def configure(self, updated):
- self.failed = mitmproxy.ctx.master.options.http2 and not tcp.HAS_ALPN
- if self.failed:
- ctx.log.warn(
- "HTTP/2 is disabled because ALPN support missing!\n"
- "OpenSSL 1.0.2+ required to support HTTP/2 connections.\n"
- "Use --no-http2 to silence this warning."
- )
diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py
index d4319468..b2db0171 100644
--- a/mitmproxy/addons/view.py
+++ b/mitmproxy/addons/view.py
@@ -10,6 +10,7 @@ The View:
"""
import collections
import typing
+import os
import blinker
import sortedcontainers
@@ -339,12 +340,17 @@ class View(collections.Sequence):
"""
Load flows into the view, without processing them with addons.
"""
- with open(path, "rb") as f:
- for i in io.FlowReader(f).stream():
- # Do this to get a new ID, so we can load the same file N times and
- # get new flows each time. It would be more efficient to just have a
- # .newid() method or something.
- self.add([i.copy()])
+ path = os.path.expanduser(path)
+ try:
+ with open(path, "rb") as f:
+ for i in io.FlowReader(f).stream():
+ # Do this to get a new ID, so we can load the same file N times and
+ # get new flows each time. It would be more efficient to just have a
+ # .newid() method or something.
+ self.add([i.copy()])
+ except IOError as e:
+ ctx.log.error(e.strerror)
+ return
@command.command("view.go")
def go(self, dst: int) -> None:
diff --git a/mitmproxy/net/tcp.py b/mitmproxy/net/tcp.py
index fce0b744..0c2f0e28 100644
--- a/mitmproxy/net/tcp.py
+++ b/mitmproxy/net/tcp.py
@@ -14,18 +14,12 @@ from typing import Optional # noqa
from mitmproxy.utils import strutils
import certifi
-import OpenSSL
from OpenSSL import SSL
from mitmproxy import certs
-from mitmproxy.utils import version_check
from mitmproxy import exceptions
from mitmproxy.types import basethread
-# This is a rather hackish way to make sure that
-# the latest version of pyOpenSSL is actually installed.
-version_check.check_pyopenssl_version()
-
socket_fileobject = socket.SocketIO
# workaround for https://bugs.python.org/issue29515
@@ -33,7 +27,6 @@ socket_fileobject = socket.SocketIO
IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41)
EINTR = 4
-HAS_ALPN = SSL._lib.Cryptography_HAS_ALPN
# To enable all SSL methods use: SSLv23
# then add options to disable certain methods
@@ -503,7 +496,6 @@ class _Connection:
if cipher_list:
try:
context.set_cipher_list(cipher_list.encode())
- context.set_tmp_ecdh(OpenSSL.crypto.get_elliptic_curve('prime256v1'))
except SSL.Error as v:
raise exceptions.TlsException("SSL cipher specification error: %s" % str(v))
@@ -511,24 +503,23 @@ class _Connection:
if log_ssl_key:
context.set_info_callback(log_ssl_key)
- if HAS_ALPN: # pragma: openssl-old no cover
- if alpn_protos is not None:
- # advertise application layer protocols
- context.set_alpn_protos(alpn_protos)
- elif alpn_select is not None and alpn_select_callback is None:
- # select application layer protocol
- def alpn_select_callback(conn_, options):
- if alpn_select in options:
- return bytes(alpn_select)
- else: # pragma: no cover
- return options[0]
- context.set_alpn_select_callback(alpn_select_callback)
- elif alpn_select_callback is not None and alpn_select is None:
- if not callable(alpn_select_callback):
- raise exceptions.TlsException("ALPN error: alpn_select_callback must be a function.")
- context.set_alpn_select_callback(alpn_select_callback)
- elif alpn_select_callback is not None and alpn_select is not None:
- raise exceptions.TlsException("ALPN error: only define alpn_select (string) OR alpn_select_callback (function).")
+ if alpn_protos is not None:
+ # advertise application layer protocols
+ context.set_alpn_protos(alpn_protos)
+ elif alpn_select is not None and alpn_select_callback is None:
+ # select application layer protocol
+ def alpn_select_callback(conn_, options):
+ if alpn_select in options:
+ return bytes(alpn_select)
+ else: # pragma: no cover
+ return options[0]
+ context.set_alpn_select_callback(alpn_select_callback)
+ elif alpn_select_callback is not None and alpn_select is None:
+ if not callable(alpn_select_callback):
+ raise exceptions.TlsException("ALPN error: alpn_select_callback must be a function.")
+ context.set_alpn_select_callback(alpn_select_callback)
+ elif alpn_select_callback is not None and alpn_select is not None:
+ raise exceptions.TlsException("ALPN error: only define alpn_select (string) OR alpn_select_callback (function).")
return context
@@ -720,7 +711,7 @@ class TCPClient(_Connection):
return self.connection.gettimeout()
def get_alpn_proto_negotiated(self):
- if HAS_ALPN and self.ssl_established: # pragma: openssl-old no cover
+ if self.ssl_established:
return self.connection.get_alpn_proto_negotiated()
else:
return b""
@@ -827,7 +818,7 @@ class BaseHandler(_Connection):
self.connection.settimeout(n)
def get_alpn_proto_negotiated(self):
- if HAS_ALPN and self.ssl_established: # pragma: openssl-old no cover
+ if self.ssl_established:
return self.connection.get_alpn_proto_negotiated()
else:
return b""
diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py
index e1d74b8e..c28ec685 100644
--- a/mitmproxy/optmanager.py
+++ b/mitmproxy/optmanager.py
@@ -518,6 +518,7 @@ def save(opts, path, defaults=False):
Raises OptionsError if the existing data is corrupt.
"""
+ path = os.path.expanduser(path)
if os.path.exists(path) and os.path.isfile(path):
with open(path, "rt", encoding="utf8") as f:
try:
diff --git a/mitmproxy/proxy/protocol/http.py b/mitmproxy/proxy/protocol/http.py
index 502280c1..a366861d 100644
--- a/mitmproxy/proxy/protocol/http.py
+++ b/mitmproxy/proxy/protocol/http.py
@@ -217,16 +217,19 @@ class HttpLayer(base.Layer):
return False
def handle_upstream_connect(self, f):
- self.establish_server_connection(
- f.request.host,
- f.request.port,
- f.request.scheme
- )
- self.send_request(f.request)
- f.response = self.read_response_headers()
- f.response.data.content = b"".join(
- self.read_response_body(f.request, f.response)
- )
+ # if the user specifies a response in the http_connect hook, we do not connect upstream here.
+ # https://github.com/mitmproxy/mitmproxy/pull/2473
+ if not f.response:
+ self.establish_server_connection(
+ f.request.host,
+ f.request.port,
+ f.request.scheme
+ )
+ self.send_request(f.request)
+ f.response = self.read_response_headers()
+ f.response.data.content = b"".join(
+ self.read_response_body(f.request, f.response)
+ )
self.send_response(f.response)
if is_ok(f.response.status_code):
layer = UpstreamConnectLayer(self, f.request)
diff --git a/mitmproxy/tools/main.py b/mitmproxy/tools/main.py
index 7debb3e0..58900d29 100644
--- a/mitmproxy/tools/main.py
+++ b/mitmproxy/tools/main.py
@@ -16,7 +16,6 @@ from mitmproxy import exceptions # noqa
from mitmproxy import options # noqa
from mitmproxy import optmanager # noqa
from mitmproxy import proxy # noqa
-from mitmproxy.utils import version_check # noqa
from mitmproxy.utils import debug # noqa
@@ -58,7 +57,6 @@ def run(MasterKlass, args, extra=None): # pragma: no cover
extra: Extra argument processing callable which returns a dict of
options.
"""
- version_check.check_pyopenssl_version()
debug.register_info_dumpers()
opts = options.Options()
diff --git a/mitmproxy/tools/web/app.py b/mitmproxy/tools/web/app.py
index c6fb2ef6..9a447fe7 100644
--- a/mitmproxy/tools/web/app.py
+++ b/mitmproxy/tools/web/app.py
@@ -18,6 +18,7 @@ from mitmproxy import io
from mitmproxy import log
from mitmproxy import version
from mitmproxy import optmanager
+from mitmproxy.tools.cmdline import CONFIG_PATH
import mitmproxy.tools.web.master # noqa
@@ -451,6 +452,14 @@ class Options(RequestHandler):
raise APIError(400, "{}".format(err))
+class SaveOptions(RequestHandler):
+ def post(self):
+ try:
+ optmanager.save(self.master.options, CONFIG_PATH, True)
+ except Exception as err:
+ raise APIError(400, "{}".format(err))
+
+
class Application(tornado.web.Application):
def __init__(self, master, debug):
self.master = master
@@ -475,7 +484,8 @@ class Application(tornado.web.Application):
FlowContentView),
(r"/settings", Settings),
(r"/clear", ClearAll),
- (r"/options", Options)
+ (r"/options", Options),
+ (r"/options/save", SaveOptions)
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
diff --git a/mitmproxy/tools/web/master.py b/mitmproxy/tools/web/master.py
index 8c2433ec..dc5b2627 100644
--- a/mitmproxy/tools/web/master.py
+++ b/mitmproxy/tools/web/master.py
@@ -125,7 +125,6 @@ class WebMaster(master.Master):
"No web browser found. Please open a browser and point it to {}".format(web_url),
"info"
)
-
try:
iol.start()
except KeyboardInterrupt:
diff --git a/mitmproxy/utils/version_check.py b/mitmproxy/utils/version_check.py
deleted file mode 100644
index 22d6d75c..00000000
--- a/mitmproxy/utils/version_check.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-Having installed a wrong version of pyOpenSSL is unfortunately a very common
-source of error. Check before every start that both versions are somewhat okay.
-"""
-import sys
-import inspect
-import os.path
-
-import OpenSSL
-
-PYOPENSSL_MIN_VERSION = (16, 0)
-
-
-def check_pyopenssl_version(min_version=PYOPENSSL_MIN_VERSION, fp=sys.stderr):
- min_version_str = ".".join(str(x) for x in min_version)
- try:
- v = tuple(int(x) for x in OpenSSL.__version__.split(".")[:2])
- except ValueError:
- print(
- "Cannot parse pyOpenSSL version: {}"
- "mitmproxy requires pyOpenSSL {} or greater.".format(
- OpenSSL.__version__, min_version_str
- ),
- file=fp
- )
- return
- if v < min_version:
- print(
- "You are using an outdated version of pyOpenSSL: "
- "mitmproxy requires pyOpenSSL {} or greater.".format(min_version_str),
- file=fp
- )
- # Some users apparently have multiple versions of pyOpenSSL installed.
- # Report which one we got.
- pyopenssl_path = os.path.dirname(inspect.getfile(OpenSSL))
- print(
- "Your pyOpenSSL {} installation is located at {}".format(
- OpenSSL.__version__, pyopenssl_path
- ),
- file=fp
- )
- sys.exit(1)
diff --git a/pathod/language/generators.py b/pathod/language/generators.py
index 1961df74..70c6ad16 100644
--- a/pathod/language/generators.py
+++ b/pathod/language/generators.py
@@ -75,7 +75,7 @@ class RandomGenerator:
class FileGenerator:
def __init__(self, path):
- self.path = path
+ self.path = os.path.expanduser(path)
def __len__(self):
return os.path.getsize(self.path)
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index 4a613349..63a15b55 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -223,14 +223,6 @@ class Pathoc(tcp.TCPClient):
self.ws_framereader = None
if self.use_http2:
- if not tcp.HAS_ALPN: # pragma: no cover
- log.write_raw(
- self.fp,
- "HTTP/2 requires ALPN support. "
- "Please use OpenSSL >= 1.0.2. "
- "Pathoc might not be working as expected without ALPN.",
- timestamp=False
- )
self.protocol = http2.HTTP2StateProtocol(self, dump_frames=self.http2_framedump)
else:
self.protocol = net_http.http1
diff --git a/pathod/pathoc_cmdline.py b/pathod/pathoc_cmdline.py
index 3b738d47..0854f6ad 100644
--- a/pathod/pathoc_cmdline.py
+++ b/pathod/pathoc_cmdline.py
@@ -208,6 +208,7 @@ def args_pathoc(argv, stdout=sys.stdout, stderr=sys.stderr):
reqs = []
for r in args.requests:
+ r = os.path.expanduser(r)
if os.path.isfile(r):
with open(r) as f:
r = f.read()
diff --git a/pathod/pathod_cmdline.py b/pathod/pathod_cmdline.py
index dee19f4f..c646aaee 100644
--- a/pathod/pathod_cmdline.py
+++ b/pathod/pathod_cmdline.py
@@ -215,6 +215,7 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
anchors = []
for patt, spec in args.anchors:
+ spec = os.path.expanduser(spec)
if os.path.isfile(spec):
with open(spec) as f:
data = f.read()
diff --git a/test/conftest.py b/test/conftest.py
index bb913548..b0842bc3 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,15 +1,8 @@
import os
import pytest
-import OpenSSL
-
-import mitmproxy.net.tcp
pytest_plugins = ('test.full_coverage_plugin',)
-requires_alpn = pytest.mark.skipif(
- not mitmproxy.net.tcp.HAS_ALPN,
- reason='requires OpenSSL with ALPN support')
-
skip_windows = pytest.mark.skipif(
os.name == "nt",
reason='Skipping due to Windows'
@@ -24,9 +17,3 @@ skip_appveyor = pytest.mark.skipif(
"APPVEYOR" in os.environ,
reason='Skipping due to Appveyor'
)
-
-
-@pytest.fixture()
-def disable_alpn(monkeypatch):
- monkeypatch.setattr(mitmproxy.net.tcp, 'HAS_ALPN', False)
- monkeypatch.setattr(OpenSSL.SSL._lib, 'Cryptography_HAS_ALPN', False)
diff --git a/test/mitmproxy/addons/test_check_alpn.py b/test/mitmproxy/addons/test_check_alpn.py
deleted file mode 100644
index 2b1d6058..00000000
--- a/test/mitmproxy/addons/test_check_alpn.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from mitmproxy.addons import check_alpn
-from mitmproxy.test import taddons
-from ...conftest import requires_alpn
-
-
-class TestCheckALPN:
-
- @requires_alpn
- def test_check_alpn(self):
- msg = 'ALPN support missing'
-
- with taddons.context() as tctx:
- a = check_alpn.CheckALPN()
- tctx.configure(a)
- assert not tctx.master.has_log(msg)
-
- def test_check_no_alpn(self, disable_alpn):
- msg = 'ALPN support missing'
-
- with taddons.context() as tctx:
- a = check_alpn.CheckALPN()
- tctx.configure(a)
- assert tctx.master.has_log(msg)
diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py
index 40136f1f..e8eeb591 100644
--- a/test/mitmproxy/addons/test_view.py
+++ b/test/mitmproxy/addons/test_view.py
@@ -170,6 +170,10 @@ def test_load(tmpdir):
assert len(v) == 2
v.load_file(path)
assert len(v) == 4
+ try:
+ v.load_file("nonexistent_file_path")
+ except IOError:
+ assert False
def test_resolve():
diff --git a/test/mitmproxy/data/no_common_name.pem b/test/mitmproxy/data/no_common_name.pem
index fc271a0e..d46448f5 100644
--- a/test/mitmproxy/data/no_common_name.pem
+++ b/test/mitmproxy/data/no_common_name.pem
@@ -1,20 +1,84 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIBOQIBAAJBAKVJ43C+8SjOvN9/pP/8HwzmHGQmRvdK/R6KlWdr7He6iiXDQNfH
-RAp+gqX0hBRT80eRjGhSmTTBLCWiXVny4UUCAwEAAQJAUQ8nZ0d85VJd9g2XUaLH
-Z4ACNGtBKk2wTKYSFyIqWZxsF5qhh7HGshJIAP6tYiX8ZW+mMSfme+zsJzWe8ChL
-gQIhAM8QpAgUHnNteZvkv0XqceX1GILEWifMt+hO9yTp4dY5AiEAzFnKr77CKCri
-/DPig4R/5q4KMpMx9EqJufHdGNmIA20CICMARxnufK86RCIr6oEg/hvG8Fu6YRr1
-Kekk3/XnavtRAiBVLVQ7vwKE5aNpRmMzOKZrS736aLpYvjz8IaFr+zgjXQIgdad5
-QZoTD49NTyMEgyZp70gTXcXQLrX2PgQKL4uNmoU=
+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
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
-MIIBgTCCASugAwIBAgIJAKlcXsPLQAQuMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV
-BAYTAkFVMB4XDTEzMTIxMjAxMzA1NVoXDTE0MDExMTAxMzA1NVowDTELMAkGA1UE
-BhMCQVUwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApUnjcL7xKM6833+k//wfDOYc
-ZCZG90r9HoqVZ2vsd7qKJcNA18dECn6CpfSEFFPzR5GMaFKZNMEsJaJdWfLhRQID
-AQABo24wbDAdBgNVHQ4EFgQUJm8BXcVRsROy0PVt5stkB3eVnEgwPQYDVR0jBDYw
-NIAUJm8BXcVRsROy0PVt5stkB3eVnEihEaQPMA0xCzAJBgNVBAYTAkFVggkAqVxe
-w8tABC4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAHHxcBEpWrIqtLVH
-m6Yn1hgqrAbfMj9IK6zY9C5Cbad/DfUj3AZMb5u758WJK0x9brmckgqdrQsuf9He
-Ef51/SU=
+MIIFtTCCA52gAwIBAgIJAM/qkBYP5ExSMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTcwNzI1MDg0MDAwWhcNMTgwNzI1MDg0MDAwWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEA4Jut+R51opC773ToeUhwJOVGnpxNqzZTDMImO141WPvKMjMsi15f0U3O
+KqK8YERDfDzaAbgqz6MNgqc8QbNJ0e9VxtMUzTkCwSlbDHMFgZNyXVRXOQEBJ/fT
+MlU+LimOH38QY0orifdAHH+kPUYIiqTBzgJvCy8w1o4hGSlzf2HW400dmlRSJEVg
+Bj6nXQENbVxmxf6f9H19eWpnLuP5aJIwP4LEsGdqLP0ESnWfZVPIAiEsLuYkhbRX
+qiuJfnc1am8LexzLi4VQMCw0K4Tm1lTbapcnOUakO6orQvX7MOEKYBU+ogGGby0M
+yOTaCkuUFi0YdTtU4DEdWJ9HfogRO/uG1325/8/T+tq8RgOkp9cUvjU3ONqpLZC6
+zs0YR8OzlTbXClmV+Mr6d1qEb3jk6zWlLykLVozOS3z5vdVxpbJqM/CtHporAA0c
+Ser2EGtN68OB3Hy3Bh7MPjqpSFSJ1uTQ755jS3qOzwggoQFCz2dBmfyi7nOGHFW4
+h/5NaF1mnIlJJVnJIZajSNl9e6klGeXmJv4ZtiqJd/CG0jTUnGWOTimukSmxVNcW
+s2vFjwQhuRJwawzGo7O1gZPkq3/0/F+yLp5karmRJs8sQ/JDvGL4rW5YqW7u1WuY
+QeHYHscfPwf0be8teKWcURIqBoHPxdJV3s9zf8Y/AN9OFFdPqwcCAwEAAaOBpzCB
+pDAdBgNVHQ4EFgQUZrQUSE9A8i0N4ZuQZq/F4I74QlwwdQYDVR0jBG4wbIAUZrQU
+SE9A8i0N4ZuQZq/F4I74QlyhSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
+b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDP
+6pAWD+RMUjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQDPeylCUY4k
+nG1KoT139g5T5G1/lxgmYnDqQB1+5JYCQWsPK7sy19tD58bq3+2N2Tozu2/f/GkG
+LZtouLyRciFtcAWy4LQlSR4hTLAWeik2WV/h+ovfv4XwvRuwS5PYVQNHQsOAO3ea
+hX+W+w+rwI+MFlgEHJO85P61ijcNVbiTpgd0s47RViKyJVDqfhCmpobzS5eTbbXn
+F1oFlV84lgEt84BE4RJxlr6fSIxZn6rQPjdbY65snol7Zs2oAt7nLb3hpZgWKobF
+3xAfkC9m19nQHeYz3JlNC7sf80top2H2HEZMVVOAD+MxXkcAjNbjBRT3/KAIyWex
+2fmoGRbvCIU0LFyyyk7/tG1xTgxNuBmd4Byz1LI25uz6eK4Ey8LeZmp5mvaewI53
+t65sAGBkx+LIRt0yGmMCRRFl735Ya4SJD7je1GTiw9I3Yd63dtaJTVd65kkFkLOk
+LD56iJHSyCY6JkDXd8RjozIVoaXkVQh2wFq/ZfXzAgIx/u5cJQCMG2DAu6/WI74+
+7invOv7dbYfoI02N4iB57iRbPxE4gSrRayYxUVdH1R/tlXbN9Fkd30fl2WfSO897
+QC/ODA9w86FSFANhn6nv2KuKIMUSEW+5ZhBowSFIBEdAaMS7yj9uuBWmQKrWNfOh
+mZJF1YiFmgRybkdKHPrlCSZyvVBdmnmM6g==
-----END CERTIFICATE-----
diff --git a/test/mitmproxy/net/test_tcp.py b/test/mitmproxy/net/test_tcp.py
index 73de0879..3345840e 100644
--- a/test/mitmproxy/net/test_tcp.py
+++ b/test/mitmproxy/net/test_tcp.py
@@ -3,7 +3,6 @@ import queue
import time
import socket
import random
-import os
import threading
import pytest
from unittest import mock
@@ -15,7 +14,6 @@ from mitmproxy import exceptions
from mitmproxy.test import tutils
from . import tservers
-from ...conftest import requires_alpn
class EchoHandler(tcp.BaseHandler):
@@ -534,36 +532,18 @@ class TestTimeOut(tservers.ServerTestBase):
c.rfile.read(10)
-class TestCryptographyALPN:
-
- def test_has_alpn(self):
- if os.environ.get("OPENSSL") == "with-alpn":
- assert tcp.HAS_ALPN
- assert SSL._lib.Cryptography_HAS_ALPN
- elif os.environ.get("OPENSSL") == "old":
- assert not tcp.HAS_ALPN
- assert not SSL._lib.Cryptography_HAS_ALPN
-
-
class TestALPNClient(tservers.ServerTestBase):
handler = ALPNHandler
ssl = dict(
alpn_select=b"bar"
)
- @requires_alpn
- @pytest.mark.parametrize('has_alpn,alpn_protos, expected_negotiated, expected_response', [
- (True, [b"foo", b"bar", b"fasel"], b'bar', b'bar'),
- (True, [], b'', b'NONE'),
- (True, None, b'', b'NONE'),
- (False, [b"foo", b"bar", b"fasel"], b'', b'NONE'),
- (False, [], b'', b'NONE'),
- (False, None, b'', b'NONE'),
+ @pytest.mark.parametrize('alpn_protos, expected_negotiated, expected_response', [
+ ([b"foo", b"bar", b"fasel"], b'bar', b'bar'),
+ ([], b'', b'NONE'),
+ (None, b'', b'NONE'),
])
- def test_alpn(self, monkeypatch, has_alpn, alpn_protos, expected_negotiated, expected_response):
- monkeypatch.setattr(tcp, 'HAS_ALPN', has_alpn)
- monkeypatch.setattr(SSL._lib, 'Cryptography_HAS_ALPN', has_alpn)
-
+ def test_alpn(self, monkeypatch, alpn_protos, expected_negotiated, expected_response):
c = tcp.TCPClient(("127.0.0.1", self.port))
with c.connect():
c.convert_to_ssl(alpn_protos=alpn_protos)
@@ -574,7 +554,7 @@ class TestALPNClient(tservers.ServerTestBase):
class TestNoSSLNoALPNClient(tservers.ServerTestBase):
handler = ALPNHandler
- def test_no_ssl_no_alpn(self, disable_alpn):
+ def test_no_ssl_no_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
with c.connect():
assert c.get_alpn_proto_negotiated() == b""
@@ -857,9 +837,8 @@ class TestSSLInvalid(tservers.ServerTestBase):
def test_alpn_error(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
with c.connect():
- if tcp.HAS_ALPN:
- with pytest.raises(exceptions.TlsException, match="must be a function"):
- c.create_ssl_context(alpn_select_callback="foo")
+ with pytest.raises(exceptions.TlsException, match="must be a function"):
+ c.create_ssl_context(alpn_select_callback="foo")
- with pytest.raises(exceptions.TlsException, match="ALPN error"):
- c.create_ssl_context(alpn_select="foo", alpn_select_callback="bar")
+ with pytest.raises(exceptions.TlsException, match="ALPN error"):
+ c.create_ssl_context(alpn_select="foo", alpn_select_callback="bar")
diff --git a/test/mitmproxy/proxy/protocol/test_http2.py b/test/mitmproxy/proxy/protocol/test_http2.py
index 487d8890..583e6e27 100644
--- a/test/mitmproxy/proxy/protocol/test_http2.py
+++ b/test/mitmproxy/proxy/protocol/test_http2.py
@@ -17,7 +17,6 @@ from mitmproxy.net.http import http1, http2
from pathod.language import generators
from ... import tservers
-from ....conftest import requires_alpn
import logging
logging.getLogger("hyper.packages.hpack.hpack").setLevel(logging.WARNING)
@@ -203,7 +202,6 @@ class _Http2Test(_Http2TestBase, _Http2ServerBase):
_Http2ServerBase.teardown_class()
-@requires_alpn
class TestSimple(_Http2Test):
request_body_buffer = b''
@@ -286,7 +284,6 @@ class TestSimple(_Http2Test):
assert response_body_buffer == b'response body'
-@requires_alpn
class TestRequestWithPriority(_Http2Test):
@classmethod
@@ -368,7 +365,6 @@ class TestRequestWithPriority(_Http2Test):
assert resp.headers.get('priority_weight', None) == expected_priority[2]
-@requires_alpn
class TestPriority(_Http2Test):
@classmethod
@@ -453,7 +449,6 @@ class TestPriority(_Http2Test):
assert self.priority_data == expected_priority
-@requires_alpn
class TestStreamResetFromServer(_Http2Test):
@classmethod
@@ -504,7 +499,6 @@ class TestStreamResetFromServer(_Http2Test):
assert self.master.state.flows[0].response is None
-@requires_alpn
class TestBodySizeLimit(_Http2Test):
@classmethod
@@ -554,7 +548,6 @@ class TestBodySizeLimit(_Http2Test):
assert len(self.master.state.flows) == 0
-@requires_alpn
class TestPushPromise(_Http2Test):
@classmethod
@@ -723,7 +716,6 @@ class TestPushPromise(_Http2Test):
# the other two bodies might not be transmitted before the reset
-@requires_alpn
class TestConnectionLost(_Http2Test):
@classmethod
@@ -765,7 +757,6 @@ class TestConnectionLost(_Http2Test):
assert self.master.state.flows[0].response is None
-@requires_alpn
class TestMaxConcurrentStreams(_Http2Test):
@classmethod
@@ -826,7 +817,6 @@ class TestMaxConcurrentStreams(_Http2Test):
assert b"Stream-ID " in flow.response.content
-@requires_alpn
class TestConnectionTerminated(_Http2Test):
@classmethod
@@ -867,7 +857,6 @@ class TestConnectionTerminated(_Http2Test):
assert connection_terminated_event.additional_data == b'foobar'
-@requires_alpn
class TestRequestStreaming(_Http2Test):
@classmethod
@@ -926,7 +915,6 @@ class TestRequestStreaming(_Http2Test):
assert connection_terminated_event is None
-@requires_alpn
class TestResponseStreaming(_Http2Test):
@classmethod
diff --git a/test/mitmproxy/tools/console/conftest.py b/test/mitmproxy/tools/console/conftest.py
new file mode 100644
index 00000000..afd94c6a
--- /dev/null
+++ b/test/mitmproxy/tools/console/conftest.py
@@ -0,0 +1,9 @@
+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 7732483f..a3478bdc 100644
--- a/test/mitmproxy/tools/console/test_master.py
+++ b/test/mitmproxy/tools/console/test_master.py
@@ -1,20 +1,12 @@
-import pytest
+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
-from mitmproxy import proxy
-from mitmproxy import options
from mitmproxy.tools.console import common
from ... import tservers
-import urwid
-from unittest import mock
-
-
-@pytest.fixture(scope="module", autouse=True)
-def definitely_atty():
- with mock.patch("sys.stdout.isatty", lambda: True):
- yield
def test_format_keyvals():
diff --git a/test/mitmproxy/tools/console/test_statusbar.py b/test/mitmproxy/tools/console/test_statusbar.py
new file mode 100644
index 00000000..55a3c4a0
--- /dev/null
+++ b/test/mitmproxy/tools/console/test_statusbar.py
@@ -0,0 +1,34 @@
+from mitmproxy import options, proxy
+from mitmproxy.tools.console import statusbar, master
+
+
+def test_statusbar(monkeypatch):
+ o = options.Options(
+ setheaders=[":~q:foo:bar"],
+ replacements=[":~q:foo:bar"],
+ ignore_hosts=["example.com", "example.org"],
+ tcp_hosts=["example.tcp"],
+ intercept="~q",
+ view_filter="~dst example.com",
+ stickycookie="~dst example.com",
+ stickyauth="~dst example.com",
+ default_contentview="javascript",
+ console_order="url",
+ anticache=True,
+ anticomp=True,
+ showhost=True,
+ refresh_server_playback=False,
+ replay_kill_extra=True,
+ upstream_cert=False,
+ console_focus_follow=True,
+ stream_large_bodies="3m",
+ mode="transparent",
+ scripts=["nonexistent"],
+ save_stream_file="foo",
+ )
+ m = master.ConsoleMaster(o, proxy.DummyServer())
+ monkeypatch.setattr(m.addons.get("clientplayback"), "count", lambda: 42)
+ monkeypatch.setattr(m.addons.get("serverplayback"), "count", lambda: 42)
+
+ bar = statusbar.StatusBar(m) # this already causes a redraw
+ assert bar.ib._w
diff --git a/test/mitmproxy/tools/web/test_app.py b/test/mitmproxy/tools/web/test_app.py
index bb439b34..4d290284 100644
--- a/test/mitmproxy/tools/web/test_app.py
+++ b/test/mitmproxy/tools/web/test_app.py
@@ -263,6 +263,9 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
assert self.put_json("/options", {"wtf": True}).code == 400
assert self.put_json("/options", {"anticache": "foo"}).code == 400
+ def test_option_save(self):
+ assert self.fetch("/options/save", method="POST").code == 200
+
def test_err(self):
with mock.patch("mitmproxy.tools.web.app.IndexHandler.get") as f:
f.side_effect = RuntimeError
@@ -279,7 +282,6 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
r2 = yield ws_client.read_message()
j1 = _json.loads(r1)
j2 = _json.loads(r2)
- print(j1)
response = dict()
response[j1['resource']] = j1
response[j2['resource']] = j2
diff --git a/test/mitmproxy/utils/test_version_check.py b/test/mitmproxy/utils/test_version_check.py
deleted file mode 100644
index d7929378..00000000
--- a/test/mitmproxy/utils/test_version_check.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import io
-from unittest import mock
-from mitmproxy.utils import version_check
-
-
-@mock.patch("sys.exit")
-def test_check_pyopenssl_version(sexit):
- fp = io.StringIO()
- version_check.check_pyopenssl_version(fp=fp)
- assert not fp.getvalue()
- assert not sexit.called
-
- version_check.check_pyopenssl_version((9999,), fp=fp)
- assert "outdated" in fp.getvalue()
- assert sexit.called
-
-
-@mock.patch("sys.exit")
-@mock.patch("OpenSSL.__version__")
-def test_unparseable_pyopenssl_version(version, sexit):
- version.split.return_value = ["foo", "bar"]
- fp = io.StringIO()
- version_check.check_pyopenssl_version(fp=fp)
- assert "Cannot parse" in fp.getvalue()
- assert not sexit.called
diff --git a/test/pathod/protocols/test_http2.py b/test/pathod/protocols/test_http2.py
index c16a6d40..b1eebc73 100644
--- a/test/pathod/protocols/test_http2.py
+++ b/test/pathod/protocols/test_http2.py
@@ -11,8 +11,6 @@ from ...mitmproxy.net import tservers as net_tservers
from pathod.protocols.http2 import HTTP2StateProtocol, TCPHandler
-from ...conftest import requires_alpn
-
class TestTCPHandlerWrapper:
def test_wrapped(self):
@@ -68,7 +66,6 @@ class TestProtocol:
assert mock_server_method.called
-@requires_alpn
class TestCheckALPNMatch(net_tservers.ServerTestBase):
handler = EchoHandler
ssl = dict(
@@ -83,7 +80,6 @@ class TestCheckALPNMatch(net_tservers.ServerTestBase):
assert protocol.check_alpn()
-@requires_alpn
class TestCheckALPNMismatch(net_tservers.ServerTestBase):
handler = EchoHandler
ssl = dict(
diff --git a/test/pathod/test_pathoc.py b/test/pathod/test_pathoc.py
index 2dd29e20..4b50e2a7 100644
--- a/test/pathod/test_pathoc.py
+++ b/test/pathod/test_pathoc.py
@@ -11,7 +11,6 @@ from pathod.protocols.http2 import HTTP2StateProtocol
from mitmproxy.test import tutils
from . import tservers
-from ..conftest import requires_alpn
def test_response():
@@ -216,7 +215,6 @@ class TestDaemonHTTP2(PathocTestDaemon):
ssl = True
explain = False
- @requires_alpn
def test_http2(self):
c = pathoc.Pathoc(
("127.0.0.1", self.d.port),
@@ -231,7 +229,6 @@ class TestDaemonHTTP2(PathocTestDaemon):
)
assert c.protocol == http1
- @requires_alpn
def test_http2_alpn(self):
c = pathoc.Pathoc(
("127.0.0.1", self.d.port),
@@ -248,7 +245,6 @@ class TestDaemonHTTP2(PathocTestDaemon):
_, kwargs = c.convert_to_ssl.call_args
assert set(kwargs['alpn_protos']) == set([b'http/1.1', b'h2'])
- @requires_alpn
def test_request(self):
c = pathoc.Pathoc(
("127.0.0.1", self.d.port),
@@ -259,14 +255,3 @@ class TestDaemonHTTP2(PathocTestDaemon):
with c.connect():
resp = c.request("get:/p/200")
assert resp.status_code == 200
-
- def test_failing_request(self, disable_alpn):
- c = pathoc.Pathoc(
- ("127.0.0.1", self.d.port),
- fp=None,
- ssl=True,
- use_http2=True,
- )
- with pytest.raises(NotImplementedError):
- with c.connect():
- c.request("get:/p/200")
diff --git a/test/pathod/test_pathod.py b/test/pathod/test_pathod.py
index 88480a59..5f191c0d 100644
--- a/test/pathod/test_pathod.py
+++ b/test/pathod/test_pathod.py
@@ -8,7 +8,6 @@ from mitmproxy import exceptions
from mitmproxy.test import tutils
from . import tservers
-from ..conftest import requires_alpn
class TestPathod:
@@ -257,11 +256,6 @@ class TestHTTP2(tservers.DaemonTests):
ssl = True
nohang = True
- @requires_alpn
def test_http2(self):
r, _ = self.pathoc(["GET:/"], ssl=True, use_http2=True)
assert r[0].status_code == 800
-
- def test_no_http2(self, disable_alpn):
- with pytest.raises(NotImplementedError):
- r, _ = self.pathoc(["GET:/"], ssl=True, use_http2=True)
diff --git a/web/src/js/__tests__/components/Modal/OptionModalSpec.js b/web/src/js/__tests__/components/Modal/OptionModalSpec.js
new file mode 100644
index 00000000..dd4e70a2
--- /dev/null
+++ b/web/src/js/__tests__/components/Modal/OptionModalSpec.js
@@ -0,0 +1,54 @@
+import React from 'react'
+import renderer from 'react-test-renderer'
+import { PureOptionDefault } from '../../../components/Modal/OptionModal'
+
+describe('PureOptionDefault Component', () => {
+
+ it('should return null when the value is default', () => {
+ let pureOptionDefault = renderer.create(
+ <PureOptionDefault value="foo" defaultVal="foo"/>
+ ),
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('should handle boolean type', () => {
+ let pureOptionDefault = renderer.create(
+ <PureOptionDefault value={true} defaultVal={false}/>
+ ),
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('should handle array', () => {
+ let a = [""], b = [], c = ['c'],
+ pureOptionDefault = renderer.create(
+ <PureOptionDefault value={a} defaultVal={b}/>
+ ),
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+
+ pureOptionDefault = renderer.create(
+ <PureOptionDefault value={a} defaultVal={c}/>
+ )
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('should handle string', () => {
+ let pureOptionDefault = renderer.create(
+ <PureOptionDefault value="foo" defaultVal=""/>
+ ),
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+ })
+
+ it('should handle null value', () => {
+ let pureOptionDefault = renderer.create(
+ <PureOptionDefault value="foo" defaultVal={null}/>
+ ),
+ tree = pureOptionDefault.toJSON()
+ expect(tree).toMatchSnapshot()
+ })
+
+})
diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap
index bfd855bd..8d9271f1 100644
--- a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap
+++ b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap
@@ -117,7 +117,7 @@ exports[`Modal Component should render correctly 2`] = `
name="choiceOption"
onChange={[Function]}
onKeyDown={[Function]}
- selected="b"
+ value="b"
>
<option
value="a"
@@ -136,6 +136,17 @@ exports[`Modal Component should render correctly 2`] = `
</option>
</select>
</div>
+ <div
+ className="small"
+ >
+ Default:
+ <strong>
+
+ a
+
+ </strong>
+
+ </div>
</div>
</div>
<div
@@ -170,6 +181,17 @@ exports[`Modal Component should render correctly 2`] = `
value={1}
/>
</div>
+ <div
+ className="small"
+ >
+ Default:
+ <strong>
+
+ 0
+
+ </strong>
+
+ </div>
</div>
</div>
<div
@@ -207,6 +229,17 @@ exports[`Modal Component should render correctly 2`] = `
<div
className="small text-danger"
/>
+ <div
+ className="small"
+ >
+ Default:
+ <strong>
+
+ null
+
+ </strong>
+
+ </div>
</div>
</div>
</div>
diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap
new file mode 100644
index 00000000..68f1c9fc
--- /dev/null
+++ b/web/src/js/__tests__/components/Modal/__snapshots__/OptionModalSpec.js.snap
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`PureOptionDefault Component should handle array 1`] = `null`;
+
+exports[`PureOptionDefault Component should handle array 2`] = `
+<div
+ className="small"
+>
+ Default:
+ <strong>
+
+ [ ]
+
+ </strong>
+
+</div>
+`;
+
+exports[`PureOptionDefault Component should handle boolean type 1`] = `
+<div
+ className="small"
+>
+ Default:
+ <strong>
+
+ false
+
+ </strong>
+
+</div>
+`;
+
+exports[`PureOptionDefault Component should handle null value 1`] = `
+<div
+ className="small"
+>
+ Default:
+ <strong>
+
+ null
+
+ </strong>
+
+</div>
+`;
+
+exports[`PureOptionDefault Component should handle string 1`] = `
+<div
+ className="small"
+>
+ Default:
+ <strong>
+
+ ""
+
+ </strong>
+
+</div>
+`;
+
+exports[`PureOptionDefault Component should return null when the value is default 1`] = `null`;
diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap
index 514e0eb5..257bddce 100644
--- a/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap
+++ b/web/src/js/__tests__/components/Modal/__snapshots__/OptionSpec.js.snap
@@ -18,7 +18,7 @@ exports[`BooleanOption Component should render correctly 1`] = `
exports[`ChoiceOption Component should render correctly 1`] = `
<select
onChange={[Function]}
- selected="a"
+ value="a"
>
<option
value="a"
diff --git a/web/src/js/__tests__/ducks/optionsSpec.js b/web/src/js/__tests__/ducks/optionsSpec.js
index 0925fcc1..9178c14e 100644
--- a/web/src/js/__tests__/ducks/optionsSpec.js
+++ b/web/src/js/__tests__/ducks/optionsSpec.js
@@ -49,3 +49,18 @@ describe('sendUpdate', () => {
])
})
})
+
+describe('save', () => {
+
+ it('should dump options', () => {
+ global.fetch = jest.fn()
+ store.dispatch(OptionsActions.save())
+ expect(fetch).toBeCalledWith(
+ '/options/save?_xsrf=undefined',
+ {
+ credentials: "same-origin",
+ method: "POST"
+ }
+ )
+ })
+})
diff --git a/web/src/js/components/Modal/Option.jsx b/web/src/js/components/Modal/Option.jsx
index 58b863d1..38e2f239 100644
--- a/web/src/js/components/Modal/Option.jsx
+++ b/web/src/js/components/Modal/Option.jsx
@@ -74,7 +74,7 @@ export function ChoicesOption({ value, onChange, choices, ...props }) {
return (
<select
onChange={(e) => onChange(e.target.value)}
- selected={value}
+ value={value}
{...props}
>
{ choices.map(
diff --git a/web/src/js/components/Modal/OptionModal.jsx b/web/src/js/components/Modal/OptionModal.jsx
index 5741ee8c..82ef8350 100644
--- a/web/src/js/components/Modal/OptionModal.jsx
+++ b/web/src/js/components/Modal/OptionModal.jsx
@@ -1,7 +1,9 @@
import React, { Component } from "react"
import { connect } from "react-redux"
import * as modalAction from "../../ducks/ui/modal"
+import * as optionAction from "../../ducks/options"
import Option from "./Option"
+import _ from "lodash"
function PureOptionHelp({help}){
return <div className="help-block small">{help}</div>;
@@ -18,6 +20,31 @@ const OptionError = connect((state, {name}) => ({
error: state.ui.optionsEditor[name] && state.ui.optionsEditor[name].error
}))(PureOptionError);
+export function PureOptionDefault({value, defaultVal}){
+ if( value === defaultVal ) {
+ return null
+ } else {
+ if (typeof(defaultVal) === 'boolean') {
+ defaultVal = defaultVal ? 'true' : 'false'
+ } else if (Array.isArray(defaultVal)){
+ if (_.isEmpty(_.compact(value)) && // filter the empty string in array
+ _.isEmpty(defaultVal)){
+ return null
+ }
+ defaultVal = '[ ]'
+ } else if (defaultVal === ''){
+ defaultVal = '\"\"'
+ } else if (defaultVal === null){
+ defaultVal = 'null'
+ }
+ return <div className="small">Default: <strong> {defaultVal} </strong> </div>
+ }
+}
+const OptionDefault = connect((state, {name}) => ({
+ value: state.options[name].value,
+ defaultVal: state.options[name].default
+}))(PureOptionDefault)
+
class PureOptionModal extends Component {
constructor(props, context) {
@@ -25,6 +52,10 @@ class PureOptionModal extends Component {
this.state = { title: 'Options' }
}
+ componentWillUnmount(){
+ this.props.save()
+ }
+
render() {
const { hideModal, options } = this.props
const { title } = this.state
@@ -53,6 +84,7 @@ class PureOptionModal extends Component {
<div className="col-xs-6">
<Option name={name}/>
<OptionError name={name}/>
+ <OptionDefault name={name}/>
</div>
</div>
)
@@ -73,5 +105,6 @@ export default connect(
}),
{
hideModal: modalAction.hideModal,
+ save: optionAction.save,
}
)(PureOptionModal)
diff --git a/web/src/js/ducks/options.js b/web/src/js/ducks/options.js
index 06144a3c..0da0fb8c 100644
--- a/web/src/js/ducks/options.js
+++ b/web/src/js/ducks/options.js
@@ -44,3 +44,7 @@ export function update(option, value) {
sendUpdate(option, value, dispatch);
}
}
+
+export function save() {
+ return dispatch => fetchApi('/options/save', { method: 'POST' })
+}