diff --git a/pkg/provider/kubernetes/gateway/grpcroute.go b/pkg/provider/kubernetes/gateway/grpcroute.go index 835de1bf7..42826a8bb 100644 --- a/pkg/provider/kubernetes/gateway/grpcroute.go +++ b/pkg/provider/kubernetes/gateway/grpcroute.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net" + "regexp" "strconv" "strings" @@ -394,14 +395,24 @@ func buildGRPCMethodRule(method *gatev1.GRPCMethodMatch) string { return `PathPrefix("/")` } + isExact := method.Type == nil || *method.Type == gatev1.GRPCMethodMatchExact + sExpr := "[^/]+" if s := ptr.Deref(method.Service, ""); s != "" { - sExpr = s + if isExact { + sExpr = regexp.QuoteMeta(s) + } else { + sExpr = s + } } mExpr := "[^/]+" if m := ptr.Deref(method.Method, ""); m != "" { - mExpr = m + if isExact { + mExpr = regexp.QuoteMeta(m) + } else { + mExpr = m + } } return fmt.Sprintf("PathRegexp(%q)", fmt.Sprintf("/%s/%s", sExpr, mExpr)) diff --git a/pkg/provider/kubernetes/gateway/grpcroute_test.go b/pkg/provider/kubernetes/gateway/grpcroute_test.go index fc70dd587..deb40abec 100644 --- a/pkg/provider/kubernetes/gateway/grpcroute_test.go +++ b/pkg/provider/kubernetes/gateway/grpcroute_test.go @@ -118,15 +118,6 @@ func Test_buildGRPCMethodRule(t *testing.T) { }, expectedRule: `PathRegexp("/[^/]+/bar")`, }, - { - desc: "Exact service and method matching", - method: &gatev1.GRPCMethodMatch{ - Type: ptr.To(gatev1.GRPCMethodMatchExact), - Service: ptr.To("foo"), - Method: ptr.To("bar"), - }, - expectedRule: `PathRegexp("/foo/bar")`, - }, { desc: "Regexp service matching", method: &gatev1.GRPCMethodMatch{ @@ -152,6 +143,40 @@ func Test_buildGRPCMethodRule(t *testing.T) { }, expectedRule: `PathRegexp("/[^1-9/]/[^1-9/]")`, }, + { + desc: "Exact type with dot in service name escapes dot", + method: &gatev1.GRPCMethodMatch{ + Type: ptr.To(gatev1.GRPCMethodMatchExact), + Service: ptr.To("foo.bar"), + Method: ptr.To("Method"), + }, + expectedRule: `PathRegexp("/foo\\.bar/Method")`, + }, + { + desc: "Nil type defaults to exact and escapes dot", + method: &gatev1.GRPCMethodMatch{ + Type: nil, + Service: ptr.To("auth.api"), + Method: ptr.To("Login"), + }, + expectedRule: `PathRegexp("/auth\\.api/Login")`, + }, + { + desc: "RegularExpression type preserves dot as regex wildcard", + method: &gatev1.GRPCMethodMatch{ + Type: ptr.To(gatev1.GRPCMethodMatchRegularExpression), + Service: ptr.To("foo.bar"), + Method: ptr.To(".*"), + }, + expectedRule: `PathRegexp("/foo.bar/.*")`, + }, + { + desc: "Exact type with neither service nor method uses full wildcard", + method: &gatev1.GRPCMethodMatch{ + Type: ptr.To(gatev1.GRPCMethodMatchExact), + }, + expectedRule: `PathRegexp("/[^/]+/[^/]+")`, + }, } for _, test := range testCases {