aboutsummaryrefslogtreecommitdiffstats
path: root/web/src
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2016-12-11 22:52:17 +0100
committerMaximilian Hils <git@maximilianhils.com>2016-12-12 00:08:29 +0100
commitd854e08653ccee12119266e2cc3f5d6c279341e5 (patch)
treea58d465ea62fdb9665389b39c284d689f2b94e78 /web/src
parentd1c7b203f08d4b1e1ee3c7a50762a4f08843feef (diff)
downloadmitmproxy-d854e08653ccee12119266e2cc3f5d6c279341e5.tar.gz
mitmproxy-d854e08653ccee12119266e2cc3f5d6c279341e5.tar.bz2
mitmproxy-d854e08653ccee12119266e2cc3f5d6c279341e5.zip
[web] various fixes
Diffstat (limited to 'web/src')
-rw-r--r--web/src/css/flowtable.less5
-rw-r--r--web/src/css/header.less1
-rw-r--r--web/src/js/components/ContentView/ShowFullContentButton.jsx4
-rw-r--r--web/src/js/components/FlowTable/FlowColumns.jsx12
-rw-r--r--web/src/js/components/Header.jsx4
-rw-r--r--web/src/js/components/Header/FileMenu.jsx8
-rw-r--r--web/src/js/components/Header/FlowMenu.jsx27
-rw-r--r--web/src/js/components/Header/MenuToggle.jsx2
-rw-r--r--web/src/js/components/common/Button.jsx2
-rw-r--r--web/src/js/ducks/flows.js44
-rw-r--r--web/src/js/ducks/ui/header.js6
-rw-r--r--web/src/js/ducks/ui/keyboard.js57
-rw-r--r--web/src/js/ducks/utils/store.js1
13 files changed, 119 insertions, 54 deletions
diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less
index 1b560eba..e8d3d5af 100644
--- a/web/src/css/flowtable.less
+++ b/web/src/css/flowtable.less
@@ -109,6 +109,9 @@
.fa-pause {
color: @interceptorange;
}
+ .fa-exclamation, .fa-times {
+ color: darkred;
+ }
}
.col-method {
width: 60px;
@@ -125,4 +128,4 @@
td.col-time, td.col-size {
text-align: right;
}
-} \ No newline at end of file
+}
diff --git a/web/src/css/header.less b/web/src/css/header.less
index a026d8aa..042d6811 100644
--- a/web/src/css/header.less
+++ b/web/src/css/header.less
@@ -47,6 +47,7 @@ header {
.menu-entry {
+ text-align: left;
height: (@menu-height - @menu-legend-height)/3;
line-height: 1;
padding: 0.5rem 1rem;
diff --git a/web/src/js/components/ContentView/ShowFullContentButton.jsx b/web/src/js/components/ContentView/ShowFullContentButton.jsx
index cfd96dd8..fd68991e 100644
--- a/web/src/js/components/ContentView/ShowFullContentButton.jsx
+++ b/web/src/js/components/ContentView/ShowFullContentButton.jsx
@@ -16,7 +16,9 @@ function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLi
return (
!showFullContent &&
<div>
- <Button className="view-all-content-btn btn-xs" onClick={() => setShowFullContent()} text="Show full content"/>
+ <Button className="view-all-content-btn btn-xs" onClick={() => setShowFullContent()}>
+ Show full content
+ </Button>
<span className="pull-right"> {visibleLines}/{contentLines} are visible &nbsp; </span>
</div>
)
diff --git a/web/src/js/components/FlowTable/FlowColumns.jsx b/web/src/js/components/FlowTable/FlowColumns.jsx
index 0ff80453..02a4fba1 100644
--- a/web/src/js/components/FlowTable/FlowColumns.jsx
+++ b/web/src/js/components/FlowTable/FlowColumns.jsx
@@ -54,6 +54,15 @@ IconColumn.getIcon = flow => {
}
export function PathColumn({ flow }) {
+
+ let err;
+ if(flow.error){
+ if (flow.error.msg === "Connection killed"){
+ err = <i className="fa fa-fw fa-times pull-right"></i>
+ } else {
+ err = <i className="fa fa-fw fa-exclamation pull-right"></i>
+ }
+ }
return (
<td className="col-path">
{flow.request.is_replay && (
@@ -62,6 +71,7 @@ export function PathColumn({ flow }) {
{flow.intercepted && (
<i className="fa fa-fw fa-pause pull-right"></i>
)}
+ {err}
{RequestUtils.pretty_url(flow.request)}
</td>
)
@@ -109,7 +119,7 @@ export function TimeColumn({ flow }) {
return (
<td className="col-time">
{flow.response ? (
- formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))
+ formatTimeDelta(1000 * (flow.response.timestamp_end - flow.server_conn.timestamp_start))
) : (
'...'
)}
diff --git a/web/src/js/components/Header.jsx b/web/src/js/components/Header.jsx
index 1500db1b..c15c951f 100644
--- a/web/src/js/components/Header.jsx
+++ b/web/src/js/components/Header.jsx
@@ -22,7 +22,9 @@ class Header extends Component {
if(selectedFlowId)
entries.push(FlowMenu)
- const Active = _.find(entries, (e) => e.title == activeMenu)
+ // Make sure to have a fallback in case FlowMenu is selected but we don't have any flows
+ // (e.g. because they are all deleted or not yet received)
+ const Active = _.find(entries, (e) => e.title == activeMenu) || MainMenu
return (
<header>
diff --git a/web/src/js/components/Header/FileMenu.jsx b/web/src/js/components/Header/FileMenu.jsx
index 53c63ea1..ec32c857 100644
--- a/web/src/js/components/Header/FileMenu.jsx
+++ b/web/src/js/components/Header/FileMenu.jsx
@@ -21,23 +21,23 @@ function FileMenu ({clearFlows, loadFlows, saveFlows}) {
<Dropdown className="pull-left" btnClass="special" text="mitmproxy">
<a href="#" onClick={e => FileMenu.onNewClick(e, clearFlows)}>
<i className="fa fa-fw fa-file"></i>
- New
+ &nbsp;New
</a>
<FileChooser
icon="fa-folder-open"
- text="Open..."
+ text="&nbsp;Open..."
onOpenFile={file => loadFlows(file)}
/>
<a href="#" onClick={e =>{ e.preventDefault(); saveFlows();}}>
<i className="fa fa-fw fa-floppy-o"></i>
- Save...
+ &nbsp;Save...
</a>
<Divider/>
<a href="http://mitm.it/" target="_blank">
<i className="fa fa-fw fa-external-link"></i>
- Install Certificates...
+ &nbsp;Install Certificates...
</a>
</Dropdown>
)
diff --git a/web/src/js/components/Header/FlowMenu.jsx b/web/src/js/components/Header/FlowMenu.jsx
index 420cb054..a404fdb7 100644
--- a/web/src/js/components/Header/FlowMenu.jsx
+++ b/web/src/js/components/Header/FlowMenu.jsx
@@ -8,21 +8,23 @@ FlowMenu.title = 'Flow'
FlowMenu.propTypes = {
flow: PropTypes.object,
- acceptFlow: PropTypes.func.isRequired,
+ resumeFlow: PropTypes.func.isRequired,
+ killFlow: PropTypes.func.isRequired,
replayFlow: PropTypes.func.isRequired,
duplicateFlow: PropTypes.func.isRequired,
removeFlow: PropTypes.func.isRequired,
revertFlow: PropTypes.func.isRequired
}
-function FlowMenu({ flow, acceptFlow, replayFlow, duplicateFlow, removeFlow, revertFlow }) {
+function FlowMenu({ flow, resumeFlow, killFlow, replayFlow, duplicateFlow, removeFlow, revertFlow }) {
if (!flow)
return <div/>
return (
<div>
<div className="menu-group">
<div className="menu-content">
- <Button title="[r]eplay flow" icon="fa-repeat text-primary" onClick={() => replayFlow(flow)}>
+ <Button title="[r]eplay flow" icon="fa-repeat text-primary"
+ onClick={() => replayFlow(flow)}>
Replay
</Button>
<Button title="[D]uplicate flow" icon="fa-copy text-info"
@@ -33,7 +35,8 @@ function FlowMenu({ flow, acceptFlow, replayFlow, duplicateFlow, removeFlow, rev
icon="fa-history text-warning" onClick={() => revertFlow(flow)}>
Revert
</Button>
- <Button title="[d]elete flow" icon="fa-trash text-danger" onClick={() => removeFlow(flow)}>
+ <Button title="[d]elete flow" icon="fa-trash text-danger"
+ onClick={() => removeFlow(flow)}>
Delete
</Button>
</div>
@@ -51,17 +54,18 @@ function FlowMenu({ flow, acceptFlow, replayFlow, duplicateFlow, removeFlow, rev
<div className="menu-group">
<div className="menu-content">
<Button disabled={!flow || !flow.intercepted} title="[a]ccept intercepted flow"
- icon="fa-play text-success" onClick={() => acceptFlow(flow)}
- >
- Resume
- </Button>
-
+ icon="fa-play text-success" onClick={() => resumeFlow(flow)}>
+ Resume
+ </Button>
+ <Button disabled={!flow || !flow.intercepted} title="kill intercepted flow [x]"
+ icon="fa-times text-danger" onClick={() => killFlow(flow)}>
+ Abort
+ </Button>
</div>
<div className="menu-legend">Interception</div>
</div>
-
</div>
)
}
@@ -71,7 +75,8 @@ export default connect(
flow: state.flows.byId[state.flows.selected[0]],
}),
{
- acceptFlow: flowsActions.accept,
+ resumeFlow: flowsActions.resume,
+ killFlow: flowsActions.kill,
replayFlow: flowsActions.replay,
duplicateFlow: flowsActions.duplicate,
removeFlow: flowsActions.remove,
diff --git a/web/src/js/components/Header/MenuToggle.jsx b/web/src/js/components/Header/MenuToggle.jsx
index 8977f3b9..91f093c6 100644
--- a/web/src/js/components/Header/MenuToggle.jsx
+++ b/web/src/js/components/Header/MenuToggle.jsx
@@ -14,7 +14,7 @@ export function MenuToggle({ value, onChange, children }) {
<div className="menu-entry">
<label>
<input type="checkbox"
- value={value}
+ checked={value}
onChange={onChange}/>
{children}
</label>
diff --git a/web/src/js/components/common/Button.jsx b/web/src/js/components/common/Button.jsx
index 69471f25..f05a68d0 100644
--- a/web/src/js/components/common/Button.jsx
+++ b/web/src/js/components/common/Button.jsx
@@ -11,7 +11,7 @@ Button.propTypes = {
export default function Button({ onClick, children, icon, disabled, className, title }) {
return (
<div className={classnames(className, 'btn btn-default')}
- onClick={onClick}
+ onClick={!disabled && onClick}
disabled={disabled}
title={title}>
{icon && (<i className={"fa fa-fw " + icon}/> )}
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
index d3717533..92408891 100644
--- a/web/src/js/ducks/flows.js
+++ b/web/src/js/ducks/flows.js
@@ -36,8 +36,29 @@ export default function reduce(state = defaultState, action) {
makeFilter(state.filter),
makeSort(state.sort)
)
+
+ let selected = state.selected
+ if(action.type === REMOVE && state.selected.includes(action.data)) {
+ if(state.selected.length > 1){
+ selected = selected.filter(x => x !== action.data)
+ } else {
+ selected = []
+ if (action.data in state.viewIndex && state.view.length > 1) {
+ let currentIndex = state.viewIndex[action.data],
+ nextSelection
+ if(currentIndex === state.view.length -1){ // last row
+ nextSelection = state.view[currentIndex - 1]
+ } else {
+ nextSelection = state.view[currentIndex + 1]
+ }
+ selected.push(nextSelection.id)
+ }
+ }
+ }
+
return {
...state,
+ selected,
...reduceStore(state, storeAction)
}
@@ -48,6 +69,12 @@ export default function reduce(state = defaultState, action) {
...reduceStore(state, storeActions.setFilter(makeFilter(action.filter), makeSort(state.sort)))
}
+ case SET_HIGHLIGHT:
+ return {
+ ...state,
+ highlight: action.highlight
+ }
+
case SET_SORT:
return {
...state,
@@ -144,14 +171,23 @@ export function selectRelative(shift) {
}
-export function accept(flow) {
- return dispatch => fetchApi(`/flows/${flow.id}/accept`, { method: 'POST' })
+export function resume(flow) {
+ return dispatch => fetchApi(`/flows/${flow.id}/resume`, { method: 'POST' })
+}
+
+export function resumeAll() {
+ return dispatch => fetchApi('/flows/resume', { method: 'POST' })
+}
+
+export function kill(flow) {
+ return dispatch => fetchApi(`/flows/${flow.id}/kill`, { method: 'POST' })
}
-export function acceptAll() {
- return dispatch => fetchApi('/flows/accept', { method: 'POST' })
+export function killAll() {
+ return dispatch => fetchApi('/flows/kill', { method: 'POST' })
}
+
export function remove(flow) {
return dispatch => fetchApi(`/flows/${flow.id}`, { method: 'DELETE' })
}
diff --git a/web/src/js/ducks/ui/header.js b/web/src/js/ducks/ui/header.js
index 25dfe602..6581149e 100644
--- a/web/src/js/ducks/ui/header.js
+++ b/web/src/js/ducks/ui/header.js
@@ -1,4 +1,4 @@
-import * as flowsActions from '../flows'
+import * as flowsActions from "../flows"
export const SET_ACTIVE_MENU = 'UI_SET_ACTIVE_MENU'
@@ -19,7 +19,7 @@ export default function reducer(state = defaultState, action) {
case flowsActions.SELECT:
// First Select
- if (action.flowIds.length && !state.isFlowSelected) {
+ if (action.flowIds.length > 0 && !state.isFlowSelected) {
return {
...state,
activeMenu: 'Flow',
@@ -28,7 +28,7 @@ export default function reducer(state = defaultState, action) {
}
// Deselect
- if (!action.flowIds.length && state.isFlowSelected) {
+ if (action.flowIds.length === 0 && state.isFlowSelected) {
let activeMenu = state.activeMenu
if (activeMenu == 'Flow') {
activeMenu = 'Start'
diff --git a/web/src/js/ducks/ui/keyboard.js b/web/src/js/ducks/ui/keyboard.js
index 7418eca9..30fd76e1 100644
--- a/web/src/js/ducks/ui/keyboard.js
+++ b/web/src/js/ducks/ui/keyboard.js
@@ -1,6 +1,6 @@
-import { Key } from '../../utils'
-import { selectTab } from './flow'
-import * as flowsActions from '../flows'
+import { Key } from "../../utils"
+import { selectTab } from "./flow"
+import * as flowsActions from "../flows"
export function onKeyDown(e) {
@@ -9,7 +9,7 @@ export function onKeyDown(e) {
return () => {
}
}
- var key = e.keyCode
+ var key = e.keyCode
var shiftKey = e.shiftKey
e.preventDefault()
return (dispatch, getState) => {
@@ -48,9 +48,8 @@ export function onKeyDown(e) {
dispatch(flowsActions.select(null))
break
- case Key.LEFT:
- {
- if(!flow) break
+ case Key.LEFT: {
+ if (!flow) break
let tabs = ['request', 'response', 'error'].filter(k => flow[k]).concat(['details']),
currentTab = getState().ui.flow.tab,
nextTab = tabs[(tabs.indexOf(currentTab) - 1 + tabs.length) % tabs.length]
@@ -59,9 +58,8 @@ export function onKeyDown(e) {
}
case Key.TAB:
- case Key.RIGHT:
- {
- if(!flow) break
+ case Key.RIGHT: {
+ if (!flow) break
let tabs = ['request', 'response', 'error'].filter(k => flow[k]).concat(['details']),
currentTab = getState().ui.flow.tab,
nextTab = tabs[(tabs.indexOf(currentTab) + 1) % tabs.length]
@@ -69,14 +67,7 @@ export function onKeyDown(e) {
break
}
- case Key.C:
- if (shiftKey) {
- dispatch(flowsActions.clear())
- }
- break
-
- case Key.D:
- {
+ case Key.D: {
if (!flow) {
return
}
@@ -88,32 +79,46 @@ export function onKeyDown(e) {
break
}
- case Key.A:
- {
+ case Key.A: {
if (shiftKey) {
- dispatch(flowsActions.acceptAll())
+ dispatch(flowsActions.resumeAll())
} else if (flow && flow.intercepted) {
- dispatch(flowsActions.accept(flow))
+ dispatch(flowsActions.resume(flow))
}
break
}
- case Key.R:
- {
+ case Key.R: {
if (!shiftKey && flow) {
dispatch(flowsActions.replay(flow))
}
break
}
- case Key.V:
- {
+ case Key.V: {
if (!shiftKey && flow && flow.modified) {
dispatch(flowsActions.revert(flow))
}
break
}
+ case Key.X: {
+ if (shiftKey) {
+ dispatch(flowsActions.killAll())
+ } else if (flow && flow.intercepted) {
+ dispatch(flowsActions.kill(flow))
+ }
+ break
+ }
+
+ case Key.Z: {
+ if (!shiftKey) {
+ dispatch(flowsActions.clear())
+ }
+ break
+ }
+
+
default:
return
}
diff --git a/web/src/js/ducks/utils/store.js b/web/src/js/ducks/utils/store.js
index 9ea4f02e..ac272650 100644
--- a/web/src/js/ducks/utils/store.js
+++ b/web/src/js/ducks/utils/store.js
@@ -85,6 +85,7 @@ export default function reduce(state = defaultState, action) {
if (!(action.id in byId)) {
break
}
+ byId = {...byId}
delete byId[action.id];
({data: list, dataIndex: listIndex} = removeData(list, listIndex, action.id))