aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/tools/web/app.py12
-rw-r--r--mitmproxy/tools/web/master.py1
-rw-r--r--test/mitmproxy/tools/web/test_app.py4
-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
11 files changed, 217 insertions, 6 deletions
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/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/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' })
+}