diff --git a/docs/07.Reference/7.02.Filters.md b/docs/07.Reference/7.02.Filters.md index 254093cdf6..a5176e65ab 100644 --- a/docs/07.Reference/7.02.Filters.md +++ b/docs/07.Reference/7.02.Filters.md @@ -1392,7 +1392,7 @@ Commmon Use-Cases: 1. Replace Prefix: -Substitute a prefix with `/account` and return a `302 Found` status code. +Substitute a prefix with `/account` and return a `302 Found` status code. The prefix was decided in `pathPrefix` in te matchting rule in HTTPServer. ```yaml name: demo-pipeline @@ -1402,7 +1402,6 @@ flow: filters: - name: redirectorv2 kind: RedirectorV2 - pathPrefix: /user path: type: ReplacePrefixMatch replacePrefixMatch: /account @@ -1441,7 +1440,6 @@ filters: kind: RedirectorV2 scheme: https hostname: newdomain.com - pathPrefix: /user path: type: ReplacePrefixMatch replacePrefixMatch: /account diff --git a/pkg/context/context.go b/pkg/context/context.go index 8c59176cf1..e5337f30e3 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -73,6 +73,7 @@ type Context struct { activeNs string + route protocols.Route requests map[string]*requestRef responses map[string]*responseRef @@ -92,6 +93,20 @@ func New(span *tracing.Span) *Context { return ctx } +// SetRoute sets the route. +func (ctx *Context) SetRoute(route protocols.Route) { + ctx.route = route +} + +// GetRoute returns the route with the existing flag. +func (ctx *Context) GetRoute() (protocols.Route, bool) { + if ctx.route == nil { + return nil, false + } + + return ctx.route, true +} + // Span returns the span of this Context. func (ctx *Context) Span() *tracing.Span { return ctx.span diff --git a/pkg/filters/redirectorv2/redirectorv2.go b/pkg/filters/redirectorv2/redirectorv2.go index 9f4ccad89e..583895dd97 100644 --- a/pkg/filters/redirectorv2/redirectorv2.go +++ b/pkg/filters/redirectorv2/redirectorv2.go @@ -25,6 +25,7 @@ import ( "github.com/megaease/easegress/v2/pkg/context" "github.com/megaease/easegress/v2/pkg/filters" + httprouters "github.com/megaease/easegress/v2/pkg/object/httpserver/routers" "github.com/megaease/easegress/v2/pkg/protocols/httpprot" gwapis "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -76,7 +77,6 @@ type ( Spec struct { filters.BaseSpec `json:",inline"` - PathPrefix *string `json:"pathPrefix,omitempty"` gwapis.HTTPRequestRedirectFilter `json:",inline"` } ) @@ -108,10 +108,6 @@ func (s *Spec) Validate() error { return errors.New("invalid path of Redirector, replaceFullPath can't be empty") } case gwapis.PrefixMatchHTTPPathModifier: - if s.PathPrefix == nil || *s.PathPrefix == "" { - return errors.New("invalid path of Redirector, pathPrefix can't be empty") - } - if s.Path.ReplacePrefixMatch == nil { return errors.New("invalid path of Redirector, replacePrefixMatch can't be empty") } @@ -178,8 +174,25 @@ func (r *Redirector) Handle(ctx *context.Context) string { case gwapis.FullPathHTTPPathModifier: redirectURL.Path = string(*r.spec.Path.ReplaceFullPath) case gwapis.PrefixMatchHTTPPathModifier: + route, existed := ctx.GetRoute() + if !existed { + ctx.AddTag("route not found") + break + } + + if route.Protocol() != "http" { + ctx.AddTag("route is not an http route") + break + } + + prefix := route.(httprouters.Route).GetPathPrefix() + if prefix == "" { + ctx.AddTag("route has no path prefix") + break + } + redirectURL.Path = r.subPrefix(redirectURL.Path, - *r.spec.PathPrefix, string(*r.spec.Path.ReplacePrefixMatch)) + prefix, string(*r.spec.Path.ReplacePrefixMatch)) } } diff --git a/pkg/filters/redirectorv2/redirectorv2_test.go b/pkg/filters/redirectorv2/redirectorv2_test.go index 4e455c9af8..f46b61fda4 100644 --- a/pkg/filters/redirectorv2/redirectorv2_test.go +++ b/pkg/filters/redirectorv2/redirectorv2_test.go @@ -22,12 +22,20 @@ import ( "testing" "github.com/megaease/easegress/v2/pkg/context" + "github.com/megaease/easegress/v2/pkg/object/httpserver/routers" "github.com/megaease/easegress/v2/pkg/protocols/httpprot" "github.com/stretchr/testify/assert" gwapis "sigs.k8s.io/gateway-api/apis/v1beta1" ) +type fakeMuxPath struct { + routers.Path +} + +func (mp *fakeMuxPath) Protocol() string { return "http" } +func (mp *fakeMuxPath) Rewrite(context *routers.RouteContext) {} + func TestSpecValidate(t *testing.T) { tests := []struct { name string @@ -123,7 +131,7 @@ func TestSpecValidate(t *testing.T) { }, }, expectErr: true, - errMsg: "invalid path of Redirector, pathPrefix can't be empty", + errMsg: "invalid path of Redirector, replacePrefixMatch can't be empty", }, } @@ -169,7 +177,6 @@ func TestRedirectorHandle2(t *testing.T) { name: "Redirect prefix", reqURL: "http://localhost/user/data/profile", spec: &Spec{ - PathPrefix: new(string), HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ Path: &gwapis.HTTPPathModifier{ Type: gwapis.PrefixMatchHTTPPathModifier, @@ -232,13 +239,15 @@ func TestRedirectorHandle2(t *testing.T) { req, _ := httpprot.NewRequest(stdReq) ctx := context.New(nil) ctx.SetInputRequest(req) + ctx.SetRoute(&fakeMuxPath{ + Path: routers.Path{PathPrefix: "/user/"}, + }) if tt.spec.Path != nil && tt.spec.Path.ReplaceFullPath != nil { *tt.spec.Path.ReplaceFullPath = "/newpath" } if tt.spec.Path != nil && tt.spec.Path.ReplacePrefixMatch != nil { - *tt.spec.PathPrefix = "/user/" *tt.spec.Path.ReplacePrefixMatch = "/account/" } diff --git a/pkg/object/httpserver/mux.go b/pkg/object/httpserver/mux.go index 87e62e53ed..47283a15c3 100644 --- a/pkg/object/httpserver/mux.go +++ b/pkg/object/httpserver/mux.go @@ -129,7 +129,8 @@ func (mi *muxInstance) putRouteToCache(req *httpprot.Request, rc *cachedRoute) { } func newMux(httpStat *httpstat.HTTPStat, topN *httpstat.TopN, - metrics *metrics, mapper context.MuxMapper) *mux { + metrics *metrics, mapper context.MuxMapper, +) *mux { m := &mux{ httpStat: httpStat, topN: topN, @@ -267,6 +268,8 @@ func (mi *muxInstance) serveHTTP(stdw http.ResponseWriter, stdr *http.Request) { routeCtx := routers.NewContext(req) route := mi.search(routeCtx) + ctx.SetRoute(route.route) + var respHeader http.Header defer func() { diff --git a/pkg/object/httpserver/routers/ordered/router.go b/pkg/object/httpserver/routers/ordered/router.go index 45dfddbaf3..37e0655480 100644 --- a/pkg/object/httpserver/routers/ordered/router.go +++ b/pkg/object/httpserver/routers/ordered/router.go @@ -86,6 +86,10 @@ func newMuxPath(p *routers.Path) *muxPath { } } +func (mp *muxPath) Protocol() string { + return "http" +} + func (mp *muxPath) matchPath(path string) bool { if mp.Path.Path == "" && mp.PathPrefix == "" && mp.pathRE == nil { return true diff --git a/pkg/object/httpserver/routers/radixtree/router.go b/pkg/object/httpserver/routers/radixtree/router.go index 83f3cfd5d9..3a2373c447 100644 --- a/pkg/object/httpserver/routers/radixtree/router.go +++ b/pkg/object/httpserver/routers/radixtree/router.go @@ -485,6 +485,10 @@ func (mp *muxPath) initRewrite() { mp.rewriteTemplate = template.Must(template.New("").Parse(repl)) } +func (mp *muxPath) Protocol() string { + return "http" +} + func (mp *muxPath) Rewrite(context *routers.RouteContext) { req := context.Request diff --git a/pkg/object/httpserver/routers/routers.go b/pkg/object/httpserver/routers/routers.go index c3e1d6131f..8bfeccbc36 100644 --- a/pkg/object/httpserver/routers/routers.go +++ b/pkg/object/httpserver/routers/routers.go @@ -25,6 +25,7 @@ import ( "net/http" "net/url" + "github.com/megaease/easegress/v2/pkg/protocols" "github.com/megaease/easegress/v2/pkg/protocols/httpprot" ) @@ -49,12 +50,24 @@ type ( // Route is the corresponding route interface for different routing policies. Route interface { + protocols.Route + // Rewrite for path rewriting. Rewrite(context *RouteContext) // GetBackend is used to get the backend corresponding to the route. GetBackend() string // GetClientMaxBodySize is used to get the clientMaxBodySize corresponding to the route. GetClientMaxBodySize() int64 + + // NOTE: Currently we only support path information in readonly. + // Without further requirements, we choose not to expose too much information. + + // GetExactPath is used to get the exact path corresponding to the route. + GetExactPath() string + // GetPathPrefix is used to get the path prefix corresponding to the route. + GetPathPrefix() string + // GetPathRegexp is used to get the path regexp corresponding to the route. + GetPathRegexp() string } // Params are used to store the variables in the search path and their corresponding values. diff --git a/pkg/object/httpserver/routers/spec.go b/pkg/object/httpserver/routers/spec.go index 1647df72bc..8799cbeab7 100644 --- a/pkg/object/httpserver/routers/spec.go +++ b/pkg/object/httpserver/routers/spec.go @@ -252,6 +252,21 @@ func (p *Path) GetClientMaxBodySize() int64 { return p.ClientMaxBodySize } +// GetExactPath returns the exact path of the route. +func (p *Path) GetExactPath() string { + return p.Path +} + +// GetPathPrefix returns the path prefix of the route. +func (p *Path) GetPathPrefix() string { + return p.PathPrefix +} + +// GetPathRegexp returns the path regexp of the route. +func (p *Path) GetPathRegexp() string { + return p.PathRegexp +} + func (hs Headers) init() { for _, h := range hs { if h.Regexp != "" { diff --git a/pkg/protocols/protocols.go b/pkg/protocols/protocols.go index 3b9560ab30..9d0b285504 100644 --- a/pkg/protocols/protocols.go +++ b/pkg/protocols/protocols.go @@ -150,3 +150,10 @@ type Protocol interface { NewResponseInfo() interface{} BuildResponse(respInfo interface{}) (Response, error) } + +// Route is the interface of a route. +// Filters could assert the real type according to the protocol. +type Route interface { + // Protocol returns the canonical name of the protocol in lower case, such as http, grpc. + Protocol() string +}