From 291489c66fa419451aeb798fe3b4364e2952acc7 Mon Sep 17 00:00:00 2001 From: Yun Long Date: Fri, 27 Oct 2023 18:15:59 +0800 Subject: [PATCH] Add filter RedirectorV2 --- docs/07.Reference/7.02.Filters.md | 118 ++++++-- pkg/filters/redirectorv2/redirectorv2.go | 232 +++++++++++++++ pkg/filters/redirectorv2/redirectorv2_test.go | 269 ++++++++++++++++++ pkg/object/gatewaycontroller/translator.go | 36 +-- 4 files changed, 605 insertions(+), 50 deletions(-) create mode 100644 pkg/filters/redirectorv2/redirectorv2.go create mode 100644 pkg/filters/redirectorv2/redirectorv2_test.go diff --git a/docs/07.Reference/7.02.Filters.md b/docs/07.Reference/7.02.Filters.md index 460faaec63..254093cdf6 100644 --- a/docs/07.Reference/7.02.Filters.md +++ b/docs/07.Reference/7.02.Filters.md @@ -70,9 +70,12 @@ - [Redirector](#redirector) - [Configuration](#configuration-22) - [Results](#results-22) -- [GRPCProxy](#grpcproxy) +- [RedirectorV2](#redirectorv2) - [Configuration](#configuration-23) - [Results](#results-23) +- [GRPCProxy](#grpcproxy) + - [Configuration](#configuration-24) + - [Results](#results-24) - [Common Types](#common-types) - [pathadaptor.Spec](#pathadaptorspec) - [pathadaptor.RegexpReplace](#pathadaptorregexpreplace) @@ -983,7 +986,7 @@ HeaderLookup has no results. ## ResultBuilder -ResultBuilder generates a string, which will be the result of the filter. +ResultBuilder generates a string, which will be the result of the filter. This filter exists to work with the [`jumpIf` mechanism](7.01.Controllers.md#pipeline) for conditional jumping. Currently, the result string can only be `result0` - `result9`, this will be @@ -1139,10 +1142,10 @@ After OIDCAdaptor handled, following OIDC related information can be obtained fr ## OPAFilter -The [Open Policy Agent (OPA)](https://www.openpolicyagent.org/docs/latest/) is an open source, -general-purpose policy engine that unifies policy enforcement across the stack. It provides a -high-level declarative language, which can be used to define and enforce policies in -Easegress API Gateway. Currently, there are 160+ built-in operators and functions we can use, +The [Open Policy Agent (OPA)](https://www.openpolicyagent.org/docs/latest/) is an open source, +general-purpose policy engine that unifies policy enforcement across the stack. It provides a +high-level declarative language, which can be used to define and enforce policies in +Easegress API Gateway. Currently, there are 160+ built-in operators and functions we can use, for examples `net.cidr_contains` and `contains`. ```yaml @@ -1163,8 +1166,8 @@ filters: allow { input.request.method == "POST" input.request.scheme == "https" - contains(input.request.path, "/") - net.cidr_contains("127.0.0.0/24",input.request.realIP) + contains(input.request.path, "/") + net.cidr_contains("127.0.0.0/24",input.request.realIP) } ``` @@ -1178,7 +1181,7 @@ The following table lists input request fields that can be used in an OPA policy | input.request.raw_query | string | The current http request raw query | "a=1&b=2&c=3" | | input.request.query | map | The current http request query map | {"a":1,"b":2,"c":3} | | input.request.headers | map | The current http request header map targeted by
includedHeaders | {"Content-Type":"application/json"} | -| input.request.scheme | string | The current http request scheme | "https" | +| input.request.scheme | string | The current http request scheme | "https" | | input.request.realIP | string | The current http request client real IP | "127.0.0.1" | | input.request.body | string | The current http request body string data | {"data":"xxx"} | @@ -1202,7 +1205,7 @@ The following table lists input request fields that can be used in an OPA policy The `Redirector` filter is used to do HTTP redirect. `Redirector` matches request url, do replacement, and return response with status code of `3xx` and put new path in response header with key of `Location`. -Here a simple example: +Here a simple example: ```yaml name: demo-pipeline kind: Pipeline @@ -1234,13 +1237,13 @@ filters: kind: Redirector match: "^/users/([0-9]+)" # by default, value of matchPart is uri, supported values: uri, path, full. - matchPart: "full" + matchPart: "full" replacement: "http://example.com/display?user=$1" ``` For request with URL of `https://example.com:8080/apis/v1/user?id=1`, URI part is `/apis/v1/user?id=1`, path part is `/apis/v1/user` and full part is `https://example.com:8080/apis/v1/user?id=1`. -By default, we return status code of `301` "Moved Permanently". To return status code of `302` "Found" or other `3xx`, change `statusCode` in yaml. +By default, we return status code of `301` "Moved Permanently". To return status code of `302` "Found" or other `3xx`, change `statusCode` in yaml. ```yaml name: demo-pipeline @@ -1256,7 +1259,7 @@ filters: replacement: "http://example.com/display?user=$1" ``` -Following are some common used examples: +Following are some common used examples: 1. URI prefix redirect ```yaml name: demo-pipeline @@ -1355,13 +1358,96 @@ output: https://example.com/api/user/123 | ---- | ---- | ----------- | -------- | | match | string | Regular expression to match request path. The syntax of the regular expression is [RE2](https://golang.org/s/re2syntax) | Yes | | matchPart | string | Parameter to decide which part of url used to do match, supported values: uri, full, path. Default value is uri. | No | -| replacement | string | Replacement when the match succeeds. Placeholders like `$1`, `$2` can be used to represent the sub-matches in `regexp` | Yes | -| statusCode | int | Status code of response. Supported values: 301, 302, 303, 304, 307, 308. Default: 301. | No | +| replacement | string | Replacement when the match succeeds. Placeholders like `$1`, `$2` can be used to represent the sub-matches in `regexp` | Yes | +| statusCode | int | Status code of response. Supported values: 301, 302, 303, 304, 307, 308. Default: 301. | No | ### Results | Value | Description | | ----- | ----------- | | redirected | The request has been redirected | + +## RedirectorV2 + +The `RedirectorV2` filter provides advanced HTTP redirection capabilities within the Kubernetes API Gateway. It offers fine-grained control over URL components such as scheme, hostname, and path. The specifications are consistent with the [Kubernetes Gateway API's `HTTPRequestRedirectFilter`](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRequestRedirectFilter). + +### Configuration + +| Name | Type | Description | Required | +|------|------|-------------|----------| +| scheme | string | The protocol for the redirected request (e.g., `http` or `https`). | No | +| hostname | string | The domain name to which the request should be redirected. To specify a port, include it in the hostname (e.g., `example.com:8080`). Note: Explicit port configurations are currently unsupported due to complexities in specification. | No | +| statusCode | int | The HTTP status code for the redirection response. | Yes | +| path.type | string | Determines the type of redirection. Supported values: `ReplacePrefixMatch`, `ReplaceFullPath`. | Yes | +| path.replacePrefixMatch | string | The new prefix for the redirection. Used only with `ReplacePrefixMatch`. | Conditional | +| path.replaceFullPath | string | The new full path for the redirection. Used only with `ReplaceFullPath`. | Conditional | + +### Results + +| Value | Description | +|-------|-------------| +| redirected | Indicates that the request has been redirected based on the `RedirectorV2` configuration. | + + +Commmon Use-Cases: + +1. Replace Prefix: + +Substitute a prefix with `/account` and return a `302 Found` status code. + +```yaml +name: demo-pipeline +kind: Pipeline +flow: +- filter: redirectorv2 +filters: +- name: redirectorv2 + kind: RedirectorV2 + pathPrefix: /user + path: + type: ReplacePrefixMatch + replacePrefixMatch: /account + statusCode: 302 +``` + +2. Replace Full Path: + +Redirect to an absolute path `/full-path`. + +```yaml +name: demo-pipeline +kind: Pipeline +flow: +- filter: redirectorv2 +filters: +- name: redirectorv2 + kind: RedirectorV2 + path: + type: ReplaceFullPath + replaceFullPath: /full-path + statusCode: 302 +``` + +3. Domain and Scheme Redirection: + +Redirect to a new domain using the HTTPS protocol. + +```yaml +name: demo-pipeline +kind: Pipeline +flow: +- filter: redirectorv2 +filters: +- name: redirectorv2 + kind: RedirectorV2 + scheme: https + hostname: newdomain.com + pathPrefix: /user + path: + type: ReplacePrefixMatch + replacePrefixMatch: /account + statusCode: 302 +``` + ## GRPCProxy The `GRPCProxy` filter is a proxy for gRPC backend service. It supports both unary RPCs and streaming RPCs. @@ -1389,7 +1475,7 @@ Same as the `Proxy` filter: * the servers of a pool can be configured dynamically via service discovery. * when there are multiple servers in a pool, the pool can do a load balance between them. -Because gRPC does not support the http `Connect` method, it does not support tunneling mode, +Because gRPC does not support the http `Connect` method, it does not support tunneling mode, we provide a new [load balancer](#proxyloadbalancespec) `policy.forward` to achieve a similar effect. Note that each gRPC client establishes a connection with Easegress. However, diff --git a/pkg/filters/redirectorv2/redirectorv2.go b/pkg/filters/redirectorv2/redirectorv2.go new file mode 100644 index 0000000000..9f4ccad89e --- /dev/null +++ b/pkg/filters/redirectorv2/redirectorv2.go @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2017, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package redirector implements a filter to handle HTTP redirects. +package redirectorv2 + +import ( + "errors" + "net/url" + "strings" + + "github.com/megaease/easegress/v2/pkg/context" + "github.com/megaease/easegress/v2/pkg/filters" + "github.com/megaease/easegress/v2/pkg/protocols/httpprot" + + gwapis "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + // Kind is the kind of Redirector. + Kind = "RedirectorV2" + + resultRedirected = "redirected" +) + +const ( + defaultStatusCode = 302 +) + +var statusCodeMap = map[int]string{ + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 307: "Temporary Redirect", + 308: "Permanent Redirect", +} + +var kind = &filters.Kind{ + Name: Kind, + Description: "Redirector redirect HTTP requests.", + Results: []string{resultRedirected}, + DefaultSpec: func() filters.Spec { + return &Spec{} + }, + CreateInstance: func(spec filters.Spec) filters.Filter { + return &Redirector{spec: spec.(*Spec)} + }, +} + +func init() { + filters.Register(kind) +} + +type ( + // Redirector is filter to redirect HTTP requests. + Redirector struct { + spec *Spec + } + + // Spec describes the Redirector. + Spec struct { + filters.BaseSpec `json:",inline"` + + PathPrefix *string `json:"pathPrefix,omitempty"` + gwapis.HTTPRequestRedirectFilter `json:",inline"` + } +) + +// Validate validates the spec. +func (s *Spec) Validate() error { + if s.Scheme != nil { + if *s.Scheme != "http" && *s.Scheme != "https" { + return errors.New("invalid scheme of Redirector, only support http and https") + } + } + + if s.Port != nil { + if *s.Port < 1 || *s.Port > 65535 { + return errors.New("invalid port of Redirector, only support 1-65535") + } + } + + if s.StatusCode != nil { + if _, ok := statusCodeMap[*s.StatusCode]; !ok { + return errors.New("invalid status code of Redirector, support 300, 301, 302, 303, 304, 307, 308") + } + } + + if s.Path != nil { + switch s.Path.Type { + case gwapis.FullPathHTTPPathModifier: + if s.Path.ReplaceFullPath == nil { + 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") + } + default: + return errors.New("invalid path type of Redirector, only support ReplaceFullPath and ReplacePrefixMatch") + } + } + + return nil +} + +// Name returns the name of the Redirector filter instance. +func (r *Redirector) Name() string { + return r.spec.Name() +} + +// Kind returns the kind of Redirector. +func (r *Redirector) Kind() *filters.Kind { + return kind +} + +// Spec returns the spec used by the Redirector +func (r *Redirector) Spec() filters.Spec { + return r.spec +} + +// Init initializes Redirector. +func (r *Redirector) Init() { + r.reload() +} + +// Inherit inherits previous generation of Redirector. +func (r *Redirector) Inherit(previousGeneration filters.Filter) { + r.Init() +} + +func (r *Redirector) reload() { +} + +// Handle Redirector Context. +func (r *Redirector) Handle(ctx *context.Context) string { + req := ctx.GetInputRequest().(*httpprot.Request) + + redirectURL := deepCopyURL(*req.URL()) + + if r.spec.Scheme != nil { + redirectURL.Scheme = *r.spec.Scheme + } + + if r.spec.Hostname != nil { + redirectURL.Host = string(*r.spec.Hostname) + } + + // NOTE: + // We choose not to support port, because its specification is complicated and confusing. + // If users want to use a specific port, they can specify it in the hostname field. + + // if r.spec.Port != nil { + // redirectLocation.Host = fmt.Sprintf("%s:%d", redirectLocation.Host, *r.spec.Port) + // } + + if r.spec.Path != nil { + switch r.spec.Path.Type { + case gwapis.FullPathHTTPPathModifier: + redirectURL.Path = string(*r.spec.Path.ReplaceFullPath) + case gwapis.PrefixMatchHTTPPathModifier: + redirectURL.Path = r.subPrefix(redirectURL.Path, + *r.spec.PathPrefix, string(*r.spec.Path.ReplacePrefixMatch)) + } + } + + if req.URL().String() == redirectURL.String() { + return "" + } + + resp, _ := httpprot.NewResponse(nil) + + statusCode := int(defaultStatusCode) + if r.spec.StatusCode != nil { + statusCode = int(*r.spec.StatusCode) + } + resp.SetStatusCode(statusCode) + + resp.SetPayload([]byte(statusCodeMap[statusCode])) + resp.Header().Add("Location", redirectURL.String()) + + ctx.SetOutputResponse(resp) + + return resultRedirected +} + +func deepCopyURL(u url.URL) *url.URL { + copied := u + + if u.User != nil { + copiedUser := *u.User + copied.User = &copiedUser + } + + return &copied +} + +// subPrefix replaces the prefix in the path with replacePrefixMatch if the prefix exists +func (r *Redirector) subPrefix(path, prefix, replacePrefixMatch string) string { + if strings.HasPrefix(path, prefix) { + return strings.Replace(path, prefix, replacePrefixMatch, 1) + } + return path +} + +// Status returns status. +func (r *Redirector) Status() interface{} { + return nil +} + +// Close closes Redirector. +func (r *Redirector) Close() { +} diff --git a/pkg/filters/redirectorv2/redirectorv2_test.go b/pkg/filters/redirectorv2/redirectorv2_test.go new file mode 100644 index 0000000000..4e455c9af8 --- /dev/null +++ b/pkg/filters/redirectorv2/redirectorv2_test.go @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017, MegaEase + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package redirectorv2 + +import ( + "net/http" + "testing" + + "github.com/megaease/easegress/v2/pkg/context" + "github.com/megaease/easegress/v2/pkg/protocols/httpprot" + "github.com/stretchr/testify/assert" + + gwapis "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestSpecValidate(t *testing.T) { + tests := []struct { + name string + spec *Spec + expectErr bool + errMsg string + }{ + { + name: "Invalid Scheme", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Scheme: new(string), + }, + }, + expectErr: true, + errMsg: "invalid scheme of Redirector, only support http and https", + }, + { + name: "Valid Scheme (http)", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Scheme: func() *string { s := "http"; return &s }(), + }, + }, + expectErr: false, + }, + { + name: "Valid Scheme (https)", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Scheme: func() *string { s := "https"; return &s }(), + }, + }, + expectErr: false, + }, + { + name: "Invalid Port", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Port: func() *gwapis.PortNumber { i := gwapis.PortNumber(70000); return &i }(), + }, + }, + expectErr: true, + errMsg: "invalid port of Redirector, only support 1-65535", + }, + { + name: "Valid Port", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Port: func() *gwapis.PortNumber { i := gwapis.PortNumber(8080); return &i }(), + }, + }, + expectErr: false, + }, + { + name: "Invalid Status Code", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + StatusCode: func() *int { i := 299; return &i }(), + }, + }, + expectErr: true, + errMsg: "invalid status code of Redirector, support 300, 301, 302, 303, 304, 307, 308", + }, + { + name: "Valid Status Code", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + StatusCode: func() *int { i := 302; return &i }(), + }, + }, + expectErr: false, + }, + { + name: "Missing replaceFullPath", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Path: &gwapis.HTTPPathModifier{ + Type: gwapis.FullPathHTTPPathModifier, + }, + }, + }, + expectErr: true, + errMsg: "invalid path of Redirector, replaceFullPath can't be empty", + }, + { + name: "Missing pathPrefix", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Path: &gwapis.HTTPPathModifier{ + Type: gwapis.PrefixMatchHTTPPathModifier, + }, + }, + }, + expectErr: true, + errMsg: "invalid path of Redirector, pathPrefix can't be empty", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.spec.Validate() + + if tt.expectErr { + assert.Error(t, err) + assert.Equal(t, tt.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestRedirectorHandle2(t *testing.T) { + tests := []struct { + name string + reqURL string + spec *Spec + expectedResult string + expectedURL string + expectedStatus int + }{ + { + name: "Redirect full path", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Path: &gwapis.HTTPPathModifier{ + Type: gwapis.FullPathHTTPPathModifier, + ReplaceFullPath: new(string), + }, + }, + }, + expectedResult: resultRedirected, + expectedURL: "http://localhost/newpath", + expectedStatus: 302, + }, + { + name: "Redirect prefix", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{ + PathPrefix: new(string), + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Path: &gwapis.HTTPPathModifier{ + Type: gwapis.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: new(string), + }, + }, + }, + expectedResult: resultRedirected, + expectedURL: "http://localhost/account/data/profile", + expectedStatus: 302, + }, + { + name: "Change scheme", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Scheme: new(string), + }, + }, + expectedResult: resultRedirected, + expectedURL: "https://localhost/user/data/profile", + expectedStatus: 302, + }, + { + name: "Redirect with hostname", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + Hostname: new(gwapis.PreciseHostname), + }, + }, + expectedResult: resultRedirected, + expectedURL: "http://example.com/user/data/profile", + expectedStatus: 302, + }, + { + name: "No redirection", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{}, + expectedResult: "", + expectedStatus: 0, // 0 indicates no redirection, so no status check + }, + { + name: "Custom status code", + reqURL: "http://localhost/user/data/profile", + spec: &Spec{ + HTTPRequestRedirectFilter: gwapis.HTTPRequestRedirectFilter{ + StatusCode: new(int), + }, + }, + expectedResult: "", + expectedURL: "http://localhost/user/data/profile", + expectedStatus: 0, // 0 indicates no redirection, so no status check + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stdReq, _ := http.NewRequest("GET", tt.reqURL, nil) + req, _ := httpprot.NewRequest(stdReq) + ctx := context.New(nil) + ctx.SetInputRequest(req) + + 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/" + } + + if tt.spec.Scheme != nil { + *tt.spec.Scheme = "https" + } + + if tt.spec.Hostname != nil { + *tt.spec.Hostname = "example.com" + } + + if tt.spec.StatusCode != nil { + *tt.spec.StatusCode = 303 + } + + rd := &Redirector{spec: tt.spec} + result := rd.Handle(ctx) + assert.Equal(t, tt.expectedResult, result) + + if result == resultRedirected { + resp := ctx.GetOutputResponse().(*httpprot.Response) + location := resp.Header().Get("Location") + assert.Equal(t, tt.expectedURL, location) + assert.Equal(t, tt.expectedStatus, resp.StatusCode()) + } + }) + } +} diff --git a/pkg/object/gatewaycontroller/translator.go b/pkg/object/gatewaycontroller/translator.go index ad0eb46988..8b2df4585f 100644 --- a/pkg/object/gatewaycontroller/translator.go +++ b/pkg/object/gatewaycontroller/translator.go @@ -24,7 +24,7 @@ import ( "github.com/megaease/easegress/v2/pkg/filters/builder" "github.com/megaease/easegress/v2/pkg/filters/proxies" "github.com/megaease/easegress/v2/pkg/filters/proxies/httpproxy" - "github.com/megaease/easegress/v2/pkg/filters/redirector" + redirector "github.com/megaease/easegress/v2/pkg/filters/redirectorv2" "github.com/megaease/easegress/v2/pkg/logger" "github.com/megaease/easegress/v2/pkg/object/httpserver" "github.com/megaease/easegress/v2/pkg/object/httpserver/routers" @@ -164,39 +164,7 @@ func (b *pipelineSpecBuilder) addURLRewrite(f *gwapis.HTTPURLRewriteFilter) { } func (b *pipelineSpecBuilder) addRequestRedirect(f *gwapis.HTTPRequestRedirectFilter) { - // TODO: The current redirector filter does not compatible with the - // Gateway API spec. - logger.Errorf("redirector filter is not supported currently") - /* - if b.redirector == nil { - b.redirector = &redirector.Spec{StatusCode: 302} - } - - var repl string - - re := `^([^:]*)://([^:/]+)(:\d+)?/` - if f.Scheme == nil { - repl = "$1://" - } else { - repl = *f.Scheme + "://" - } - - if f.Hostname == nil { - repl += "$2" - } else { - repl += string(*f.Hostname) - } - - if f.Port == nil { - repl += "$3" - } else { - repl += fmt.Sprintf(":%d", *f.Port) - } - - if f.StatusCode != nil { - b.redirector.StatusCode = *f.StatusCode - } - */ + b.redirector.HTTPRequestRedirectFilter = *f } func (b *pipelineSpecBuilder) addRequestMirror(addr string) {