import React from 'react' import Table from 'react-bootstrap/Table' import Form from 'react-bootstrap/Form' import Button from 'react-bootstrap/Button' import './App.css' import BreakPoint from './components/BreakPoint' import EditFlow from './components/EditFlow' import { FlowManager } from './flow' import { isTextBody, getSize } from './utils' import { parseMessage, SendMessageType, buildMessageMeta, IFlow, MessageType, IRequest, IResponse } from './message' interface IState { flows: IFlow[] flow: IFlow | null flowTab: 'Headers' | 'Preview' | 'Response' } class App extends React.Component { private flowMgr: FlowManager private ws: WebSocket | null constructor(props: any) { super(props) this.flowMgr = new FlowManager() this.state = { flows: this.flowMgr.showList(), flow: null, flowTab: 'Headers', // Headers, Preview, Response } this.ws = null } componentDidMount() { this.initWs() } componentWillUnmount() { if (this.ws) { this.ws.close() } } initWs() { if (this.ws) return let host if (process.env.NODE_ENV === 'development') { host = 'localhost:9081' } else { host = new URL(document.URL).host } this.ws = new WebSocket(`ws://${host}/echo`) this.ws.binaryType = 'arraybuffer' this.ws.onopen = () => { console.log('OPEN') } this.ws.onclose = () => { console.log('CLOSE') } this.ws.onmessage = evt => { const msg = parseMessage(evt.data) if (!msg) { console.error('parse error:', evt.data) return } // console.log('msg:', msg) if (msg.type === MessageType.REQUEST) { const flow = { id: msg.id, request: msg.content as IRequest, waitIntercept: msg.waitIntercept } this.flowMgr.add(flow) this.setState({ flows: this.flowMgr.showList() }) } else if (msg.type === MessageType.REQUEST_BODY) { const flow = this.flowMgr.get(msg.id) if (!flow) return flow.waitIntercept = msg.waitIntercept flow.request.body = msg.content as ArrayBuffer this.setState({ flows: this.state.flows }) } else if (msg.type === MessageType.RESPONSE) { const flow = this.flowMgr.get(msg.id) if (!flow) return flow.waitIntercept = msg.waitIntercept flow.response = msg.content as IResponse this.setState({ flows: this.state.flows }) } else if (msg.type === MessageType.RESPONSE_BODY) { const flow = this.flowMgr.get(msg.id) if (!flow || !flow.response) return flow.waitIntercept = msg.waitIntercept flow.response.body = msg.content as ArrayBuffer this.setState({ flows: this.state.flows }) } } this.ws.onerror = evt => { console.log('ERROR:', evt) } } renderFlow() { const { flow, flowTab } = this.state if (!flow) return null const request = flow.request const response: IResponse = (flow.response || {}) as any return (
{ this.setState({ flow: null }) }}>x { this.setState({ flowTab: 'Headers' }) }}>Headers { this.setState({ flowTab: 'Preview' }) }}>Preview { this.setState({ flowTab: 'Response' }) }}>Response { 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 => { if (!flow.response) flow.response = {} as IResponse 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 => { if (this.ws) this.ws.send(msg) flow.waitIntercept = false this.setState({ flows: this.state.flows }) }} />
{ !(flowTab === 'Headers') ? null :

General

Request URL: {request.url}

Request Method: {request.method}

Status Code: {`${response.statusCode || '(pending)'}`}

{ !(response.header) ? null :

Response Headers

{ Object.keys(response.header).map(key => { return (

{key}: {response.header[key].join(' ')}

) }) }
}

Request Headers

{ !(request.header) ? null : Object.keys(request.header).map(key => { return (

{key}: {request.header[key].join(' ')}

) }) }
{ !(request.body && request.body.byteLength) ? null :

Request Body

{ !(isTextBody(request)) ? 'Not text' : new TextDecoder().decode(request.body) }

}
} { !(flowTab === 'Response') ? null : !(response.body && response.body.byteLength) ?
No response
: !(isTextBody(response)) ?
Not text response
:
{new TextDecoder().decode(response.body)}
}
) } render() { const { flows } = this.state return (
{ const value = e.target.value this.flowMgr.changeFilterLazy(value, () => { this.setState({ flows: this.flowMgr.showList() }) }) }} >
{ const msg = buildMessageMeta(SendMessageType.CHANGE_BREAK_POINT_RULES, rules) if (this.ws) this.ws.send(msg) }} />
{ flows.map(f => { const url = f.request.url const u = new URL(url) let host = u.host if (host.length > 35) host = host.slice(0, 35) + '...' let path = u.pathname + u.search if (path.length > 65) path = path.slice(0, 65) + '...' const request = f.request const response: IResponse = (f.response || {}) as any const classNames = [] if (this.state.flow && this.state.flow.id === f.id) classNames.push('tr-selected') if (f.waitIntercept) classNames.push('tr-wait-intercept') return ( { this.setState({ flow: f }) }} > ) }) }
No Host Path Method Status Size
{f.no} {host} {path} {request.method} {response.statusCode || '(pending)'} {getSize(response)}
{this.renderFlow()}
) } } export default App