aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-09-17 17:30:19 +0200
committerMaximilian Hils <git@maximilianhils.com>2014-09-17 17:30:19 +0200
commit102bd075689892b06765fb857c89604fe9cf33e5 (patch)
tree6782d4ee795337204d29a873e7854d6e426574df /web/src/js
parent8245dd19f4f2f4cdd74a6fdf9b5e051c2cd2fac6 (diff)
downloadmitmproxy-102bd075689892b06765fb857c89604fe9cf33e5.tar.gz
mitmproxy-102bd075689892b06765fb857c89604fe9cf33e5.tar.bz2
mitmproxy-102bd075689892b06765fb857c89604fe9cf33e5.zip
implement FlowStore basics
Diffstat (limited to 'web/src/js')
-rw-r--r--web/src/js/actions.js22
-rw-r--r--web/src/js/components/eventlog.jsx11
-rw-r--r--web/src/js/components/flowtable.jsx121
-rw-r--r--web/src/js/components/proxyapp.jsx2
-rw-r--r--web/src/js/components/traffictable.jsx34
-rw-r--r--web/src/js/connection.js2
-rw-r--r--web/src/js/stores/flowstore.js74
7 files changed, 228 insertions, 38 deletions
diff --git a/web/src/js/actions.js b/web/src/js/actions.js
index e55a1d16..43fbfb14 100644
--- a/web/src/js/actions.js
+++ b/web/src/js/actions.js
@@ -1,6 +1,13 @@
var ActionTypes = {
+ //Settings
UPDATE_SETTINGS: "update_settings",
- ADD_EVENT: "add_event"
+
+ //EventLog
+ ADD_EVENT: "add_event",
+
+ //Flow
+ ADD_FLOW: "add_flow",
+ UPDATE_FLOW: "update_flow",
};
var SettingsActions = {
@@ -15,3 +22,16 @@ var SettingsActions = {
});
}
};
+
+var EventLogActions = {
+ add_event: function(message, level){
+ AppDispatcher.dispatchViewAction({
+ type: ActionTypes.ADD_EVENT,
+ data: {
+ message: message,
+ level: level || "info",
+ source: "ui"
+ }
+ });
+ }
+}; \ No newline at end of file
diff --git a/web/src/js/components/eventlog.jsx b/web/src/js/components/eventlog.jsx
index 32fc01ee..df212177 100644
--- a/web/src/js/components/eventlog.jsx
+++ b/web/src/js/components/eventlog.jsx
@@ -27,8 +27,15 @@ var EventLog = React.createClass({
},
render: function () {
var messages = this.state.log.map(function(row) {
- return (<div key={row.id}>{row.message}</div>);
+ var indicator = null;
+ if(row.source === "ui"){
+ indicator = <i className="fa fa-html5"></i>;
+ }
+ return (
+ <div key={row.id}>
+ { indicator } {row.message}
+ </div>);
});
return <pre className="eventlog">{messages}</pre>;
}
-});
+}); \ No newline at end of file
diff --git a/web/src/js/components/flowtable.jsx b/web/src/js/components/flowtable.jsx
new file mode 100644
index 00000000..5e9f6718
--- /dev/null
+++ b/web/src/js/components/flowtable.jsx
@@ -0,0 +1,121 @@
+/** @jsx React.DOM */
+
+var FlowRow = React.createClass({
+ render: function(){
+ var flow = this.props.flow;
+ var columns = this.props.columns.map(function(column){
+ return column({flow: flow});
+ }.bind(this));
+ return <tr>{columns}</tr>;
+ }
+});
+
+var FlowTableHead = React.createClass({
+ render: function(){
+ var columns = this.props.columns.map(function(column){
+ return column.renderTitle();
+ }.bind(this));
+ return <thead>{columns}</thead>;
+ }
+});
+
+var FlowTableBody = React.createClass({
+ render: function(){
+ var rows = this.props.flows.map(function(flow){
+ return <FlowRow flow={flow} columns={this.props.columns}/>
+ }.bind(this));
+ return <tbody>{rows}</tbody>;
+ }
+});
+
+var PathColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="PathColumn">Path</th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ return <td key="PathColumn">{flow.request.scheme + "://" + flow.request.host + flow.request.path}</td>;
+ }
+});
+var MethodColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="MethodColumn">Method</th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ return <td key="MethodColumn">{flow.request.method}</td>;
+ }
+});
+var StatusColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="StatusColumn">Status</th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ var status;
+ if(flow.response){
+ status = flow.response.code + " " + flow.response.msg;
+ } else {
+ status = null;
+ }
+ return <td key="StatusColumn">{status}</td>;
+ }
+});
+var TimeColumn = React.createClass({
+ statics: {
+ renderTitle: function(){
+ return <th key="TimeColumn">Time</th>;
+ }
+ },
+ render: function(){
+ var flow = this.props.flow;
+ var time;
+ if(flow.response){
+ time = Math.round(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))+"ms";
+ } else {
+ time = "...";
+ }
+ return <td key="TimeColumn">{time}</td>;
+ }
+});
+
+var all_columns = [PathColumn, MethodColumn, StatusColumn, TimeColumn];
+
+var FlowTable = React.createClass({
+ getInitialState: function () {
+ return {
+ flows: [],
+ columns: all_columns
+ };
+ },
+ componentDidMount: function () {
+ this.flowStore = FlowStore.getView();
+ this.flowStore.addListener("change",this.onFlowChange);
+ },
+ componentWillUnmount: function () {
+ this.flowStore.removeListener("change",this.onFlowChange);
+ this.flowStore.close();
+ },
+ onFlowChange: function () {
+ this.setState({
+ flows: this.flowStore.getAll()
+ });
+ },
+ render: function () {
+ var flows = this.state.flows.map(function(flow){
+ return <div>{flow.request.method} {flow.request.scheme}://{flow.request.host}{flow.request.path}</div>;
+ });
+ return (
+ <table className="flow-table">
+ <FlowTableHead columns={this.state.columns}/>
+ <FlowTableBody columns={this.state.columns} flows={this.state.flows}/>
+ </table>
+ );
+ }
+});
diff --git a/web/src/js/components/proxyapp.jsx b/web/src/js/components/proxyapp.jsx
index 2f1a9861..63998ffe 100644
--- a/web/src/js/components/proxyapp.jsx
+++ b/web/src/js/components/proxyapp.jsx
@@ -38,7 +38,7 @@ var ProxyAppMain = React.createClass({
var ProxyApp = (
<ReactRouter.Routes location="hash">
<ReactRouter.Route name="app" path="/" handler={ProxyAppMain}>
- <ReactRouter.Route name="main" handler={TrafficTable}/>
+ <ReactRouter.Route name="main" handler={FlowTable}/>
<ReactRouter.Route name="reports" handler={Reports}/>
<ReactRouter.Redirect to="main"/>
</ReactRouter.Route>
diff --git a/web/src/js/components/traffictable.jsx b/web/src/js/components/traffictable.jsx
deleted file mode 100644
index 8071b97e..00000000
--- a/web/src/js/components/traffictable.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-/** @jsx React.DOM */
-
-var TrafficTable = React.createClass({
- getInitialState: function () {
- return {
- flows: []
- };
- },
- componentDidMount: function () {
- //this.flowStore = FlowStore.getView();
- //this.flowStore.addListener("change",this.onFlowChange);
- },
- componentWillUnmount: function () {
- //this.flowStore.removeListener("change",this.onFlowChange);
- //this.flowStore.close();
- },
- onFlowChange: function () {
- this.setState({
- //flows: this.flowStore.getAll()
- });
- },
- render: function () {
- /*var flows = this.state.flows.map(function(flow){
- return <div>{flow.request.method} {flow.request.scheme}://{flow.request.host}{flow.request.path}</div>;
- }); */
- //Dummy Text for layout testing
- x = "Flow";
- i = 12;
- while (i--) x += x;
- return (
- <div>Flow</div>
- );
- }
-});
diff --git a/web/src/js/connection.js b/web/src/js/connection.js
index 5d464e52..640e9742 100644
--- a/web/src/js/connection.js
+++ b/web/src/js/connection.js
@@ -22,9 +22,11 @@ _Connection.prototype.onmessage = function (message) {
AppDispatcher.dispatchServerAction(m);
};
_Connection.prototype.onerror = function (error) {
+ EventLogActions.add_event("WebSocket Connection Error.");
console.log("onerror", this, arguments);
};
_Connection.prototype.onclose = function (close) {
+ EventLogActions.add_event("WebSocket Connection closed.");
console.log("onclose", this, arguments);
};
diff --git a/web/src/js/stores/flowstore.js b/web/src/js/stores/flowstore.js
new file mode 100644
index 00000000..006eeb24
--- /dev/null
+++ b/web/src/js/stores/flowstore.js
@@ -0,0 +1,74 @@
+function FlowView(store, live) {
+ EventEmitter.call(this);
+ this._store = store;
+ this.live = live;
+ this.flows = [];
+
+ this.add = this.add.bind(this);
+ this.update = this.update.bind(this);
+
+ if (live) {
+ this._store.addListener(ActionTypes.ADD_FLOW, this.add);
+ this._store.addListener(ActionTypes.UPDATE_FLOW, this.update);
+ }
+}
+
+_.extend(FlowView.prototype, EventEmitter.prototype, {
+ close: function () {
+ this._store.removeListener(ActionTypes.ADD_FLOW, this.add);
+ this._store.removeListener(ActionTypes.UPDATE_FLOW, this.update);
+ },
+ getAll: function () {
+ return this.flows;
+ },
+ add: function (flow) {
+ return this.update(flow);
+ },
+ add_bulk: function (flows) {
+ //Treat all previously received updates as newer than the bulk update.
+ //If they weren't newer, we're about to receive an update for them very soon.
+ var updates = this.flows;
+ this.flows = flows;
+ updates.forEach(function(flow){
+ this.update(flow);
+ }.bind(this));
+ },
+ update: function(flow){
+ console.debug("FIXME: Use UUID");
+ var idx = _.findIndex(this.flows, function(f){
+ return flow.request.timestamp_start == f.request.timestamp_start
+ });
+
+ if(idx < 0){
+ this.flows.push(flow);
+ } else {
+ this.flows[idx] = flow;
+ }
+ this.emit("change");
+ },
+});
+
+
+function _FlowStore() {
+ EventEmitter.call(this);
+}
+_.extend(_FlowStore.prototype, EventEmitter.prototype, {
+ getView: function (since) {
+ var view = new FlowView(this, !since);
+ return view;
+ },
+ handle: function (action) {
+ switch (action.type) {
+ case ActionTypes.ADD_FLOW:
+ case ActionTypes.UPDATE_FLOW:
+ this.emit(action.type, action.data);
+ break;
+ default:
+ return;
+ }
+ }
+});
+
+
+var FlowStore = new _FlowStore();
+AppDispatcher.register(FlowStore.handle.bind(FlowStore));