web code refactoring

addon-dailer
lqqyt2423 2 years ago
parent 9bd9ca04d0
commit 217ac902ed

@ -8,9 +8,10 @@ import BreakPoint from './components/BreakPoint'
import FlowPreview from './components/FlowPreview' import FlowPreview from './components/FlowPreview'
import ViewFlow from './components/ViewFlow' import ViewFlow from './components/ViewFlow'
import { FlowManager } from './flow' import { Flow, FlowManager } from './lib/flow'
import { parseMessage, SendMessageType, buildMessageMeta, Flow, MessageType } from './message' import { parseMessage, SendMessageType, buildMessageMeta, MessageType } from './lib/message'
import { isInViewPort } from './utils' import { isInViewPort } from './lib/utils'
import { ConnectionManager, IConnection } from './lib/connection'
interface IState { interface IState {
flows: Flow[] flows: Flow[]
@ -25,6 +26,7 @@ const wsReconnIntervals = [1, 1, 2, 2, 4, 4, 8, 8, 16, 16, 32, 32]
interface IProps {} interface IProps {}
class App extends React.Component<IProps, IState> { class App extends React.Component<IProps, IState> {
private connMgr: ConnectionManager
private flowMgr: FlowManager private flowMgr: FlowManager
private ws: WebSocket | null private ws: WebSocket | null
private wsUnmountClose: boolean private wsUnmountClose: boolean
@ -35,6 +37,7 @@ class App extends React.Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props) super(props)
this.connMgr = new ConnectionManager()
this.flowMgr = new FlowManager() this.flowMgr = new FlowManager()
this.state = { this.state = {
@ -106,7 +109,9 @@ class App extends React.Component<IProps, IState> {
} }
// console.log('msg:', msg) // console.log('msg:', msg)
if (msg.type === MessageType.REQUEST) { if (msg.type === MessageType.CONN) {
this.connMgr.set(msg.id, msg.content as IConnection)
} else if (msg.type === MessageType.REQUEST) {
const flow = new Flow(msg) const flow = new Flow(msg)
this.flowMgr.add(flow) this.flowMgr.add(flow)

@ -3,10 +3,9 @@ import Button from 'react-bootstrap/Button'
import Modal from 'react-bootstrap/Modal' import Modal from 'react-bootstrap/Modal'
import Form from 'react-bootstrap/Form' import Form from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert' import Alert from 'react-bootstrap/Alert'
import { SendMessageType, buildMessageEdit } from '../lib/message'
import { SendMessageType, buildMessageEdit, IRequest, IResponse, Header, Flow } from '../message' import { isTextBody } from '../lib/utils'
import { isTextBody } from '../utils' import type { Flow, Header, IRequest, IResponse } from '../lib/flow'
const stringifyRequest = (request: IRequest) => { const stringifyRequest = (request: IRequest) => {
const firstLine = `${request.method} ${request.url}` const firstLine = `${request.method} ${request.url}`

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { IFlowPreview } from '../message' import { shallowEqual } from '../lib/utils'
import { shallowEqual } from '../utils' import type { IFlowPreview } from '../lib/flow'
interface IProps { interface IProps {
flow: IFlowPreview flow: IFlowPreview

@ -4,9 +4,8 @@ import FormCheck from 'react-bootstrap/FormCheck'
import fetchToCurl from 'fetch-to-curl' import fetchToCurl from 'fetch-to-curl'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import JSONPretty from 'react-json-pretty' import JSONPretty from 'react-json-pretty'
import { Flow, IResponse } from '../message' import { isTextBody } from '../lib/utils'
import { isTextBody } from '../utils' import type { Flow, IResponse } from '../lib/flow'
import EditFlow from './EditFlow' import EditFlow from './EditFlow'
interface Iprops { interface Iprops {

@ -1,80 +0,0 @@
import { Flow } from './message'
export class FlowManager {
private items: Flow[]
private _map: Map<string, Flow>
private filterText: string
private filterTimer: number | null
private num: number
private max: number
constructor() {
this.items = []
this._map = new Map()
this.filterText = ''
this.filterTimer = null
this.num = 0
this.max = 1000
}
showList() {
let text = this.filterText
if (text) text = text.trim()
if (!text) return this.items
// regexp
if (text.startsWith('/') && text.endsWith('/')) {
text = text.slice(1, text.length - 1).trim()
if (!text) return this.items
try {
const reg = new RegExp(text)
return this.items.filter(item => {
return reg.test(item.request.url)
})
} catch (err) {
return this.items
}
}
return this.items.filter(item => {
return item.request.url.includes(text)
})
}
add(item: Flow) {
item.no = ++this.num
this.items.push(item)
this._map.set(item.id, item)
if (this.items.length > this.max) {
const oldest = this.items.shift()
if (oldest) this._map.delete(oldest.id)
}
}
get(id: string) {
return this._map.get(id)
}
changeFilter(text: string) {
this.filterText = text
}
changeFilterLazy(text: string, callback: () => void) {
if (this.filterTimer) {
clearTimeout(this.filterTimer)
this.filterTimer = null
}
this.filterTimer = setTimeout(() => {
this.filterText = text
callback()
}, 300) as any
}
clear() {
this.items = []
this._map = new Map()
}
}

@ -0,0 +1,33 @@
export interface IConnection {
id: string
clientConn: {
id: string
tls: boolean
address: string
}
serverConn: {
id: string
address: string
peername: string
}
}
export class ConnectionManager {
private _map: Map<string, IConnection>
constructor() {
this._map = new Map()
}
get(id: string) {
return this._map.get(id)
}
set(id: string, conn: IConnection) {
this._map.set(id, conn)
}
delete(id: string) {
this._map.delete(id)
}
}

@ -1,29 +1,8 @@
import { IMessage, MessageType } from './message'
import { arrayBufferToBase64, bufHexView, getSize, isTextBody } from './utils' import { arrayBufferToBase64, bufHexView, getSize, isTextBody } from './utils'
export enum MessageType {
CONN = 0,
REQUEST = 1,
REQUEST_BODY = 2,
RESPONSE = 3,
RESPONSE_BODY = 4,
}
export type Header = Record<string, string[]> export type Header = Record<string, string[]>
export interface IConnection {
id: string
clientConn: {
id: string
tls: boolean
address: string
}
serverConn: {
id: string
address: string
peername: string
}
}
export interface IRequest { export interface IRequest {
method: string method: string
url: string url: string
@ -32,7 +11,7 @@ export interface IRequest {
body?: ArrayBuffer body?: ArrayBuffer
} }
interface IFlowRequest { export interface IFlowRequest {
connId: string connId: string
request: IRequest request: IRequest
} }
@ -43,11 +22,9 @@ export interface IResponse {
body?: ArrayBuffer body?: ArrayBuffer
} }
export interface IMessage { export interface IPreviewBody {
type: MessageType type: 'image' | 'json' | 'binary'
id: string data: string | null
waitIntercept: boolean
content?: ArrayBuffer | IFlowRequest | IResponse
} }
export interface IFlowPreview { export interface IFlowPreview {
@ -63,11 +40,6 @@ export interface IFlowPreview {
contentType: string contentType: string
} }
interface IPreviewBody {
type: 'image' | 'json' | 'binary'
data: string | null
}
export class Flow { export class Flow {
public no: number public no: number
public id: string public id: string
@ -278,121 +250,81 @@ export class Flow {
} }
} }
const allMessageBytes = [ export class FlowManager {
MessageType.CONN, private items: Flow[]
MessageType.REQUEST, private _map: Map<string, Flow>
MessageType.REQUEST_BODY, private filterText: string
MessageType.RESPONSE, private filterTimer: number | null
MessageType.RESPONSE_BODY, private num: number
] private max: number
constructor() {
// type: 0/1/2/3/4 this.items = []
// messageFlow this._map = new Map()
// version 1 byte + type 1 byte + id 36 byte + waitIntercept 1 byte + content left bytes this.filterText = ''
export const parseMessage = (data: ArrayBuffer): IMessage | null => { this.filterTimer = null
if (data.byteLength < 39) return null this.num = 0
const meta = new Int8Array(data.slice(0, 39))
const version = meta[0] this.max = 1000
if (version !== 1) return null
const type = meta[1] as MessageType
if (!allMessageBytes.includes(type)) return null
const id = new TextDecoder().decode(data.slice(2, 38))
const waitIntercept = meta[38] === 1
const resp: IMessage = {
type,
id,
waitIntercept,
}
if (data.byteLength === 39) return resp
if (type === MessageType.REQUEST_BODY || type === MessageType.RESPONSE_BODY) {
resp.content = data.slice(39)
return resp
}
const contentStr = new TextDecoder().decode(data.slice(39))
let content: any
try {
content = JSON.parse(contentStr)
} catch (err) {
return null
} }
resp.content = content showList() {
return resp let text = this.filterText
} if (text) text = text.trim()
if (!text) return this.items
export enum SendMessageType { // regexp
CHANGE_REQUEST = 11, if (text.startsWith('/') && text.endsWith('/')) {
CHANGE_RESPONSE = 12, text = text.slice(1, text.length - 1).trim()
DROP_REQUEST = 13, if (!text) return this.items
DROP_RESPONSE = 14, try {
CHANGE_BREAK_POINT_RULES = 21, const reg = new RegExp(text)
} return this.items.filter(item => {
return reg.test(item.request.url)
})
} catch (err) {
return this.items
}
}
// type: 11/12/13/14 return this.items.filter(item => {
// messageEdit return item.request.url.includes(text)
// version 1 byte + type 1 byte + id 36 byte + header len 4 byte + header content bytes + body len 4 byte + [body content bytes] })
export const buildMessageEdit = (messageType: SendMessageType, flow: Flow) => {
if (messageType === SendMessageType.DROP_REQUEST || messageType === SendMessageType.DROP_RESPONSE) {
const view = new Uint8Array(38)
view[0] = 1
view[1] = messageType
view.set(new TextEncoder().encode(flow.id), 2)
return view
} }
let header: Omit<IRequest, 'body'> | Omit<IResponse, 'body'> add(item: Flow) {
let body: ArrayBuffer | Uint8Array | undefined item.no = ++this.num
this.items.push(item)
this._map.set(item.id, item)
if (messageType === SendMessageType.CHANGE_REQUEST) { if (this.items.length > this.max) {
({ body, ...header } = flow.request) const oldest = this.items.shift()
} else if (messageType === SendMessageType.CHANGE_RESPONSE) { if (oldest) this._map.delete(oldest.id)
({ body, ...header } = flow.response as IResponse) }
} else {
throw new Error('invalid message type')
} }
if (body instanceof ArrayBuffer) body = new Uint8Array(body) get(id: string) {
const bodyLen = (body && body.byteLength) ? body.byteLength : 0 return this._map.get(id)
}
if ('Content-Encoding' in header.header) delete header.header['Content-Encoding']
if ('Transfer-Encoding' in header.header) delete header.header['Transfer-Encoding']
header.header['Content-Length'] = [String(bodyLen)]
const headerBytes = new TextEncoder().encode(JSON.stringify(header))
const len = 2 + 36 + 4 + headerBytes.byteLength + 4 + bodyLen
const data = new ArrayBuffer(len)
const view = new Uint8Array(data)
view[0] = 1
view[1] = messageType
view.set(new TextEncoder().encode(flow.id), 2)
view.set(headerBytes, 2 + 36 + 4)
if (bodyLen) view.set(body as Uint8Array, 2 + 36 + 4 + headerBytes.byteLength + 4)
const view2 = new DataView(data)
view2.setUint32(2 + 36, headerBytes.byteLength)
view2.setUint32(2 + 36 + 4 + headerBytes.byteLength, bodyLen)
return view changeFilter(text: string) {
} this.filterText = text
}
changeFilterLazy(text: string, callback: () => void) {
if (this.filterTimer) {
clearTimeout(this.filterTimer)
this.filterTimer = null
}
// type: 21 this.filterTimer = setTimeout(() => {
// messageMeta this.filterText = text
// version 1 byte + type 1 byte + content left bytes callback()
export const buildMessageMeta = (messageType: SendMessageType, rules: any) => { }, 300) as any
if (messageType !== SendMessageType.CHANGE_BREAK_POINT_RULES) {
throw new Error('invalid message type')
} }
const rulesBytes = new TextEncoder().encode(JSON.stringify(rules)) clear() {
const view = new Uint8Array(2 + rulesBytes.byteLength) this.items = []
view[0] = 1 this._map = new Map()
view[1] = messageType }
view.set(rulesBytes, 2)
return view
} }

@ -0,0 +1,133 @@
import type { IConnection } from './connection'
import type { Flow, IFlowRequest, IRequest, IResponse } from './flow'
export enum MessageType {
CONN = 0,
REQUEST = 1,
REQUEST_BODY = 2,
RESPONSE = 3,
RESPONSE_BODY = 4,
}
const allMessageBytes = [
MessageType.CONN,
MessageType.REQUEST,
MessageType.REQUEST_BODY,
MessageType.RESPONSE,
MessageType.RESPONSE_BODY,
]
export interface IMessage {
type: MessageType
id: string
waitIntercept: boolean
content?: ArrayBuffer | IFlowRequest | IResponse | IConnection
}
// type: 0/1/2/3/4
// messageFlow
// version 1 byte + type 1 byte + id 36 byte + waitIntercept 1 byte + content left bytes
export const parseMessage = (data: ArrayBuffer): IMessage | null => {
if (data.byteLength < 39) return null
const meta = new Int8Array(data.slice(0, 39))
const version = meta[0]
if (version !== 1) return null
const type = meta[1] as MessageType
if (!allMessageBytes.includes(type)) return null
const id = new TextDecoder().decode(data.slice(2, 38))
const waitIntercept = meta[38] === 1
const resp: IMessage = {
type,
id,
waitIntercept,
}
if (data.byteLength === 39) return resp
if (type === MessageType.REQUEST_BODY || type === MessageType.RESPONSE_BODY) {
resp.content = data.slice(39)
return resp
}
const contentStr = new TextDecoder().decode(data.slice(39))
let content: any
try {
content = JSON.parse(contentStr)
} catch (err) {
return null
}
resp.content = content
return resp
}
export enum SendMessageType {
CHANGE_REQUEST = 11,
CHANGE_RESPONSE = 12,
DROP_REQUEST = 13,
DROP_RESPONSE = 14,
CHANGE_BREAK_POINT_RULES = 21,
}
// type: 11/12/13/14
// messageEdit
// version 1 byte + type 1 byte + id 36 byte + header len 4 byte + header content bytes + body len 4 byte + [body content bytes]
export const buildMessageEdit = (messageType: SendMessageType, flow: Flow) => {
if (messageType === SendMessageType.DROP_REQUEST || messageType === SendMessageType.DROP_RESPONSE) {
const view = new Uint8Array(38)
view[0] = 1
view[1] = messageType
view.set(new TextEncoder().encode(flow.id), 2)
return view
}
let header: Omit<IRequest, 'body'> | Omit<IResponse, 'body'>
let body: ArrayBuffer | Uint8Array | undefined
if (messageType === SendMessageType.CHANGE_REQUEST) {
({ body, ...header } = flow.request)
} else if (messageType === SendMessageType.CHANGE_RESPONSE) {
({ body, ...header } = flow.response as IResponse)
} else {
throw new Error('invalid message type')
}
if (body instanceof ArrayBuffer) body = new Uint8Array(body)
const bodyLen = (body && body.byteLength) ? body.byteLength : 0
if ('Content-Encoding' in header.header) delete header.header['Content-Encoding']
if ('Transfer-Encoding' in header.header) delete header.header['Transfer-Encoding']
header.header['Content-Length'] = [String(bodyLen)]
const headerBytes = new TextEncoder().encode(JSON.stringify(header))
const len = 2 + 36 + 4 + headerBytes.byteLength + 4 + bodyLen
const data = new ArrayBuffer(len)
const view = new Uint8Array(data)
view[0] = 1
view[1] = messageType
view.set(new TextEncoder().encode(flow.id), 2)
view.set(headerBytes, 2 + 36 + 4)
if (bodyLen) view.set(body as Uint8Array, 2 + 36 + 4 + headerBytes.byteLength + 4)
const view2 = new DataView(data)
view2.setUint32(2 + 36, headerBytes.byteLength)
view2.setUint32(2 + 36 + 4 + headerBytes.byteLength, bodyLen)
return view
}
// type: 21
// messageMeta
// version 1 byte + type 1 byte + content left bytes
export const buildMessageMeta = (messageType: SendMessageType, rules: any) => {
if (messageType !== SendMessageType.CHANGE_BREAK_POINT_RULES) {
throw new Error('invalid message type')
}
const rulesBytes = new TextEncoder().encode(JSON.stringify(rules))
const view = new Uint8Array(2 + rulesBytes.byteLength)
view[0] = 1
view[1] = messageType
view.set(rulesBytes, 2)
return view
}

@ -1,4 +1,4 @@
import { IRequest, IResponse } from './message' import type { IRequest, IResponse } from './flow'
export const isTextBody = (payload: IRequest | IResponse) => { export const isTextBody = (payload: IRequest | IResponse) => {
if (!payload) return false if (!payload) return false
Loading…
Cancel
Save