Skip to content

Commit

Permalink
Add filter support for HTTP headers
Browse files Browse the repository at this point in the history
  • Loading branch information
dstotijn committed Mar 31, 2022
1 parent fd27955 commit 2ce4218
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
3 changes: 3 additions & 0 deletions admin/src/features/reqlog/components/Search.tsx
Expand Up @@ -76,6 +76,7 @@ function Search(): JSX.Element {
<ClickAwayListener onClickAway={handleClickAway}>
<Paper
component="form"
autoComplete="off"
onSubmit={handleSubmit}
ref={filterRef}
sx={{
Expand Down Expand Up @@ -109,6 +110,8 @@ function Search(): JSX.Element {
value={searchExpr}
onChange={(e) => setSearchExpr(e.target.value)}
onFocus={() => setFilterOpen(true)}
autoCorrect="false"
spellCheck="false"
/>
<Tooltip title="Search">
<IconButton type="submit" sx={{ padding: 1.25 }}>
Expand Down
18 changes: 18 additions & 0 deletions pkg/proxy/intercept/filter.go
Expand Up @@ -133,6 +133,15 @@ func matchReqInfixExpr(req *http.Request, expr search.InfixExpression) (bool, er
return false, fmt.Errorf("failed to get string literal from request for left operand: %w", err)
}

if leftVal == "headers" {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, req.Header)
if err != nil {
return false, fmt.Errorf("failed to match request HTTP headers: %w", err)
}

return match, nil
}

if expr.Operator == search.TokOpRe || expr.Operator == search.TokOpNotRe {
right, ok := expr.Right.(search.RegexpLiteral)
if !ok {
Expand Down Expand Up @@ -324,6 +333,15 @@ func matchResInfixExpr(res *http.Response, expr search.InfixExpression) (bool, e
return false, fmt.Errorf("failed to get string literal from response for left operand: %w", err)
}

if leftVal == "headers" {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, res.Header)
if err != nil {
return false, fmt.Errorf("failed to match request HTTP headers: %w", err)
}

return match, nil
}

if expr.Operator == search.TokOpRe || expr.Operator == search.TokOpNotRe {
right, ok := expr.Right.(search.RegexpLiteral)
if !ok {
Expand Down
18 changes: 18 additions & 0 deletions pkg/reqlog/search.go
Expand Up @@ -98,6 +98,24 @@ func (reqLog RequestLog) matchInfixExpr(expr search.InfixExpression) (bool, erro

leftVal := reqLog.getMappedStringLiteral(left.Value)

if leftVal == "req.headers" {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, reqLog.Header)
if err != nil {
return false, fmt.Errorf("failed to match request HTTP headers: %w", err)
}

return match, nil
}

if leftVal == "res.headers" && reqLog.Response != nil {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, reqLog.Response.Header)
if err != nil {
return false, fmt.Errorf("failed to match response HTTP headers: %w", err)
}

return match, nil
}

if expr.Operator == search.TokOpRe || expr.Operator == search.TokOpNotRe {
right, ok := expr.Right.(search.RegexpLiteral)
if !ok {
Expand Down
82 changes: 82 additions & 0 deletions pkg/search/http.go
@@ -0,0 +1,82 @@
package search

import (
"errors"
"fmt"
"net/http"
)

func MatchHTTPHeaders(op TokenType, expr Expression, headers http.Header) (bool, error) {
if headers == nil {
return false, nil
}

switch op {
case TokOpEq:
strLiteral, ok := expr.(StringLiteral)
if !ok {
return false, errors.New("search: expression must be a string literal")
}

// Return `true` if at least one header (<key>: <value>) is equal to the string literal.
for key, values := range headers {
for _, value := range values {
if strLiteral.Value == fmt.Sprintf("%v: %v", key, value) {
return true, nil
}
}
}

return false, nil
case TokOpNotEq:
strLiteral, ok := expr.(StringLiteral)
if !ok {
return false, errors.New("search: expression must be a string literal")
}

// Return `true` if none of the headers (<key>: <value>) are equal to the string literal.
for key, values := range headers {
for _, value := range values {
if strLiteral.Value == fmt.Sprintf("%v: %v", key, value) {
return false, nil
}
}
}

return true, nil
case TokOpRe:
re, ok := expr.(RegexpLiteral)
if !ok {
return false, errors.New("search: expression must be a regular expression")
}

// Return `true` if at least one header (<key>: <value>) matches the regular expression.
for key, values := range headers {
for _, value := range values {
if re.MatchString(fmt.Sprintf("%v: %v", key, value)) {
return true, nil
}
}
}

return false, nil
case TokOpNotRe:
re, ok := expr.(RegexpLiteral)
if !ok {
return false, errors.New("search: expression must be a regular expression")
}

// Return `true` if none of the headers (<key>: <value>) match the regular expression.
for key, values := range headers {
for _, value := range values {
if re.MatchString(fmt.Sprintf("%v: %v", key, value)) {
return false, nil
}
}
}

return true, nil
default:
return false, fmt.Errorf("search: unsupported operator %q", op.String())
}
}
18 changes: 18 additions & 0 deletions pkg/sender/search.go
Expand Up @@ -91,6 +91,24 @@ func (req Request) matchInfixExpr(expr search.InfixExpression) (bool, error) {

leftVal := req.getMappedStringLiteral(left.Value)

if leftVal == "req.headers" {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, req.Header)
if err != nil {
return false, fmt.Errorf("failed to match request HTTP headers: %w", err)
}

return match, nil
}

if leftVal == "res.headers" && req.Response != nil {
match, err := search.MatchHTTPHeaders(expr.Operator, expr.Right, req.Response.Header)
if err != nil {
return false, fmt.Errorf("failed to match response HTTP headers: %w", err)
}

return match, nil
}

if expr.Operator == search.TokOpRe || expr.Operator == search.TokOpNotRe {
right, ok := expr.Right.(search.RegexpLiteral)
if !ok {
Expand Down

0 comments on commit 2ce4218

Please sign in to comment.