feat: web addon can edit flow

addon-dailer
liqiang 4 years ago
parent 2cfc8fcb96
commit 46f9a17509

@ -5,10 +5,11 @@ import Button from 'react-bootstrap/Button'
import './App.css' import './App.css'
import BreakPoint from './components/BreakPoint' import BreakPoint from './components/BreakPoint'
import EditFlow from './components/EditFlow'
import { FlowManager } from './flow' import { FlowManager } from './flow'
import { isTextResponse, getSize } from './utils' import { isTextBody, getSize } from './utils'
import { parseMessage, sendMessageEnum, buildMessageEdit, buildMessageMeta } from './message' import { parseMessage, sendMessageEnum, buildMessageMeta } from './message'
class App extends React.Component { class App extends React.Component {
@ -104,25 +105,29 @@ class App extends React.Component {
<span className={flowTab === 'Headers' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Headers' }) }}>Headers</span> <span className={flowTab === 'Headers' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Headers' }) }}>Headers</span>
<span className={flowTab === 'Preview' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Preview' }) }}>Preview</span> <span className={flowTab === 'Preview' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Preview' }) }}>Preview</span>
<span className={flowTab === 'Response' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Response' }) }}>Response</span> <span className={flowTab === 'Response' ? 'selected' : null} onClick={() => { this.setState({ flowTab: 'Response' }) }}>Response</span>
{
!flow.waitIntercept ? null : <EditFlow
<div className="flow-wait-area"> flow={flow}
<Button size="sm" onClick={() => { onChangeRequest={request => {
const msgType = flow.response ? sendMessageEnum.changeResponse : sendMessageEnum.changeRequest flow.request.method = request.method
const msg = buildMessageEdit(msgType, flow) flow.request.url = request.url
this.ws.send(msg) flow.request.header = request.header
flow.waitIntercept = false if (isTextBody(flow.request)) flow.request.body = request.body
this.setState({ flows: this.state.flows }) this.setState({ flows: this.state.flows })
}}>Continue</Button> }}
<Button size="sm" onClick={() => { onChangeResponse={response => {
const msgType = flow.response ? sendMessageEnum.dropResponse : sendMessageEnum.dropRequest flow.response.statusCode = response.statusCode
const msg = buildMessageEdit(msgType, flow) flow.response.header = response.header
this.ws.send(msg) if (isTextBody(flow.response)) flow.response.body = response.body
flow.waitIntercept = false this.setState({ flows: this.state.flows })
this.setState({ flows: this.state.flows }) }}
}}>Drop</Button> onMessage={msg => {
</div> this.ws.send(msg)
} flow.waitIntercept = false
this.setState({ flows: this.state.flows })
}}
/>
</div> </div>
<div style={{ padding: '20px' }}> <div style={{ padding: '20px' }}>
@ -175,7 +180,7 @@ class App extends React.Component {
<div className="header-block-content"> <div className="header-block-content">
<p> <p>
{ {
!(isTextResponse(request)) ? "Not text" : !(isTextBody(request)) ? "Not text" :
new TextDecoder().decode(request.body) new TextDecoder().decode(request.body)
} }
</p> </p>
@ -189,7 +194,7 @@ class App extends React.Component {
{ {
!(flowTab === 'Response') ? null : !(flowTab === 'Response') ? null :
!(response.body && response.body.byteLength) ? <div>No response</div> : !(response.body && response.body.byteLength) ? <div>No response</div> :
!(isTextResponse(response)) ? <div>Not text response</div> : !(isTextBody(response)) ? <div>Not text response</div> :
<div> <div>
{new TextDecoder().decode(response.body)} {new TextDecoder().decode(response.body)}
</div> </div>

@ -102,7 +102,7 @@ class BreakPoint extends React.Component {
<Button variant="primary" onClick={this.handleSave}> <Button variant="primary" onClick={this.handleSave}>
Save Save
</Button> </Button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
</div> </div>
) )

@ -0,0 +1,208 @@
import React from 'react'
import Button from 'react-bootstrap/Button'
import Modal from 'react-bootstrap/Modal'
import Form from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert'
import { sendMessageEnum, buildMessageEdit } from '../message'
import { isTextBody } from '../utils'
const stringifyRequest = request => {
const firstLine = `${request.method} ${request.url}`
const headerLines = Object.keys(request.header).map(key => {
const valstr = request.header[key].join(' \t ') // for parse convenience
return `${key}: ${valstr}`
}).join('\n')
let bodyLines = ''
if (request.body && isTextBody(request)) bodyLines = new TextDecoder().decode(request.body)
return `${firstLine}\n\n${headerLines}\n\n${bodyLines}`
}
const parseRequest = content => {
const sections = content.split('\n\n')
if (sections.length !== 3) return
const [firstLine, headerLines, bodyLines] = sections
const [method, url] = firstLine.split(' ')
if (!method || !url) return
const header = {}
for (const line of headerLines.split('\n')) {
const [key, vals] = line.split(': ')
if (!key || !vals) return
header[key] = vals.split(' \t ')
}
let body = null
if (bodyLines) body = new TextEncoder().encode(bodyLines)
return {
method,
url,
header,
body,
}
}
const stringifyResponse = response => {
const firstLine = `${response.statusCode}`
const headerLines = Object.keys(response.header).map(key => {
const valstr = response.header[key].join(' \t ') // for parse convenience
return `${key}: ${valstr}`
}).join('\n')
let bodyLines = ''
if (response.body && isTextBody(response)) bodyLines = new TextDecoder().decode(response.body)
return `${firstLine}\n\n${headerLines}\n\n${bodyLines}`
}
const parseResponse = content => {
const sections = content.split('\n\n')
if (sections.length !== 3) return
const [firstLine, headerLines, bodyLines] = sections
const statusCode = parseInt(firstLine)
if (isNaN(statusCode)) return
const header = {}
for (const line of headerLines.split('\n')) {
const [key, vals] = line.split(': ')
if (!key || !vals) return
header[key] = vals.split(' \t ')
}
let body = null
if (bodyLines) body = new TextEncoder().encode(bodyLines)
return {
statusCode,
header,
body,
}
}
class EditFlow extends React.Component {
constructor(props) {
super(props)
this.state = {
show: false,
alertMsg: '',
content: '',
}
this.handleClose = this.handleClose.bind(this)
this.handleShow = this.handleShow.bind(this)
this.handleSave = this.handleSave.bind(this)
}
showAlert(msg) {
this.setState({ alertMsg: msg })
}
handleClose() {
this.setState({ show: false })
}
handleShow() {
const { flow } = this.props
const when = flow.response ? 'response' : 'request'
let content = ''
if (when === 'request') {
content = stringifyRequest(flow.request)
} else {
content = stringifyResponse(flow.response)
}
this.setState({ show: true, alertMsg: '', content })
}
handleSave() {
const { flow } = this.props
const when = flow.response ? 'response' : 'request'
const { content } = this.state
if (when === 'request') {
const request = parseRequest(content)
if (!request) {
this.showAlert('parse error')
return
}
this.props.onChangeRequest(request)
this.handleClose()
} else {
const response = parseResponse(content)
if (!response) {
this.showAlert('parse error')
return
}
this.props.onChangeResponse(response)
this.handleClose()
}
}
render() {
const { flow } = this.props
if (!flow.waitIntercept) return null
const { alertMsg } = this.state
const when = flow.response ? 'response' : 'request'
return (
<div className="flow-wait-area">
<Button size="sm" onClick={this.handleShow}>Edit</Button>
<Button size="sm" onClick={() => {
const msgType = when === 'response' ? sendMessageEnum.changeResponse : sendMessageEnum.changeRequest
const msg = buildMessageEdit(msgType, flow)
this.props.onMessage(msg)
}}>Continue</Button>
<Button size="sm" onClick={() => {
const msgType = when === 'response' ? sendMessageEnum.dropResponse : sendMessageEnum.dropRequest
const msg = buildMessageEdit(msgType, flow)
this.props.onMessage(msg)
}}>Drop</Button>
<Modal size="lg" show={this.state.show} onHide={this.handleClose}>
<Modal.Header closeButton>
<Modal.Title>Edit {when === 'request' ? 'Request' : 'Response'}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group>
<Form.Control as="textarea" rows={10} value={this.state.content} onChange={e => { this.setState({ content: e.target.value }) }} />
</Form.Group>
{
!alertMsg ? null : <Alert variant="danger">{alertMsg}</Alert>
}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleClose}>
Close
</Button>
<Button variant="primary" onClick={this.handleSave}>
Save
</Button>
</Modal.Footer>
</Modal>
</div>
)
}
}
export default EditFlow

@ -1,9 +1,9 @@
export const isTextResponse = response => { export const isTextBody = payload => {
if (!response) return false if (!payload) return false
if (!response.header) return false if (!payload.header) return false
if (!response.header['Content-Type']) return false if (!payload.header['Content-Type']) return false
return /text|javascript|json/.test(response.header['Content-Type'].join('')) return /text|javascript|json/.test(payload.header['Content-Type'].join(''))
} }
export const getSize = response => { export const getSize = response => {

@ -148,7 +148,7 @@ func (c *concurrentConn) waitIntercept(f *flow.Flow, after *messageFlow) {
f.Request.Header = msg.request.Header f.Request.Header = msg.request.Header
f.Request.Body = msg.request.Body f.Request.Body = msg.request.Body
} else if msg.mType == messageTypeChangeResponse { } else if msg.mType == messageTypeChangeResponse {
// TODO: statusCode f.Response.StatusCode = msg.response.StatusCode
f.Response.Header = msg.response.Header f.Response.Header = msg.response.Header
f.Response.Body = msg.response.Body f.Response.Body = msg.response.Body
} }

Loading…
Cancel
Save