Skip to content

Commit

Permalink
Expose route for context (#1128)
Browse files Browse the repository at this point in the history
* Expose route for context

* Update documantation

* Use a protocol-independent route
  • Loading branch information
xxx7xxxx committed Nov 8, 2023
1 parent e98c986 commit 1c0e586
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 13 deletions.
4 changes: 1 addition & 3 deletions docs/07.Reference/7.02.Filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -1402,7 +1402,6 @@ flow:
filters:
- name: redirectorv2
kind: RedirectorV2
pathPrefix: /user
path:
type: ReplacePrefixMatch
replacePrefixMatch: /account
Expand Down Expand Up @@ -1441,7 +1440,6 @@ filters:
kind: RedirectorV2
scheme: https
hostname: newdomain.com
pathPrefix: /user
path:
type: ReplacePrefixMatch
replacePrefixMatch: /account
Expand Down
15 changes: 15 additions & 0 deletions pkg/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Context struct {

activeNs string

route protocols.Route
requests map[string]*requestRef
responses map[string]*responseRef

Expand All @@ -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
Expand Down
25 changes: 19 additions & 6 deletions pkg/filters/redirectorv2/redirectorv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/v1"
Expand Down Expand Up @@ -76,7 +77,6 @@ type (
Spec struct {
filters.BaseSpec `json:",inline"`

PathPrefix *string `json:"pathPrefix,omitempty"`
gwapis.HTTPRequestRedirectFilter `json:",inline"`
}
)
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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))
}
}

Expand Down
15 changes: 12 additions & 3 deletions pkg/filters/redirectorv2/redirectorv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/v1"
)

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
Expand Down Expand Up @@ -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",
},
}

Expand Down Expand Up @@ -169,7 +177,6 @@ func TestRedirectorHandle2(t *testing.T) {
name: "Redirect prefix",
reqURL: "http:https://localhost/user/data/profile",
spec: &Spec{
PathPrefix: new(string),
HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{
Path: &gwapis.HTTPPathModifier{
Type: gwapis.PrefixMatchHTTPPathModifier,
Expand Down Expand Up @@ -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/"
}

Expand Down
5 changes: 4 additions & 1 deletion pkg/object/httpserver/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
4 changes: 4 additions & 0 deletions pkg/object/httpserver/routers/ordered/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/object/httpserver/routers/radixtree/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 13 additions & 0 deletions pkg/object/httpserver/routers/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/http"
"net/url"

"github.com/megaease/easegress/v2/pkg/protocols"
"github.com/megaease/easegress/v2/pkg/protocols/httpprot"
)

Expand All @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions pkg/object/httpserver/routers/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
7 changes: 7 additions & 0 deletions pkg/protocols/protocols.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 1c0e586

Please sign in to comment.