aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-12-23 20:33:42 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-12-23 20:33:42 +0100
commit2861d99de4d329bcba0a3c2193523398a22673c0 (patch)
tree0aa1a2aae666c2909285a6fd5049f2ef7d6d8ebc /web
parent459772a8ef79ac0adeaeba56577972e86074265e (diff)
downloadmitmproxy-2861d99de4d329bcba0a3c2193523398a22673c0.tar.gz
mitmproxy-2861d99de4d329bcba0a3c2193523398a22673c0.tar.bz2
mitmproxy-2861d99de4d329bcba0a3c2193523398a22673c0.zip
web: intercept feature
Diffstat (limited to 'web')
-rw-r--r--web/gulpfile.js1
-rw-r--r--web/src/css/flowtable.less11
-rw-r--r--web/src/js/actions.js6
-rw-r--r--web/src/js/components/flowtable.jsx.js9
-rw-r--r--web/src/js/components/footer.jsx.js3
-rw-r--r--web/src/js/components/header.jsx.js91
-rw-r--r--web/src/js/components/mainview.jsx.js11
-rw-r--r--web/src/js/components/utils.jsx.js2
-rw-r--r--web/src/js/store/settingstore.js0
-rw-r--r--web/src/js/utils.js8
10 files changed, 91 insertions, 51 deletions
diff --git a/web/gulpfile.js b/web/gulpfile.js
index 8dc888e7..5a0b93af 100644
--- a/web/gulpfile.js
+++ b/web/gulpfile.js
@@ -40,7 +40,6 @@ var path = {
'js/flow/utils.js',
'js/store/store.js',
'js/store/view.js',
- 'js/store/settingstore.js',
'js/connection.js',
'js/components/utils.jsx.js',
'js/components/virtualscroll.jsx.js',
diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less
index 2b0e8df3..b18a71fa 100644
--- a/web/src/css/flowtable.less
+++ b/web/src/css/flowtable.less
@@ -40,7 +40,16 @@
text-overflow: ellipsis;
}
- tr
+ tr.intercepted:not(.has-response) {
+ .col-path, .col-method {
+ color: hsl(30, 100%, 50%);
+ }
+ }
+ tr.intercepted.has-response {
+ .col-status, .col-size, .col-time {
+ color: hsl(30, 100%, 50%);
+ }
+ }
.col-tls {
width: 10px;
diff --git a/web/src/js/actions.js b/web/src/js/actions.js
index 863663f3..e7799118 100644
--- a/web/src/js/actions.js
+++ b/web/src/js/actions.js
@@ -38,7 +38,11 @@ var ConnectionActions = {
var SettingsActions = {
update: function (settings) {
- //TODO: Update server.
+ jQuery.ajax({
+ type: "PUT",
+ url: "/settings",
+ data: settings
+ });
//Facebook Flux: We do an optimistic update on the client already.
AppDispatcher.dispatchViewAction({
diff --git a/web/src/js/components/flowtable.jsx.js b/web/src/js/components/flowtable.jsx.js
index efc975a6..a3a37c40 100644
--- a/web/src/js/components/flowtable.jsx.js
+++ b/web/src/js/components/flowtable.jsx.js
@@ -11,6 +11,15 @@ var FlowRow = React.createClass({
if (this.props.highlighted) {
className += " highlighted";
}
+ if (flow.intercepted) {
+ className += " intercepted";
+ }
+ if (flow.request) {
+ className += " has-request";
+ }
+ if (flow.response) {
+ className += " has-response";
+ }
return (
<tr className={className} onClick={this.props.selectFlow.bind(null, flow)}>
diff --git a/web/src/js/components/footer.jsx.js b/web/src/js/components/footer.jsx.js
index 73fadef2..52d52e0f 100644
--- a/web/src/js/components/footer.jsx.js
+++ b/web/src/js/components/footer.jsx.js
@@ -1,9 +1,12 @@
var Footer = React.createClass({
render: function () {
var mode = this.props.settings.mode;
+ var intercept = this.props.settings.intercept;
return (
<footer>
{mode != "regular" ? <span className="label label-success">{mode} mode</span> : null}
+ &nbsp;
+ {intercept ? <span className="label label-success">Intercept: {intercept}</span> : null}
</footer>
);
}
diff --git a/web/src/js/components/header.jsx.js b/web/src/js/components/header.jsx.js
index 9e090770..cb9cd149 100644
--- a/web/src/js/components/header.jsx.js
+++ b/web/src/js/components/header.jsx.js
@@ -1,9 +1,8 @@
var FilterInput = React.createClass({
getInitialState: function () {
- // Focus: Show popover
- // Mousefocus: Mouse over Tooltip
- // onBlur is triggered before click on tooltip,
- // hiding the tooltip before link is clicked.
+ // Consider both focus and mouseover for showing/hiding the tooltip,
+ // because onBlur of the input is triggered before the click on the tooltip
+ // finalized, hiding the tooltip just as the user clicks on it.
return {
value: this.props.value,
focus: false,
@@ -18,16 +17,14 @@ var FilterInput = React.createClass({
this.setState({
value: nextValue
});
- try {
- Filt.parse(nextValue);
- } catch (err) {
- return;
+ // Only propagate valid filters upwards.
+ if (this.isValid(nextValue)) {
+ this.props.onChange(nextValue);
}
- this.props.onChange(nextValue);
},
- isValid: function () {
+ isValid: function (filt) {
try {
- Filt.parse(this.state.value);
+ Filt.parse(filt || this.state.value);
return true;
} catch (e) {
return false;
@@ -64,16 +61,14 @@ var FilterInput = React.createClass({
this.setState({mousefocus: false});
},
onKeyDown: function (e) {
- if (e.target.value === "" &&
- e.keyCode === Key.BACKSPACE) {
- e.preventDefault();
- this.remove();
+ if (e.keyCode === Key.ESC || e.keyCode === Key.ENTER) {
+ this.blur();
+ // If closed using ESC/ENTER, hide the tooltip.
+ this.setState({mousefocus: false});
}
},
- remove: function () {
- if (this.props.onRemove) {
- this.props.onRemove();
- }
+ blur: function () {
+ this.refs.input.getDOMNode().blur();
},
focus: function () {
this.refs.input.getDOMNode().select();
@@ -100,7 +95,7 @@ var FilterInput = React.createClass({
<span className="input-group-addon">
<i className={icon} style={{color: this.props.color}}></i>
</span>
- <input type="text" placeholder="filter expression" className="form-control"
+ <input type="text" placeholder={this.props.placeholder} className="form-control"
ref="input"
onChange={this.onChange}
onFocus={this.onFocus}
@@ -115,12 +110,6 @@ var FilterInput = React.createClass({
var MainMenu = React.createClass({
mixins: [Navigation, State],
- getInitialState: function () {
- return {
- filter: this.getQuery()[Query.FILTER] || "",
- highlight: this.getQuery()[Query.HIGHLIGHT] || ""
- };
- },
statics: {
title: "Traffic",
route: "flows"
@@ -131,39 +120,59 @@ var MainMenu = React.createClass({
});
},
clearFlows: function () {
- $.post("/clear");
+ jQuery.post("/clear");
},
- applyFilter: function (filter, highlight) {
+ onFilterChange: function (val) {
var d = {};
- d[Query.FILTER] = filter;
- d[Query.HIGHLIGHT] = highlight;
+ d[Query.FILTER] = val;
this.setQuery(d);
},
- onFilterChange: function (val) {
- this.setState({filter: val});
- this.applyFilter(val, this.state.highlight);
- },
onHighlightChange: function (val) {
- this.setState({highlight: val});
- this.applyFilter(this.state.filter, val);
+ var d = {};
+ d[Query.HIGHLIGHT] = val;
+ this.setQuery(d);
+ },
+ onInterceptChange: function (val) {
+ SettingsActions.update({intercept: val});
},
render: function () {
+ var filter = this.getQuery()[Query.FILTER] || "";
+ var highlight = this.getQuery()[Query.HIGHLIGHT] || "";
+ var intercept = this.props.settings.intercept || "";
+
return (
<div>
<button className={"btn " + (this.props.settings.showEventLog ? "btn-primary" : "btn-default")} onClick={this.toggleEventLog}>
<i className="fa fa-database"></i>
&nbsp;Display Event Log
</button>
- &nbsp;
+ <span> </span>
<button className="btn btn-default" onClick={this.clearFlows}>
<i className="fa fa-eraser"></i>
&nbsp;Clear Flows
</button>
- &nbsp;
+ <span> </span>
<form className="form-inline" style={{display: "inline"}}>
- <FilterInput type="filter" color="black" value={this.state.filter} onChange={this.onFilterChange} />
- &nbsp;
- <FilterInput type="tag" color="hsl(48, 100%, 50%)" value={this.state.highlight} onChange={this.onHighlightChange}/>
+ <FilterInput
+ placeholder="Filter"
+ type="filter"
+ color="black"
+ value={filter}
+ onChange={this.onFilterChange} />
+ <span> </span>
+ <FilterInput
+ placeholder="Highlight"
+ type="tag"
+ color="hsl(48, 100%, 50%)"
+ value={highlight}
+ onChange={this.onHighlightChange}/>
+ <span> </span>
+ <FilterInput
+ placeholder="Intercept"
+ type="pause"
+ color="hsl(208, 56%, 53%)"
+ value={intercept}
+ onChange={this.onInterceptChange}/>
</form>
</div>
);
diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js
index 113b0896..046d6af0 100644
--- a/web/src/js/components/mainview.jsx.js
+++ b/web/src/js/components/mainview.jsx.js
@@ -16,12 +16,12 @@ var MainView = React.createClass({
var filt = Filt.parse(this.getQuery()[Query.FILTER] || "");
var highlightStr = this.getQuery()[Query.HIGHLIGHT];
var highlight = highlightStr ? Filt.parse(highlightStr) : false;
- } catch(e){
+ } catch (e) {
console.error("Error when processing filter: " + e);
}
return function filter_and_highlight(flow) {
- if(!this._highlight){
+ if (!this._highlight) {
this._highlight = {};
}
this._highlight[flow.id] = highlight && highlight(flow);
@@ -143,6 +143,13 @@ var MainView = React.createClass({
this.refs.flowDetails.nextTab(+1);
}
break;
+ case Key.A:
+ if (e.shiftKey) {
+ $.post("/flows/accept");
+ } else if(this.getSelected()) {
+ $.post("/flows/" + this.getSelected().id + "/accept");
+ }
+ break;
default:
console.debug("keydown", e.keyCode);
return;
diff --git a/web/src/js/components/utils.jsx.js b/web/src/js/components/utils.jsx.js
index 81ba6b4d..20dbda94 100644
--- a/web/src/js/components/utils.jsx.js
+++ b/web/src/js/components/utils.jsx.js
@@ -113,7 +113,7 @@ var xsrf = $.param({_xsrf: getCookie("_xsrf")});
//Tornado XSRF Protection.
$.ajaxPrefilter(function (options) {
- if (options.type === "post" && options.url[0] === "/") {
+ if (["post","put","delete"].indexOf(options.type.toLowerCase()) >= 0 && options.url[0] === "/") {
if (options.data) {
options.data += ("&" + xsrf);
} else {
diff --git a/web/src/js/store/settingstore.js b/web/src/js/store/settingstore.js
deleted file mode 100644
index e69de29b..00000000
--- a/web/src/js/store/settingstore.js
+++ /dev/null
diff --git a/web/src/js/utils.js b/web/src/js/utils.js
index b96aed0b..082f7272 100644
--- a/web/src/js/utils.js
+++ b/web/src/js/utils.js
@@ -90,11 +90,11 @@ var Key = {
TAB: 9,
SPACE: 32,
BACKSPACE: 8,
- J: 74,
- K: 75,
- H: 72,
- L: 76
};
+// Add A-Z
+for(var i=65; i <= 90; i++){
+ Key[String.fromCharCode(i)] = i;
+}
var formatSize = function (bytes) {