aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorJason <jason.daurus@gmail.com>2016-06-18 14:18:24 +0800
committerJason <jason.daurus@gmail.com>2016-06-18 14:18:24 +0800
commit9cb5b0af9db83d84af0bdb45d56a9755b400c212 (patch)
treea2f2c6985f8a2230be3b8796567e678733e03b7f /web
parent6c0511b06fdc359ec4c48879f803c80d7fbeb34c (diff)
parent23b976a999f41439e83c1010474ec9dc680b8486 (diff)
downloadmitmproxy-9cb5b0af9db83d84af0bdb45d56a9755b400c212.tar.gz
mitmproxy-9cb5b0af9db83d84af0bdb45d56a9755b400c212.tar.bz2
mitmproxy-9cb5b0af9db83d84af0bdb45d56a9755b400c212.zip
Merge branch 'master' of github.com:mitmproxy/mitmproxy into websocket
Conflicts: mitmproxy/web/static/app.js web/src/js/components/ProxyApp.jsx web/src/js/connection.js
Diffstat (limited to 'web')
-rw-r--r--web/package.json4
-rw-r--r--web/src/js/components/Header/MainMenu.jsx11
-rw-r--r--web/src/js/components/ProxyApp.jsx77
-rw-r--r--web/src/js/ducks/index.js2
-rw-r--r--web/src/js/ducks/settings.js77
-rw-r--r--web/src/js/ducks/websocket.js29
-rw-r--r--web/src/js/store/store.js96
-rw-r--r--web/src/js/store/view.js0
-rw-r--r--web/src/js/utils.js18
9 files changed, 148 insertions, 166 deletions
diff --git a/web/package.json b/web/package.json
index 54555cb1..7ace808a 100644
--- a/web/package.json
+++ b/web/package.json
@@ -2,7 +2,9 @@
"name": "mitmproxy",
"private": true,
"scripts": {
- "test": "jest"
+ "test": "jest",
+ "build": "gulp prod",
+ "start": "gulp"
},
"jest": {
"testPathDirs": [
diff --git a/web/src/js/components/Header/MainMenu.jsx b/web/src/js/components/Header/MainMenu.jsx
index 86bf961a..7b0b542c 100644
--- a/web/src/js/components/Header/MainMenu.jsx
+++ b/web/src/js/components/Header/MainMenu.jsx
@@ -1,9 +1,10 @@
import React, { Component, PropTypes } from 'react'
-import { SettingsActions } from "../../actions.js"
import FilterInput from './FilterInput'
import { Query } from '../../actions.js'
+import {setInterceptPattern} from "../../ducks/settings"
+import { connect } from 'react-redux'
-export default class MainMenu extends Component {
+class MainMenu extends Component {
static title = 'Start'
static route = 'flows'
@@ -28,7 +29,7 @@ export default class MainMenu extends Component {
}
onInterceptChange(val) {
- SettingsActions.update({ intercept: val })
+ this.props.setInterceptPattern(val);
}
render() {
@@ -71,3 +72,7 @@ export default class MainMenu extends Component {
)
}
}
+
+export default connect(undefined, {
+ setInterceptPattern
+})(MainMenu);
diff --git a/web/src/js/components/ProxyApp.jsx b/web/src/js/components/ProxyApp.jsx
index 11c321e7..89bd95fd 100644
--- a/web/src/js/components/ProxyApp.jsx
+++ b/web/src/js/components/ProxyApp.jsx
@@ -7,7 +7,6 @@ import { init as appInit, destruct as appDestruct } from '../ducks/app'
import Header from './Header'
import EventLog from './EventLog'
import Footer from './Footer'
-import { SettingsStore } from '../store/store.js'
import { Key } from '../utils.js'
class ProxyAppMain extends Component {
@@ -23,17 +22,9 @@ class ProxyAppMain extends Component {
constructor(props, context) {
super(props, context)
- this.settingsStore = new SettingsStore()
-
- // Default Settings before fetch
- _.extend(this.settingsStore.dict, {})
-
- this.state = { settings: this.settingsStore.dict }
-
this.focus = this.focus.bind(this)
this.onKeyDown = this.onKeyDown.bind(this)
this.updateLocation = this.updateLocation.bind(this)
- this.onSettingsChange = this.onSettingsChange.bind(this)
}
componentWillMount() {
@@ -41,53 +32,14 @@ class ProxyAppMain extends Component {
}
/**
- * @todo move to actions
- */
- updateLocation(pathname, queryUpdate) {
- if (pathname === undefined) {
- pathname = this.props.location.pathname
- }
- const query = this.props.location.query
- for (const key of Object.keys(queryUpdate || {})) {
- query[key] = queryUpdate[key] || undefined
- }
- this.context.router.replace({ pathname, query })
- }
-
- /**
- * @todo pass in with props
- */
- getQuery() {
- // For whatever reason, react-router always returns the same object, which makes comparing
- // the current props with nextProps impossible. As a workaround, we just clone the query object.
- return _.clone(this.props.location.query)
- }
-
- /**
- * @todo remove settings store
- * @todo connect websocket here
* @todo listen to window's key events
*/
componentDidMount() {
this.focus()
- this.settingsStore.addListener('recalculate', this.onSettingsChange)
}
- /**
- * @todo remove settings store
- * @todo disconnect websocket here
- * @todo stop listening to window's key events
- */
componentWillUnmount() {
this.props.appDestruct()
- this.settingsStore.removeListener('recalculate', this.onSettingsChange)
- }
-
- /**
- * @todo move to actions
- */
- onSettingsChange() {
- this.setState({ settings: this.settingsStore.dict })
}
/**
@@ -144,9 +96,31 @@ class ProxyAppMain extends Component {
e.preventDefault()
}
+ /**
+ * @todo move to actions
+ */
+ updateLocation(pathname, queryUpdate) {
+ if (pathname === undefined) {
+ pathname = this.props.location.pathname
+ }
+ const query = this.props.location.query
+ for (const key of Object.keys(queryUpdate || {})) {
+ query[key] = queryUpdate[key] || undefined
+ }
+ this.context.router.replace({ pathname, query })
+ }
+
+ /**
+ * @todo pass in with props
+ */
+ getQuery() {
+ // For whatever reason, react-router always returns the same object, which makes comparing
+ // the current props with nextProps impossible. As a workaround, we just clone the query object.
+ return _.clone(this.props.location.query)
+ }
+
render() {
- const { showEventLog, location, children } = this.props
- const { settings } = this.state
+ const { showEventLog, location, children, settings } = this.props
const query = this.getQuery()
return (
<div id="container" tabIndex="0" onKeyDown={this.onKeyDown}>
@@ -166,7 +140,8 @@ class ProxyAppMain extends Component {
export default connect(
state => ({
- showEventLog: state.eventLog.visible
+ showEventLog: state.eventLog.visible,
+ settings: state.settings.settings,
}),
{
appInit,
diff --git a/web/src/js/ducks/index.js b/web/src/js/ducks/index.js
index fee4d792..ffde1a64 100644
--- a/web/src/js/ducks/index.js
+++ b/web/src/js/ducks/index.js
@@ -2,12 +2,14 @@ import {combineReducers} from 'redux'
import eventLog from './eventLog'
import websocket from './websocket'
import flows from './flows'
+import settings from './settings'
import ui from './ui'
const rootReducer = combineReducers({
eventLog,
websocket,
flows,
+ settings,
ui
})
diff --git a/web/src/js/ducks/settings.js b/web/src/js/ducks/settings.js
new file mode 100644
index 00000000..73c62120
--- /dev/null
+++ b/web/src/js/ducks/settings.js
@@ -0,0 +1,77 @@
+import {fetchApi} from "../utils";
+
+export const REQUEST_SETTINGS = "REQUEST_SETTINGS"
+export const RECEIVE_SETTINGS = "RECEIVE_SETTINGS"
+export const UPDATE_SETTINGS = "UPDATE_SETTINGS"
+
+const defaultState = {
+ settings: {},
+ isFetching: false,
+ actionsDuringFetch: [],
+}
+
+export default function reducer(state = defaultState, action) {
+ switch (action.type) {
+
+ case REQUEST_SETTINGS:
+ return {
+ ...state,
+ isFetching: true
+ }
+
+ case RECEIVE_SETTINGS:
+ let s = {
+ settings: action.settings,
+ isFetching: false,
+ actionsDuringFetch: [],
+ }
+ for (action of state.actionsDuringFetch) {
+ s = reducer(s, action)
+ }
+ return s
+
+ case UPDATE_SETTINGS:
+ if (state.isFetching) {
+ return {
+ ...state,
+ actionsDuringFetch: [...state.actionsDuringFetch, action]
+ }
+ }
+ return {
+ ...state,
+ settings: {...state.settings, ...action.settings}
+ }
+
+ default:
+ return state
+ }
+}
+
+export function updateSettings(event) {
+ /* This action creator takes all WebSocket events */
+ if (event.cmd === "update") {
+ return {
+ type: UPDATE_SETTINGS,
+ settings: event.data
+ }
+ }
+ console.error("unknown settings update", event)
+}
+
+export function fetchSettings() {
+ return dispatch => {
+ dispatch({type: REQUEST_SETTINGS})
+
+ return fetchApi("/settings")
+ .then(response => response.json())
+ .then(json =>
+ dispatch({type: RECEIVE_SETTINGS, settings: json.data})
+ )
+ // TODO: Error handling
+ }
+}
+
+export function setInterceptPattern(intercept) {
+ return dispatch =>
+ fetchApi.put("/settings", {intercept})
+}
diff --git a/web/src/js/ducks/websocket.js b/web/src/js/ducks/websocket.js
index 0d0556ad..766f7236 100644
--- a/web/src/js/ducks/websocket.js
+++ b/web/src/js/ducks/websocket.js
@@ -1,7 +1,8 @@
import { ConnectionActions } from '../actions.js'
import { AppDispatcher } from '../dispatcher.js'
import * as eventLogActions from './eventLog'
-import * as flowActions from './flows'
+import * as flowsActions from './flows'
+import * as settingsActions from './settings'
export const CONNECT = 'WEBSOCKET_CONNECT'
export const CONNECTED = 'WEBSOCKET_CONNECTED'
@@ -62,15 +63,8 @@ export function onConnect() {
// workaround to make sure that our state is already available.
return dispatch => {
dispatch({ type: CONNECTED })
- dispatch(flowActions.fetchFlows()).then(() => ConnectionActions.open())
- }
-}
-
-export function onDisconnect() {
- return dispatch => {
- ConnectionActions.close()
- dispatch(eventLogActions.addLogEntry('WebSocket connection closed.'))
- dispatch({ type: DISCONNECTED })
+ dispatch(settingsActions.fetchSettings())
+ dispatch(flowsActions.fetchFlows()).then(() => ConnectionActions.open())
}
}
@@ -85,8 +79,11 @@ export function onMessage(msg) {
case eventLogActions.UPDATE_LOG:
return dispatch(eventLogActions.updateLogEntries(data))
- case flowActions.UPDATE_FLOWS:
- return dispatch(flowActions.updateFlows(data))
+ case flowsActions.UPDATE_FLOWS:
+ return dispatch(flowsActions.updateFlows(data))
+
+ case settingsActions.UPDATE_SETTINGS:
+ return dispatch(settingsActions.updateSettings(message))
default:
console.warn('unknown message', data)
@@ -96,6 +93,14 @@ export function onMessage(msg) {
}
}
+export function onDisconnect() {
+ return dispatch => {
+ ConnectionActions.close()
+ dispatch(eventLogActions.addLogEntry('WebSocket connection closed.'))
+ dispatch({ type: DISCONNECTED })
+ }
+}
+
export function onError(error) {
// @todo let event log subscribe WebSocketActions.ERROR
return dispatch => {
diff --git a/web/src/js/store/store.js b/web/src/js/store/store.js
deleted file mode 100644
index f3e2074f..00000000
--- a/web/src/js/store/store.js
+++ /dev/null
@@ -1,96 +0,0 @@
-
-import _ from "lodash";
-import $ from "jquery";
-import {EventEmitter} from 'events';
-import {ActionTypes, StoreCmds} from "../actions.js";
-import {AppDispatcher} from "../dispatcher.js";
-
-
-function DictStore() {
- EventEmitter.call(this);
- this.reset();
-}
-_.extend(DictStore.prototype, EventEmitter.prototype, {
- update: function (dict) {
- _.merge(this.dict, dict);
- this.emit("recalculate");
- },
- reset: function (dict) {
- this.dict = dict || {};
- this.emit("recalculate");
- }
-});
-
-function LiveStoreMixin(type) {
- this.type = type;
-
- this._updates_before_fetch = undefined;
- this._fetchxhr = false;
-
- this.handle = this.handle.bind(this);
- AppDispatcher.register(this.handle);
-
- // Avoid double-fetch on startup.
- if (!(window.ws && window.ws.readyState === WebSocket.CONNECTING)) {
- this.fetch();
- }
-}
-_.extend(LiveStoreMixin.prototype, {
- handle: function (event) {
- if (event.type === ActionTypes.CONNECTION_OPEN) {
- return this.fetch();
- }
- if (event.type === this.type) {
- if (event.cmd === StoreCmds.RESET) {
- this.fetch(event.data);
- } else if (this._updates_before_fetch) {
- console.log("defer update", event);
- this._updates_before_fetch.push(event);
- } else {
- this[event.cmd](event.data);
- }
- }
- },
- close: function () {
- AppDispatcher.unregister(this.handle);
- },
- fetch: function (data) {
- console.log("fetch " + this.type);
- if (this._fetchxhr) {
- this._fetchxhr.abort();
- }
- this._updates_before_fetch = []; // (JS: empty array is true)
- if (data) {
- this.handle_fetch(data);
- } else {
- this._fetchxhr = $.getJSON("/" + this.type)
- .done(function (message) {
- this.handle_fetch(message.data);
- }.bind(this))
- .fail(function () {
- console.error("Could not fetch " + this.type)
- }.bind(this));
- }
- },
- handle_fetch: function (data) {
- this._fetchxhr = false;
- console.log(this.type + " fetched.", this._updates_before_fetch);
- this.reset(data);
- var updates = this._updates_before_fetch;
- this._updates_before_fetch = false;
- for (var i = 0; i < updates.length; i++) {
- this.handle(updates[i]);
- }
- },
-});
-
-function LiveDictStore(type) {
- DictStore.call(this);
- LiveStoreMixin.call(this, type);
-}
-_.extend(LiveDictStore.prototype, DictStore.prototype, LiveStoreMixin.prototype);
-
-
-export function SettingsStore() {
- return new LiveDictStore(ActionTypes.SETTINGS_STORE);
-} \ No newline at end of file
diff --git a/web/src/js/store/view.js b/web/src/js/store/view.js
deleted file mode 100644
index e69de29b..00000000
--- a/web/src/js/store/view.js
+++ /dev/null
diff --git a/web/src/js/utils.js b/web/src/js/utils.js
index 2e5c3005..2e25016e 100644
--- a/web/src/js/utils.js
+++ b/web/src/js/utils.js
@@ -109,7 +109,19 @@ export function fetchApi(url, options) {
url += "&" + xsrf;
}
return fetch(url, {
- ...options,
- credentials: 'same-origin'
+ credentials: 'same-origin',
+ ...options
});
-} \ No newline at end of file
+}
+
+fetchApi.put = (url, json, options) => fetchApi(
+ url,
+ {
+ method: "PUT",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(json),
+ ...options
+ }
+)