Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: use ast.Value and []string in request input #676

Merged
merged 1 commit into from
Apr 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions envoyauth/ast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package envoyauth

import (
"github.com/open-policy-agent/opa/v1/ast"
)

func keyValue(k, v string) [2]*ast.Term {
return [2]*ast.Term{{Value: ast.String(k)}, {Value: ast.String(v)}}
}
38 changes: 19 additions & 19 deletions envoyauth/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ import (
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/dynamicpb"

"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/logging"
"github.com/open-policy-agent/opa/v1/util"
)

var v2Info = map[string]string{"ext_authz": "v2", "encoding": "encoding/json"}
var v3Info = map[string]string{"ext_authz": "v3", "encoding": "protojson"}
var v2Info = ast.NewObject(
keyValue("ext_authz", "v2"),
keyValue("encoding", "encoding/json"),
)
var v3Info = ast.NewObject(
keyValue("ext_authz", "v3"),
keyValue("encoding", "protojson"),
)

// RequestToInput - Converts a CheckRequest in either protobuf 2 or 3 to an input map
func RequestToInput(req interface{}, logger logging.Logger, protoSet *protoregistry.Files, skipRequestBodyParse bool) (map[string]interface{}, error) {
Expand All @@ -33,7 +40,8 @@ func RequestToInput(req interface{}, logger logging.Logger, protoSet *protoregis

var rawBody []byte
var path, body string
var headers, version map[string]string
var headers map[string]string
var version ast.Value

// NOTE: The path/body/headers blocks look silly, but they allow us to retrieve
// the parts of the incoming request we care about, without having to convert
Expand Down Expand Up @@ -84,32 +92,24 @@ func RequestToInput(req interface{}, logger logging.Logger, protoSet *protoregis
return input, nil
}

func getParsedPathAndQuery(path string) ([]interface{}, map[string]interface{}, error) {
func getParsedPathAndQuery(path string) ([]string, map[string]any, error) {
parsedURL, err := url.Parse(path)
if err != nil {
return nil, nil, err
}

parsedPath := strings.Split(strings.TrimLeft(parsedURL.Path, "/"), "/")
parsedPathInterface := make([]interface{}, len(parsedPath))
for i, v := range parsedPath {
parsedPathInterface[i] = v
}

query := parsedURL.Query()
parsedQueryInterface := make(map[string]interface{}, len(query))
parsedQueryInterface := make(map[string]any, len(query))
for paramKey, paramValues := range query {
queryValues := make([]interface{}, len(paramValues))
for i, v := range paramValues {
queryValues[i] = v
}
parsedQueryInterface[paramKey] = queryValues
parsedQueryInterface[paramKey] = paramValues
}

return parsedPathInterface, parsedQueryInterface, nil
return parsedPath, parsedQueryInterface, nil
}

func getParsedBody(logger logging.Logger, headers map[string]string, body string, rawBody []byte, parsedPath []interface{}, protoSet *protoregistry.Files) (interface{}, bool, error) {
func getParsedBody(logger logging.Logger, headers map[string]string, body string, rawBody []byte, parsedPath []string, protoSet *protoregistry.Files) (interface{}, bool, error) {
var data interface{}

if val, ok := headers["content-type"]; ok {
Expand Down Expand Up @@ -269,7 +269,7 @@ func getParsedBody(logger logging.Logger, headers map[string]string, body string
return data, false, nil
}

func getGRPCBody(logger logging.Logger, in []byte, parsedPath []interface{}, data interface{}, files *protoregistry.Files) (found, truncated bool, _ error) {
func getGRPCBody(logger logging.Logger, in []byte, parsedPath []string, data interface{}, files *protoregistry.Files) (found, truncated bool, _ error) {

// the first 5 bytes are part of gRPC framing. We need to remove them to be able to parse
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
Expand All @@ -294,12 +294,12 @@ func getGRPCBody(logger logging.Logger, in []byte, parsedPath []interface{}, dat
in = in[5 : size+5]

// Note: we've already checked that len(path)>=2
svc, err := findService(parsedPath[0].(string), files)
svc, err := findService(parsedPath[0], files)
if err != nil {
logger.WithFields(map[string]interface{}{"err": err}).Debug("could not find service")
return false, false, nil
}
msgDesc, err := findMessageInputDesc(parsedPath[1].(string), svc)
msgDesc, err := findMessageInputDesc(parsedPath[1], svc)
if err != nil {
logger.WithFields(map[string]interface{}{"err": err}).Debug("could not find message")
return false, false, nil
Expand Down
26 changes: 13 additions & 13 deletions envoyauth/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ func TestGetParsedBody(t *testing.T) {

logger := logging.NewNoOpLogger()
req := createCheckRequest(requestContentTypeJSONInvalid)
path := []interface{}{}
path := []string{}
protoSet := (*protoregistry.Files)(nil)
headers, body := req.GetAttributes().GetRequest().GetHttp().GetHeaders(), req.GetAttributes().GetRequest().GetHttp().GetBody()
_, _, err := getParsedBody(logger, headers, body, nil, path, protoSet)
Expand Down Expand Up @@ -617,38 +617,38 @@ func TestGetParsedBodygRPC(t *testing.T) {
func TestParsedPathAndQuery(t *testing.T) {
var tests = []struct {
request *ext_authz.CheckRequest
expectedPath []interface{}
expectedPath []string
expectedQuery map[string]interface{}
}{
{
createExtReqWithPath("/my/test/path"),
[]interface{}{"my", "test", "path"},
[]string{"my", "test", "path"},
map[string]interface{}{},
},
{
createExtReqWithPath("/my/test/path?a=1"),
[]interface{}{"my", "test", "path"},
map[string]interface{}{"a": []interface{}{"1"}},
[]string{"my", "test", "path"},
map[string]interface{}{"a": []string{"1"}},
},
{
createExtReqWithPath("/my/test/path?a=1&a=2"),
[]interface{}{"my", "test", "path"},
map[string]interface{}{"a": []interface{}{"1", "2"}},
[]string{"my", "test", "path"},
map[string]interface{}{"a": []string{"1", "2"}},
},
{
createExtReqWithPath("/my/test/path?a=1&b=2"),
[]interface{}{"my", "test", "path"},
map[string]interface{}{"a": []interface{}{"1"}, "b": []interface{}{"2"}},
[]string{"my", "test", "path"},
map[string]interface{}{"a": []string{"1"}, "b": []string{"2"}},
},
{
createExtReqWithPath("/my/test/path?a=1&a=new%0aline"),
[]interface{}{"my", "test", "path"},
map[string]interface{}{"a": []interface{}{"1", "new\nline"}},
[]string{"my", "test", "path"},
map[string]interface{}{"a": []string{"1", "new\nline"}},
},
{
createExtReqWithPath("%2Fmy%2Ftest%2Fpath?a=1&a=new%0aline"),
[]interface{}{"my", "test", "path"},
map[string]interface{}{"a": []interface{}{"1", "new\nline"}},
[]string{"my", "test", "path"},
map[string]interface{}{"a": []string{"1", "new\nline"}},
},
}

Expand Down