|
|
@ -262,13 +262,26 @@ export class Flow {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FLOW_FILTER_SCOPES = ['url', 'method', 'code', 'header', 'reqheader', 'resheader', 'body', 'reqbody', 'resbody', 'all'] as const
|
|
|
|
|
|
|
|
type FlowFilterScope = typeof FLOW_FILTER_SCOPES[number]
|
|
|
|
|
|
|
|
|
|
|
|
class FlowFilter {
|
|
|
|
class FlowFilter {
|
|
|
|
private keyword: string | RegExp | undefined
|
|
|
|
private keyword: string | RegExp | undefined
|
|
|
|
|
|
|
|
private scope: FlowFilterScope = 'url'
|
|
|
|
|
|
|
|
|
|
|
|
constructor(text: string) {
|
|
|
|
constructor(text: string) {
|
|
|
|
text = text.trim()
|
|
|
|
text = text.trim()
|
|
|
|
if (!text) return
|
|
|
|
if (!text) return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const scope of FLOW_FILTER_SCOPES) {
|
|
|
|
|
|
|
|
if (text.startsWith(`${scope}:`)) {
|
|
|
|
|
|
|
|
this.scope = scope
|
|
|
|
|
|
|
|
text = text.replace(`${scope}:`, '').trim()
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!text) return
|
|
|
|
|
|
|
|
|
|
|
|
// regexp
|
|
|
|
// regexp
|
|
|
|
if (text.startsWith('/') && (text.endsWith('/') || text.endsWith('/i'))) {
|
|
|
|
if (text.startsWith('/') && (text.endsWith('/') || text.endsWith('/i'))) {
|
|
|
|
let flags: string | undefined
|
|
|
|
let flags: string | undefined
|
|
|
@ -286,10 +299,90 @@ class FlowFilter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match(flow: Flow): boolean {
|
|
|
|
public match(flow: Flow): boolean {
|
|
|
|
|
|
|
|
switch (this.scope) {
|
|
|
|
|
|
|
|
case 'url':
|
|
|
|
|
|
|
|
return this.matchUrl(flow)
|
|
|
|
|
|
|
|
case 'method':
|
|
|
|
|
|
|
|
return this.matchMethod(flow)
|
|
|
|
|
|
|
|
case 'code':
|
|
|
|
|
|
|
|
return this.matchCode(flow)
|
|
|
|
|
|
|
|
case 'reqheader':
|
|
|
|
|
|
|
|
return this.matchReqHeader(flow)
|
|
|
|
|
|
|
|
case 'resheader':
|
|
|
|
|
|
|
|
return this.matchResHeader(flow)
|
|
|
|
|
|
|
|
case 'header':
|
|
|
|
|
|
|
|
return this.matchHeader(flow)
|
|
|
|
|
|
|
|
case 'reqbody':
|
|
|
|
|
|
|
|
return this.matchReqBody(flow)
|
|
|
|
|
|
|
|
case 'resbody':
|
|
|
|
|
|
|
|
return this.matchResBody(flow)
|
|
|
|
|
|
|
|
case 'body':
|
|
|
|
|
|
|
|
return this.matchBody(flow)
|
|
|
|
|
|
|
|
case 'all':
|
|
|
|
|
|
|
|
return this.matchAll(flow)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw new Error(`invalid scope ${this.scope}`)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchUrl(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this.matchKeyword(flow.request.url)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchMethod(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this.matchKeyword(flow.request.method) || this.matchKeyword(flow.request.method.toLowerCase())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchCode(flow: Flow): boolean {
|
|
|
|
|
|
|
|
if (!flow.response) return false
|
|
|
|
|
|
|
|
return this.matchKeyword(flow.response.statusCode.toString())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _matchHeader(header: Header): boolean {
|
|
|
|
|
|
|
|
return Object.entries(header).some(([key, vals]) => {
|
|
|
|
|
|
|
|
return [key].concat(vals).some(text => this.matchKeyword(text))
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchReqHeader(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this._matchHeader(flow.request.header)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchResHeader(flow: Flow): boolean {
|
|
|
|
|
|
|
|
if (!flow.response) return false
|
|
|
|
|
|
|
|
return this._matchHeader(flow.response.header)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchHeader(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this.matchReqHeader(flow) || this.matchResHeader(flow)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchReqBody(flow: Flow): boolean {
|
|
|
|
|
|
|
|
const body = flow.requestBody()
|
|
|
|
|
|
|
|
if (!body) return false
|
|
|
|
|
|
|
|
return this.matchKeyword(body)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchResBody(flow: Flow): boolean {
|
|
|
|
|
|
|
|
const body = flow.responseBody()
|
|
|
|
|
|
|
|
if (!body) return false
|
|
|
|
|
|
|
|
return this.matchKeyword(body)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchBody(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this.matchReqBody(flow) || this.matchResBody(flow)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchAll(flow: Flow): boolean {
|
|
|
|
|
|
|
|
return this.matchUrl(flow) || this.matchMethod(flow) || this.matchHeader(flow) || this.matchBody(flow)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private matchKeyword(text: string): boolean {
|
|
|
|
if (!this.keyword) return true
|
|
|
|
if (!this.keyword) return true
|
|
|
|
if (this.keyword instanceof RegExp) return this.keyword.test(flow.request.url)
|
|
|
|
if (!text) return false
|
|
|
|
return flow.request.url.includes(this.keyword)
|
|
|
|
if (this.keyword instanceof RegExp) return this.keyword.test(text)
|
|
|
|
|
|
|
|
return text.includes(this.keyword)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -330,10 +423,6 @@ export class FlowManager {
|
|
|
|
return this._map.get(id)
|
|
|
|
return this._map.get(id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
changeFilter(text: string) {
|
|
|
|
|
|
|
|
this.flowFilter = new FlowFilter(text)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
changeFilterLazy(text: string, callback: (err: any) => void) {
|
|
|
|
changeFilterLazy(text: string, callback: (err: any) => void) {
|
|
|
|
if (this.filterTimer) {
|
|
|
|
if (this.filterTimer) {
|
|
|
|
clearTimeout(this.filterTimer)
|
|
|
|
clearTimeout(this.filterTimer)
|
|
|
@ -342,10 +431,10 @@ export class FlowManager {
|
|
|
|
|
|
|
|
|
|
|
|
this.filterTimer = setTimeout(() => {
|
|
|
|
this.filterTimer = setTimeout(() => {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
this.changeFilter(text)
|
|
|
|
this.flowFilter = new FlowFilter(text)
|
|
|
|
callback(null)
|
|
|
|
callback(null)
|
|
|
|
} catch (err) {
|
|
|
|
} catch (err) {
|
|
|
|
this.changeFilter('')
|
|
|
|
this.flowFilter = undefined
|
|
|
|
callback(err)
|
|
|
|
callback(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 300) as any
|
|
|
|
}, 300) as any
|
|
|
|