Show http trailers in web-ui (#4142)

This commit is contained in:
sanlengjingvv 2020-08-12 23:21:31 +08:00 committed by GitHub
parent 5ebc338fde
commit 5d0e1906e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 306 additions and 39 deletions

View File

@ -7,6 +7,7 @@ Unreleased: mitmproxy next
* Fix links to anticache docs in mitmweb and use HTTPS for links to documentation (@rugk) * Fix links to anticache docs in mitmweb and use HTTPS for links to documentation (@rugk)
* Updated typing for WebsocketMessage.content (@prinzhorn) * Updated typing for WebsocketMessage.content (@prinzhorn)
* Prevent transparent mode from connecting to itself in the basic cases (@prinzhorn) * Prevent transparent mode from connecting to itself in the basic cases (@prinzhorn)
* Display HTTP trailers in mitmweb (@sanlengjingvv)
* --- TODO: add new PRs above this line --- * --- TODO: add new PRs above this line ---

View File

@ -305,6 +305,10 @@ class FlowHandler(RequestHandler):
request.headers.clear() request.headers.clear()
for header in v: for header in v:
request.headers.add(*header) request.headers.add(*header)
elif k == "trailers":
request.trailers.clear()
for trailer in v:
request.trailers.add(*trailer)
elif k == "content": elif k == "content":
request.text = v request.text = v
else: else:
@ -321,6 +325,10 @@ class FlowHandler(RequestHandler):
response.headers.clear() response.headers.clear()
for header in v: for header in v:
response.headers.add(*header) response.headers.add(*header)
elif k == "trailers":
response.trailers.clear()
for trailer in v:
response.trailers.add(*trailer)
elif k == "content": elif k == "content":
response.text = v response.text = v
else: else:

View File

@ -58,7 +58,7 @@ describe('Request Component', () => {
}) })
it('should handle change on flow request header', () => { it('should handle change on flow request header', () => {
let headers = TestUtils.findRenderedComponentWithType(provider, Headers) let headers = TestUtils.scryRenderedComponentsWithType(provider, Headers).filter(headers => headers.props.type === 'headers')[0]
headers.props.onChange('foo') headers.props.onChange('foo')
expect(store.getActions()).toEqual([updateEdit({ request: { headers: 'foo' }})]) expect(store.getActions()).toEqual([updateEdit({ request: { headers: 'foo' }})])
}) })
@ -115,7 +115,7 @@ describe('Response Component', () => {
}) })
it('should handle change on flow response headers', () => { it('should handle change on flow response headers', () => {
let headers = TestUtils.findRenderedComponentWithType(provider, Headers) let headers = TestUtils.scryRenderedComponentsWithType(provider, Headers).filter(headers => headers.props.type === 'headers')[0]
headers.props.onChange('foo') headers.props.onChange('foo')
expect(store.getActions()).toEqual([updateEdit( { response: { headers: 'foo' }})]) expect(store.getActions()).toEqual([updateEdit( { response: { headers: 'foo' }})])
}) })

View File

@ -199,6 +199,111 @@ exports[`Request Component should render correctly 1`] = `
</tbody> </tbody>
</table> </table>
<hr /> <hr />
<hr />
<table
className="header-table"
>
<tbody>
<tr>
<td
className="header-name"
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "trailer",
}
}
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": "qvalue",
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onInput={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</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> </article>
<footer> <footer>
<div <div
@ -462,6 +567,111 @@ exports[`Response Component should render correctly 1`] = `
</tbody> </tbody>
</table> </table>
<hr /> <hr />
<hr />
<table
className="header-table"
>
<tbody>
<tr>
<td
className="header-name"
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "trailer",
}
}
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": "qvalue",
}
}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onInput={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</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> </article>
<footer> <footer>
<div <div

View File

@ -46,7 +46,17 @@ export default function(){
"pretty_host": "address", "pretty_host": "address",
"scheme": "http", "scheme": "http",
"timestamp_end": null, "timestamp_end": null,
"timestamp_start": null "timestamp_start": null,
"trailers": [
[
"trailer",
"qvalue"
],
[
"content-length",
"7"
]
]
}, },
"response": { "response": {
"contentHash": "ab530a13e45914982b79f9b7e3fba994cfd1f3fb22f71cea1afbf02b460c6d1d", "contentHash": "ab530a13e45914982b79f9b7e3fba994cfd1f3fb22f71cea1afbf02b460c6d1d",
@ -66,7 +76,17 @@ export default function(){
"reason": "OK", "reason": "OK",
"status_code": 200, "status_code": 200,
"timestamp_end": 1495370312.4814625, "timestamp_end": 1495370312.4814625,
"timestamp_start": 1495370312.481462 "timestamp_start": 1495370312.481462,
"trailers": [
[
"trailer",
"qvalue"
],
[
"content-length",
"7"
]
]
}, },
"server_conn": { "server_conn": {
"address": [ "address": [

View File

@ -46,10 +46,15 @@ export default class Headers extends Component {
static propTypes = { static propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
message: PropTypes.object.isRequired, message: PropTypes.object.isRequired,
type: PropTypes.string.isRequired,
}
static defaultProps = {
type: 'headers',
} }
onChange(row, col, val) { onChange(row, col, val) {
const nextHeaders = _.cloneDeep(this.props.message.headers) const nextHeaders = _.cloneDeep(this.props.message[this.props.type])
nextHeaders[row][col] = val nextHeaders[row][col] = val
@ -75,7 +80,7 @@ export default class Headers extends Component {
} }
onTab(row, col, e) { onTab(row, col, e) {
const headers = this.props.message.headers const headers = this.props.message[this.props.type]
if (col === 0) { if (col === 0) {
this._nextSel = `${row}-value` this._nextSel = `${row}-value`
@ -88,7 +93,7 @@ export default class Headers extends Component {
e.preventDefault() e.preventDefault()
const nextHeaders = _.cloneDeep(this.props.message.headers) const nextHeaders = _.cloneDeep(this.props.message[this.props.type])
nextHeaders.push(['Name', 'Value']) nextHeaders.push(['Name', 'Value'])
this.props.onChange(nextHeaders) this.props.onChange(nextHeaders)
this._nextSel = `${row + 1}-key` this._nextSel = `${row + 1}-key`
@ -113,37 +118,45 @@ export default class Headers extends Component {
render() { render() {
const { message, readonly } = this.props const { message, readonly } = this.props
if (message[this.props.type]) {
return ( return (
<table className="header-table"> <table className="header-table">
<tbody> <tbody>
{message.headers.map((header, i) => ( {message[this.props.type].map((header, i) => (
<tr key={i}> <tr key={i}>
<td className="header-name"> <td className="header-name">
<HeaderEditor <HeaderEditor
ref={`${i}-key`} ref={`${i}-key`}
content={header[0]} content={header[0]}
readonly={readonly} readonly={readonly}
onDone={val => this.onChange(i, 0, val)} onDone={val => this.onChange(i, 0, val)}
onRemove={event => this.onRemove(i, 0, event)} onRemove={event => this.onRemove(i, 0, event)}
onTab={event => this.onTab(i, 0, event)} onTab={event => this.onTab(i, 0, event)}
/> />
<span className="header-colon">:</span> <span className="header-colon">:</span>
</td> </td>
<td className="header-value"> <td className="header-value">
<HeaderEditor <HeaderEditor
ref={`${i}-value`} ref={`${i}-value`}
content={header[1]} content={header[1]}
readonly={readonly} readonly={readonly}
onDone={val => this.onChange(i, 1, val)} onDone={val => this.onChange(i, 1, val)}
onRemove={event => this.onRemove(i, 1, event)} onRemove={event => this.onRemove(i, 1, event)}
onTab={event => this.onTab(i, 1, event)} onTab={event => this.onTab(i, 1, event)}
/> />
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
) )
} else {
return (
<table className="header-table">
<tbody>
</tbody>
</table>
)
}
} }
} }

View File

@ -105,6 +105,14 @@ export class Request extends Component {
flow={flow} flow={flow}
onContentChange={content => updateFlow({ request: {content}})} onContentChange={content => updateFlow({ request: {content}})}
message={flow.request}/> message={flow.request}/>
<hr/>
<Headers
message={flow.request}
readonly={!isEdit}
onChange={trailers => updateFlow({ request: { trailers } })}
type='trailers'
/>
</article> </article>
<HideInStatic> <HideInStatic>
{!noContent && {!noContent &&
@ -150,6 +158,13 @@ export class Response extends Component {
onContentChange={content => updateFlow({ response: {content}})} onContentChange={content => updateFlow({ response: {content}})}
message={flow.response} message={flow.response}
/> />
<hr/>
<Headers
message={flow.response}
readonly={!isEdit}
onChange={trailers => updateFlow({ response: { trailers } })}
type='trailers'
/>
</article> </article>
<HideInStatic> <HideInStatic>
{!noContent && {!noContent &&