diff --git a/mitmproxy/tools/cmdline.py b/mitmproxy/tools/cmdline.py index a94f28206..b345a4ccf 100644 --- a/mitmproxy/tools/cmdline.py +++ b/mitmproxy/tools/cmdline.py @@ -137,6 +137,7 @@ def mitmweb(opts): opts.make_parser(group, "web_open_browser") opts.make_parser(group, "web_port", metavar="PORT") opts.make_parser(group, "web_host", metavar="HOST") + opts.make_parser(group, "web_columns") common_options(parser, opts) group = parser.add_argument_group( diff --git a/mitmproxy/tools/web/static/app.css b/mitmproxy/tools/web/static/app.css index bd69915bc..70b37ea6a 100644 Binary files a/mitmproxy/tools/web/static/app.css and b/mitmproxy/tools/web/static/app.css differ diff --git a/mitmproxy/tools/web/static/app.js b/mitmproxy/tools/web/static/app.js index f97d73054..936f0519c 100644 Binary files a/mitmproxy/tools/web/static/app.js and b/mitmproxy/tools/web/static/app.js differ diff --git a/mitmproxy/tools/web/webaddons.py b/mitmproxy/tools/web/webaddons.py index 5e7d7982d..b792c5420 100644 --- a/mitmproxy/tools/web/webaddons.py +++ b/mitmproxy/tools/web/webaddons.py @@ -1,6 +1,8 @@ import webbrowser from mitmproxy import ctx +from typing import Sequence + class WebAddon: @@ -21,6 +23,10 @@ class WebAddon: "web_host", str, "127.0.0.1", "Web UI host." ) + loader.add_option( + "web_columns", Sequence[str], ["tls", "icon", "path", "method", "status", "size", "time"], + "Columns to show in the flow list" + ) def running(self): if hasattr(ctx.options, "web_open_browser") and ctx.options.web_open_browser: diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less index e8d3d5afc..f7771dd3f 100644 --- a/web/src/css/flowtable.less +++ b/web/src/css/flowtable.less @@ -125,6 +125,9 @@ .col-time { width: 50px; } + .col-timestamp { + width: auto; + } td.col-time, td.col-size { text-align: right; } diff --git a/web/src/js/__tests__/components/FlowTable/FlowRowSpec.js b/web/src/js/__tests__/components/FlowTable/FlowRowSpec.js index 7dfc1dcd0..d8ef1d3e0 100644 --- a/web/src/js/__tests__/components/FlowTable/FlowRowSpec.js +++ b/web/src/js/__tests__/components/FlowTable/FlowRowSpec.js @@ -1,12 +1,17 @@ import React from 'react' import renderer from 'react-test-renderer' import FlowRow from '../../../components/FlowTable/FlowRow' -import { TFlow } from '../../ducks/tutils' +import { TFlow, TStore } from '../../ducks/tutils' +import { Provider } from 'react-redux' describe('FlowRow Component', () => { let tFlow = new TFlow(), selectFn = jest.fn(), - flowRow = renderer.create(), + store = TStore(), + flowRow = renderer.create( + + + ), tree = flowRow.toJSON() it('should render correctly', () => { diff --git a/web/src/js/__tests__/components/FlowTable/FlowTableHeadSpec.js b/web/src/js/__tests__/components/FlowTable/FlowTableHeadSpec.js index 3b7302f22..977425e3f 100644 --- a/web/src/js/__tests__/components/FlowTable/FlowTableHeadSpec.js +++ b/web/src/js/__tests__/components/FlowTable/FlowTableHeadSpec.js @@ -7,7 +7,11 @@ import { TStore } from '../../ducks/tutils' describe('FlowTableHead Component', () => { let sortFn = jest.fn(), - flowTableHead = renderer.create(), + store = TStore(), + flowTableHead = renderer.create( + + + ), tree =flowTableHead.toJSON() it('should render correctly', () => { diff --git a/web/src/js/__tests__/components/FlowTable/__snapshots__/FlowTableHeadSpec.js.snap b/web/src/js/__tests__/components/FlowTable/__snapshots__/FlowTableHeadSpec.js.snap index 3f066c6e9..46617efd4 100644 --- a/web/src/js/__tests__/components/FlowTable/__snapshots__/FlowTableHeadSpec.js.snap +++ b/web/src/js/__tests__/components/FlowTable/__snapshots__/FlowTableHeadSpec.js.snap @@ -79,6 +79,12 @@ exports[`FlowTableHead Component should render correctly 1`] = ` > Status + + TimeStamp + + {flow.request.timestamp_start ? ( + formatTimeStamp(flow.request.timestamp_start) + ) : ( + '...' + )} + + ) +} + +TimeStampColumn.headerClass = 'col-timestamp' +TimeStampColumn.headerName = 'TimeStamp' + export default [ TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, + TimeStampColumn, SizeColumn, TimeColumn, ] diff --git a/web/src/js/components/FlowTable/FlowRow.jsx b/web/src/js/components/FlowTable/FlowRow.jsx index 71a30e398..8325b41e7 100644 --- a/web/src/js/components/FlowTable/FlowRow.jsx +++ b/web/src/js/components/FlowTable/FlowRow.jsx @@ -1,8 +1,10 @@ import React from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import columns from './FlowColumns' +import {defaultColumnNames} from './FlowColumns' import { pure } from '../../utils' +import {getDisplayColumns} from './FlowTableHead' +import { connect } from 'react-redux' FlowRow.propTypes = { onSelect: PropTypes.func.isRequired, @@ -11,7 +13,7 @@ FlowRow.propTypes = { selected: PropTypes.bool, } -function FlowRow({ flow, selected, highlighted, onSelect }) { +function FlowRow({ flow, selected, highlighted, onSelect, displayColumnNames }) { const className = classnames({ 'selected': selected, 'highlighted': highlighted, @@ -20,13 +22,19 @@ function FlowRow({ flow, selected, highlighted, onSelect }) { 'has-response': flow.response, }) + const displayColumns = getDisplayColumns(displayColumnNames) + return ( onSelect(flow.id)}> - {columns.map(Column => ( + {displayColumns.map(Column => ( ))} ) } -export default pure(FlowRow) +export default connect( + state => ({ + displayColumnNames: state.options["web_columns"] ? state.options["web_columns"].value : defaultColumnNames, + }) +)(pure(FlowRow)) diff --git a/web/src/js/components/FlowTable/FlowTableHead.jsx b/web/src/js/components/FlowTable/FlowTableHead.jsx index bbd8c9169..be1b3d771 100644 --- a/web/src/js/components/FlowTable/FlowTableHead.jsx +++ b/web/src/js/components/FlowTable/FlowTableHead.jsx @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' import classnames from 'classnames' -import columns from './FlowColumns' +import columns, {defaultColumnNames} from './FlowColumns' import { setSort } from '../../ducks/flows' @@ -10,14 +10,30 @@ FlowTableHead.propTypes = { setSort: PropTypes.func.isRequired, sortDesc: PropTypes.bool.isRequired, sortColumn: PropTypes.string, + displayColumnNames: PropTypes.array, } -export function FlowTableHead({ sortColumn, sortDesc, setSort }) { +export function getDisplayColumns(displayColumnNames) { + let displayColumns = [] + if (typeof displayColumnNames == "undefined") { + return columns + } + for (const column of columns) { + if (displayColumnNames.includes(column.name.slice(0,-6).toLowerCase())) { + displayColumns.push(column) + } + } + return displayColumns +} + +export function FlowTableHead({ sortColumn, sortDesc, setSort, displayColumnNames}) { const sortType = sortDesc ? 'sort-desc' : 'sort-asc' + const displayColumns = getDisplayColumns(displayColumnNames) + return ( - {columns.map(Column => ( + {displayColumns.map(Column => ( setSort(Column.name, Column.name !== sortColumn ? false : !sortDesc)}> @@ -32,6 +48,7 @@ export default connect( state => ({ sortDesc: state.flows.sort.desc, sortColumn: state.flows.sort.column, + displayColumnNames: state.options["web_columns"] ? state.options["web_columns"].value : defaultColumnNames, }), { setSort