diff --git a/addon/web/client/src/App.js b/addon/web/client/src/App.js index 5584f57..a854ae1 100644 --- a/addon/web/client/src/App.js +++ b/addon/web/client/src/App.js @@ -5,10 +5,11 @@ import Button from 'react-bootstrap/Button' import './App.css' import BreakPoint from './components/BreakPoint' +import EditFlow from './components/EditFlow' import { FlowManager } from './flow' -import { isTextResponse, getSize } from './utils' -import { parseMessage, sendMessageEnum, buildMessageEdit, buildMessageMeta } from './message' +import { isTextBody, getSize } from './utils' +import { parseMessage, sendMessageEnum, buildMessageMeta } from './message' class App extends React.Component { @@ -104,25 +105,29 @@ class App extends React.Component { { this.setState({ flowTab: 'Headers' }) }}>Headers { this.setState({ flowTab: 'Preview' }) }}>Preview { this.setState({ flowTab: 'Response' }) }}>Response - { - !flow.waitIntercept ? null : -
- - -
- } + + { + flow.request.method = request.method + flow.request.url = request.url + flow.request.header = request.header + if (isTextBody(flow.request)) flow.request.body = request.body + this.setState({ flows: this.state.flows }) + }} + onChangeResponse={response => { + flow.response.statusCode = response.statusCode + flow.response.header = response.header + if (isTextBody(flow.response)) flow.response.body = response.body + this.setState({ flows: this.state.flows }) + }} + onMessage={msg => { + this.ws.send(msg) + flow.waitIntercept = false + this.setState({ flows: this.state.flows }) + }} + /> +
@@ -175,7 +180,7 @@ class App extends React.Component {

{ - !(isTextResponse(request)) ? "Not text" : + !(isTextBody(request)) ? "Not text" : new TextDecoder().decode(request.body) }

@@ -189,7 +194,7 @@ class App extends React.Component { { !(flowTab === 'Response') ? null : !(response.body && response.body.byteLength) ?
No response
: - !(isTextResponse(response)) ?
Not text response
: + !(isTextBody(response)) ?
Not text response
:
{new TextDecoder().decode(response.body)}
diff --git a/addon/web/client/src/components/BreakPoint.js b/addon/web/client/src/components/BreakPoint.js index 18399dd..cc83ad1 100644 --- a/addon/web/client/src/components/BreakPoint.js +++ b/addon/web/client/src/components/BreakPoint.js @@ -102,7 +102,7 @@ class BreakPoint extends React.Component { - +
) diff --git a/addon/web/client/src/components/EditFlow.js b/addon/web/client/src/components/EditFlow.js new file mode 100644 index 0000000..fe8536b --- /dev/null +++ b/addon/web/client/src/components/EditFlow.js @@ -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 ( +
+ + + + + + + + + + + Edit {when === 'request' ? 'Request' : 'Response'} + + + + + { this.setState({ content: e.target.value }) }} /> + + { + !alertMsg ? null : {alertMsg} + } + + + + + + + + +
+ ) + } +} + +export default EditFlow diff --git a/addon/web/client/src/utils.js b/addon/web/client/src/utils.js index 73caa4b..a175224 100644 --- a/addon/web/client/src/utils.js +++ b/addon/web/client/src/utils.js @@ -1,9 +1,9 @@ -export const isTextResponse = response => { - if (!response) return false - if (!response.header) return false - if (!response.header['Content-Type']) return false +export const isTextBody = payload => { + if (!payload) return false + if (!payload.header) 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 => { diff --git a/addon/web/conn.go b/addon/web/conn.go index d7b4c86..f9f730a 100644 --- a/addon/web/conn.go +++ b/addon/web/conn.go @@ -148,7 +148,7 @@ func (c *concurrentConn) waitIntercept(f *flow.Flow, after *messageFlow) { f.Request.Header = msg.request.Header f.Request.Body = msg.request.Body } else if msg.mType == messageTypeChangeResponse { - // TODO: statusCode + f.Response.StatusCode = msg.response.StatusCode f.Response.Header = msg.response.Header f.Response.Body = msg.response.Body }