Merge pull request #4533 from mhils/pr-4188
mitmweb: Make columns configurable/customizable (#4188)
This commit is contained in:
commit
04bceb0e05
|
@ -41,6 +41,7 @@ If you depend on these features, please raise your voice in
|
|||
events have been replaced with new events, see addon documentation for details (@mhils)
|
||||
* Contentviews now implement `render_priority` instead of `should_render`, allowing more specialization (@mhils)
|
||||
* Addition of block_list option to block requests with a set status code (@ericbeland)
|
||||
* Make mitmweb columns configurable and customizable (@gorogoroumaru)
|
||||
* Automatic JSON view mode when `+json` suffix in content type (@kam800)
|
||||
* Use pyca/cryptography to generate certificates, not pyOpenSSL (@mhils)
|
||||
* Remove the legacy protocol stack (@Kriechi)
|
||||
|
|
|
@ -184,9 +184,6 @@ def tserver_conn() -> connection.Server:
|
|||
return c
|
||||
|
||||
|
||||
def terr(content="error"):
|
||||
"""
|
||||
@return: mitmproxy.proxy.protocol.primitives.Error
|
||||
"""
|
||||
err = flow.Error(content)
|
||||
def terr(content: str = "error") -> flow.Error:
|
||||
err = flow.Error(content, 946681207)
|
||||
return err
|
||||
|
|
|
@ -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(
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,7 @@
|
|||
import webbrowser
|
||||
|
||||
from mitmproxy import ctx
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
class WebAddon:
|
||||
|
@ -21,6 +22,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:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import asyncio
|
||||
import json as _json
|
||||
import logging
|
||||
from unittest import mock
|
||||
import os
|
||||
import asyncio
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -331,14 +331,18 @@ class TestApp(tornado.testing.AsyncHTTPTestCase):
|
|||
# Set some value as constant, so that _tflow.js would not change every time.
|
||||
_tflow['client_conn']['id'] = "4a18d1a0-50a1-48dd-9aa6-d45d74282939"
|
||||
_tflow['id'] = "d91165be-ca1f-4612-88a9-c0f8696f3e29"
|
||||
_tflow['error']['timestamp'] = 1495370312.4814785
|
||||
_tflow['response']['timestamp_end'] = 1495370312.4814625
|
||||
_tflow['response']['timestamp_start'] = 1495370312.481462
|
||||
_tflow['server_conn']['id'] = "f087e7b2-6d0a-41a8-a8f0-e1a4761395f8"
|
||||
_tflow["request"]["trailers"] = [["trailer", "qvalue"]]
|
||||
_tflow["response"]["trailers"] = [["trailer", "qvalue"]]
|
||||
tflow_json = _json.dumps(_tflow, indent=4, sort_keys=True)
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
web_root = os.path.join(here, os.pardir, os.pardir, os.pardir, os.pardir, 'web')
|
||||
tflow_path = os.path.join(web_root, 'src/js/__tests__/ducks/_tflow.js')
|
||||
content = f"""export default function(){{\n return {tflow_json}\n}}"""
|
||||
content = (
|
||||
f"/** Auto-generated by test_app.py:TestApp._test_generate_tflow_js */\n"
|
||||
f"export default function(){{\n"
|
||||
f" return {tflow_json}\n"
|
||||
f"}}"
|
||||
)
|
||||
with open(tflow_path, 'w', newline="\n") as f:
|
||||
f.write(content)
|
||||
|
|
|
@ -7,6 +7,10 @@ and activate your virtualenv environment before proceeding.**
|
|||
- Run `yarn run gulp` to start live-compilation.
|
||||
- Run `mitmweb` and open http://localhost:8081/
|
||||
|
||||
## Testing
|
||||
|
||||
- Run `yarn run test` to run the testsuite.
|
||||
|
||||
## Architecture
|
||||
|
||||
There are two components:
|
||||
|
|
|
@ -214,7 +214,7 @@ gulp.task(
|
|||
|
||||
gulp.task("default", gulp.series(
|
||||
"dev",
|
||||
function () {
|
||||
function watch() {
|
||||
livereload.listen({auto: true});
|
||||
gulp.watch(["src/css/vendor*"], gulp.series("styles-vendor-dev"));
|
||||
gulp.watch(["src/css/**"], gulp.series("styles-app-dev"));
|
||||
|
|
|
@ -125,6 +125,9 @@
|
|||
.col-time {
|
||||
width: 50px;
|
||||
}
|
||||
.col-timestamp {
|
||||
width: auto;
|
||||
}
|
||||
td.col-time, td.col-size {
|
||||
text-align: right;
|
||||
}
|
||||
|
|
|
@ -105,4 +105,12 @@ describe('FlowColumns Components', () => {
|
|||
tree = timeColumn.toJSON()
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should render TimeStampColumn', () => {
|
||||
let timeStampColumn = renderer.create(<Columns.TimeStampColumn flow={tflow}/>),
|
||||
tree = timeStampColumn.toJSON()
|
||||
tflow.request.timestamp_start =
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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(<FlowRow flow={tFlow} onSelect={selectFn}/>),
|
||||
store = TStore(),
|
||||
flowRow = renderer.create(
|
||||
<Provider store={store} >
|
||||
<FlowRow flow={tFlow} onSelect={selectFn}/>
|
||||
</Provider>),
|
||||
tree = flowRow.toJSON()
|
||||
|
||||
it('should render correctly', () => {
|
||||
|
|
|
@ -7,7 +7,11 @@ import { TStore } from '../../ducks/tutils'
|
|||
|
||||
describe('FlowTableHead Component', () => {
|
||||
let sortFn = jest.fn(),
|
||||
flowTableHead = renderer.create(<FlowTableHead setSort={sortFn} sortDesc={true}/>),
|
||||
store = TStore(),
|
||||
flowTableHead = renderer.create(
|
||||
<Provider store={store}>
|
||||
<FlowTableHead setSort={sortFn} sortDesc={true}/>
|
||||
</Provider>),
|
||||
tree =flowTableHead.toJSON()
|
||||
|
||||
it('should render correctly', () => {
|
||||
|
|
|
@ -127,7 +127,7 @@ exports[`FlowColumns Components should render TimeColumn 1`] = `
|
|||
<td
|
||||
className="col-time"
|
||||
>
|
||||
415381h
|
||||
3s
|
||||
</td>
|
||||
`;
|
||||
|
||||
|
@ -139,6 +139,14 @@ exports[`FlowColumns Components should render TimeColumn 2`] = `
|
|||
</td>
|
||||
`;
|
||||
|
||||
exports[`FlowColumns Components should render TimeStampColumn 1`] = `
|
||||
<td
|
||||
className="col-start"
|
||||
>
|
||||
1999-12-31 23:00:00.000
|
||||
</td>
|
||||
`;
|
||||
|
||||
exports[`FlowColumns Components should render pathColumn 1`] = `
|
||||
<td
|
||||
className="col-path"
|
||||
|
|
|
@ -46,7 +46,7 @@ exports[`FlowRow Component should render correctly 1`] = `
|
|||
<td
|
||||
className="col-time"
|
||||
>
|
||||
415381h
|
||||
3s
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
|
|
@ -79,6 +79,12 @@ exports[`FlowTableHead Component should render correctly 1`] = `
|
|||
>
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
className="col-timestamp"
|
||||
onClick={[Function]}
|
||||
>
|
||||
TimeStamp
|
||||
</th>
|
||||
<th
|
||||
className="col-size"
|
||||
onClick={[Function]}
|
||||
|
|
|
@ -155,6 +155,18 @@ exports[`Details Component should render correctly 1`] = `
|
|||
TLSv1.2
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<abbr
|
||||
title="ALPN protocol negotiated"
|
||||
>
|
||||
ALPN:
|
||||
</abbr>
|
||||
</td>
|
||||
<td>
|
||||
http/1.1
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Resolved address:
|
||||
|
@ -182,24 +194,20 @@ exports[`Details Component should render correctly 1`] = `
|
|||
className="timing-table"
|
||||
>
|
||||
<tbody>
|
||||
<tr />
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
Server conn. initiated
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:01.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. established
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:01.000
|
||||
1999-12-31 23:00:02.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
2s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -208,25 +216,57 @@ exports[`Details Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:02.000
|
||||
1999-12-31 23:00:03.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
3s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. established
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1999-12-31 23:00:00.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
0ms
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
First request byte
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1999-12-31 23:00:00.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. SSL handshake
|
||||
Request complete
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:02.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Server conn. SSL handshake
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:03.000
|
||||
1999-12-31 23:00:01.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
1s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -235,7 +275,14 @@ exports[`Details Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:02.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
2s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -244,7 +291,14 @@ exports[`Details Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:03.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
3s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -323,8 +377,6 @@ exports[`Details Component should render correctly when server address is missin
|
|||
className="timing-table"
|
||||
>
|
||||
<tbody>
|
||||
<tr />
|
||||
<tr />
|
||||
<tr />
|
||||
<tr />
|
||||
<tr />
|
||||
|
@ -334,16 +386,40 @@ exports[`Details Component should render correctly when server address is missin
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:01.000
|
||||
1999-12-31 23:00:00.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
0ms
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
First request byte
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1999-12-31 23:00:00.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. SSL handshake
|
||||
Request complete
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:02.000
|
||||
1999-12-31 23:00:01.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
1s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -352,7 +428,14 @@ exports[`Details Component should render correctly when server address is missin
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:02.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
2s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -361,7 +444,14 @@ exports[`Details Component should render correctly when server address is missin
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:03.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
3s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -400,24 +490,20 @@ exports[`Timing Component should render correctly 1`] = `
|
|||
className="timing-table"
|
||||
>
|
||||
<tbody>
|
||||
<tr />
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
Server conn. initiated
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:01.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. established
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:01.000
|
||||
1999-12-31 23:00:02.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
2s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -426,25 +512,57 @@ exports[`Timing Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:02.000
|
||||
1999-12-31 23:00:03.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
3s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. established
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1999-12-31 23:00:00.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
0ms
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr />
|
||||
<tr>
|
||||
<td>
|
||||
First request byte
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1999-12-31 23:00:00.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Client conn. SSL handshake
|
||||
Request complete
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:02.000
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Server conn. SSL handshake
|
||||
:
|
||||
</td>
|
||||
<td>
|
||||
1970-01-01 00:00:03.000
|
||||
1999-12-31 23:00:01.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
1s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -453,7 +571,14 @@ exports[`Timing Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:02.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
2s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -462,7 +587,14 @@ exports[`Timing Component should render correctly 1`] = `
|
|||
:
|
||||
</td>
|
||||
<td>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:03.000
|
||||
<span
|
||||
className="text-muted"
|
||||
>
|
||||
(
|
||||
3s
|
||||
)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -10,7 +10,7 @@ exports[`Error Component should render correctly 1`] = `
|
|||
error
|
||||
<div>
|
||||
<small>
|
||||
2017-05-21 12:38:32.481
|
||||
1999-12-31 23:00:07.000
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -253,55 +253,6 @@ exports[`Request Component should render correctly 1`] = `
|
|||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
className="header-name"
|
||||
>
|
||||
<div
|
||||
className="inline-input readonly"
|
||||
contentEditable={undefined}
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "content-length",
|
||||
}
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onInput={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onPaste={[Function]}
|
||||
tabIndex={undefined}
|
||||
/>
|
||||
<span
|
||||
className="header-colon"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
className="header-value"
|
||||
>
|
||||
<div
|
||||
className="inline-input readonly"
|
||||
contentEditable={undefined}
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "7",
|
||||
}
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onInput={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onPaste={[Function]}
|
||||
tabIndex={undefined}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
@ -621,55 +572,6 @@ exports[`Response Component should render correctly 1`] = `
|
|||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
className="header-name"
|
||||
>
|
||||
<div
|
||||
className="inline-input readonly"
|
||||
contentEditable={undefined}
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "content-length",
|
||||
}
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onInput={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onPaste={[Function]}
|
||||
tabIndex={undefined}
|
||||
/>
|
||||
<span
|
||||
className="header-colon"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
className="header-value"
|
||||
>
|
||||
<div
|
||||
className="inline-input readonly"
|
||||
contentEditable={undefined}
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "7",
|
||||
}
|
||||
}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onInput={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onPaste={[Function]}
|
||||
tabIndex={undefined}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/** Auto-generated by test_app.py:TestApp._test_generate_tflow_js */
|
||||
export default function(){
|
||||
return {
|
||||
"client_conn": {
|
||||
|
@ -7,21 +8,21 @@ export default function(){
|
|||
],
|
||||
"alpn_proto_negotiated": "http/1.1",
|
||||
"cipher_name": "cipher",
|
||||
"clientcert": null,
|
||||
"id": "4a18d1a0-50a1-48dd-9aa6-d45d74282939",
|
||||
"sni": "address",
|
||||
"ssl_established": false,
|
||||
"timestamp_end": 3.0,
|
||||
"timestamp_ssl_setup": 2.0,
|
||||
"timestamp_start": 1.0,
|
||||
"timestamp_end": 946681206,
|
||||
"timestamp_start": 946681200,
|
||||
"timestamp_tls_setup": 946681201,
|
||||
"tls_established": true,
|
||||
"tls_version": "TLSv1.2"
|
||||
},
|
||||
"error": {
|
||||
"msg": "error",
|
||||
"timestamp": 1495370312.4814785
|
||||
"timestamp": 946681207.0
|
||||
},
|
||||
"id": "d91165be-ca1f-4612-88a9-c0f8696f3e29",
|
||||
"intercepted": false,
|
||||
"is_replay": null,
|
||||
"marked": false,
|
||||
"modified": false,
|
||||
"request": {
|
||||
|
@ -45,16 +46,12 @@ export default function(){
|
|||
"port": 22,
|
||||
"pretty_host": "address",
|
||||
"scheme": "http",
|
||||
"timestamp_end": null,
|
||||
"timestamp_start": null,
|
||||
"timestamp_end": 946681201,
|
||||
"timestamp_start": 946681200,
|
||||
"trailers": [
|
||||
[
|
||||
"trailer",
|
||||
"qvalue"
|
||||
],
|
||||
[
|
||||
"content-length",
|
||||
"7"
|
||||
]
|
||||
]
|
||||
},
|
||||
|
@ -75,16 +72,12 @@ export default function(){
|
|||
"is_replay": false,
|
||||
"reason": "OK",
|
||||
"status_code": 200,
|
||||
"timestamp_end": 1495370312.4814625,
|
||||
"timestamp_start": 1495370312.481462,
|
||||
"timestamp_end": 946681203,
|
||||
"timestamp_start": 946681202,
|
||||
"trailers": [
|
||||
[
|
||||
"trailer",
|
||||
"qvalue"
|
||||
],
|
||||
[
|
||||
"content-length",
|
||||
"7"
|
||||
]
|
||||
]
|
||||
},
|
||||
|
@ -93,7 +86,7 @@ export default function(){
|
|||
"address",
|
||||
22
|
||||
],
|
||||
"alpn_proto_negotiated": null,
|
||||
"alpn_proto_negotiated": "http/1.1",
|
||||
"id": "f087e7b2-6d0a-41a8-a8f0-e1a4761395f8",
|
||||
"ip_address": [
|
||||
"192.168.0.1",
|
||||
|
@ -104,13 +97,12 @@ export default function(){
|
|||
"address",
|
||||
22
|
||||
],
|
||||
"ssl_established": false,
|
||||
"timestamp_end": 4.0,
|
||||
"timestamp_ssl_setup": 3.0,
|
||||
"timestamp_start": 1.0,
|
||||
"timestamp_tcp_setup": 2.0,
|
||||
"tls_version": "TLSv1.2",
|
||||
"via": null
|
||||
"timestamp_end": 946681205,
|
||||
"timestamp_start": 946681202,
|
||||
"timestamp_tcp_setup": 946681203,
|
||||
"timestamp_tls_setup": 946681204,
|
||||
"tls_established": true,
|
||||
"tls_version": "TLSv1.2"
|
||||
},
|
||||
"type": "http"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React, { Component } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { RequestUtils, ResponseUtils } from '../../flow/utils.js'
|
||||
import { formatSize, formatTimeDelta } from '../../utils.js'
|
||||
import { formatSize, formatTimeDelta, formatTimeStamp } from '../../utils.js'
|
||||
|
||||
export const defaultColumnNames = ["tls", "icon", "path", "method", "status", "size", "time"]
|
||||
|
||||
export function TLSColumn({ flow }) {
|
||||
return (
|
||||
|
@ -148,12 +150,28 @@ export function TimeColumn({ flow }) {
|
|||
TimeColumn.headerClass = 'col-time'
|
||||
TimeColumn.headerName = 'Time'
|
||||
|
||||
export function TimeStampColumn({ flow }) {
|
||||
return (
|
||||
<td className="col-start">
|
||||
{flow.request.timestamp_start ? (
|
||||
formatTimeStamp(flow.request.timestamp_start)
|
||||
) : (
|
||||
'...'
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
}
|
||||
|
||||
TimeStampColumn.headerClass = 'col-timestamp'
|
||||
TimeStampColumn.headerName = 'TimeStamp'
|
||||
|
||||
export default [
|
||||
TLSColumn,
|
||||
IconColumn,
|
||||
PathColumn,
|
||||
MethodColumn,
|
||||
StatusColumn,
|
||||
TimeStampColumn,
|
||||
SizeColumn,
|
||||
TimeColumn,
|
||||
]
|
||||
|
|
|
@ -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 (
|
||||
<tr className={className} onClick={() => onSelect(flow.id)}>
|
||||
{columns.map(Column => (
|
||||
{displayColumns.map(Column => (
|
||||
<Column key={Column.name} flow={flow}/>
|
||||
))}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default pure(FlowRow)
|
||||
export default connect(
|
||||
state => ({
|
||||
displayColumnNames: state.options["web_columns"] ? state.options["web_columns"].value : defaultColumnNames,
|
||||
})
|
||||
)(pure(FlowRow))
|
||||
|
|
|
@ -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 (
|
||||
<tr>
|
||||
{columns.map(Column => (
|
||||
{displayColumns.map(Column => (
|
||||
<th className={classnames(Column.headerClass, sortColumn === Column.name && sortType)}
|
||||
key={Column.name}
|
||||
onClick={() => 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
|
||||
|
|
Loading…
Reference in New Issue