-
Notifications
You must be signed in to change notification settings - Fork 360
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: huabing zhao <zhaohuabing@gmail.com> sort HTTP filters Signed-off-by: huabing zhao <zhaohuabing@gmail.com> address comments Signed-off-by: huabing zhao <zhaohuabing@gmail.com> refactor route filter config Signed-off-by: huabing zhao <zhaohuabing@gmail.com> address comments Signed-off-by: huabing zhao <zhaohuabing@gmail.com>
- Loading branch information
1 parent
930592c
commit 596400e
Showing
13 changed files
with
540 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// Copyright Envoy Gateway Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// The full text of the Apache license is available in the LICENSE file at | ||
// the root of the repo. | ||
|
||
package translator | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" | ||
corsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" | ||
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" | ||
matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" | ||
"github.com/envoyproxy/go-control-plane/pkg/wellknown" | ||
"github.com/golang/protobuf/ptypes/wrappers" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
|
||
"github.com/envoyproxy/gateway/internal/ir" | ||
) | ||
|
||
// patchHCMWithCorsFilter builds and appends the Cors Filter to the HTTP | ||
// Connection Manager if applicable. | ||
func patchHCMWithCorsFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { | ||
if mgr == nil { | ||
return errors.New("hcm is nil") | ||
} | ||
|
||
if irListener == nil { | ||
return errors.New("ir listener is nil") | ||
} | ||
|
||
if !listenerContainsCors(irListener) { | ||
return nil | ||
} | ||
|
||
// Return early if filter already exists. | ||
for _, httpFilter := range mgr.HttpFilters { | ||
if httpFilter.Name == wellknown.CORS { | ||
return nil | ||
} | ||
} | ||
|
||
corsFilter, err := buildHCMCorsFilter() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Ensure the cors filter is the first one in the filter chain. | ||
mgr.HttpFilters = append([]*hcmv3.HttpFilter{corsFilter}, mgr.HttpFilters...) | ||
|
||
return nil | ||
} | ||
|
||
// buildHCMCorsFilter returns a Cors filter from the provided IR listener. | ||
func buildHCMCorsFilter() (*hcmv3.HttpFilter, error) { | ||
corsProto := &corsv3.Cors{} | ||
|
||
corsAny, err := anypb.New(corsProto) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &hcmv3.HttpFilter{ | ||
Name: wellknown.CORS, | ||
ConfigType: &hcmv3.HttpFilter_TypedConfig{ | ||
TypedConfig: corsAny, | ||
}, | ||
}, nil | ||
} | ||
|
||
// listenerContainsCors returns true if the provided listener has Cors | ||
// policies attached to its routes. | ||
func listenerContainsCors(irListener *ir.HTTPListener) bool { | ||
if irListener == nil { | ||
return false | ||
} | ||
|
||
for _, route := range irListener.Routes { | ||
if route.Cors != nil { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// patchRouteWithCorsConfig patches the provided route with the Cors config if | ||
// applicable. | ||
func patchRouteWithCorsConfig(route *routev3.Route, irRoute *ir.HTTPRoute) error { | ||
if route == nil { | ||
return errors.New("xds route is nil") | ||
} | ||
if irRoute == nil { | ||
return errors.New("ir route is nil") | ||
} | ||
if irRoute.Cors == nil { | ||
return nil | ||
} | ||
|
||
filterCfg := route.GetTypedPerFilterConfig() | ||
if _, ok := filterCfg[wellknown.CORS]; ok { | ||
// This should not happen since this is the only place where the cors | ||
// filter is added in a route. | ||
return fmt.Errorf("route already contains cors config: %+v", route) | ||
} | ||
|
||
var ( | ||
allowOrigins []*matcherv3.StringMatcher | ||
allowMethods string | ||
allowHeaders string | ||
exposeHeaders string | ||
maxAge string | ||
allowCredentials *wrappers.BoolValue | ||
allowPrivateNetworkAccess *wrappers.BoolValue | ||
) | ||
|
||
//nolint:gocritic | ||
|
||
for _, origin := range irRoute.Cors.AllowOrigins { | ||
allowOrigins = append(allowOrigins, buildXdsStringMatcher(origin)) | ||
} | ||
|
||
allowMethods = strings.Join(irRoute.Cors.AllowMethods, ", ") | ||
allowHeaders = strings.Join(irRoute.Cors.AllowHeaders, ", ") | ||
exposeHeaders = strings.Join(irRoute.Cors.ExposeHeaders, ", ") | ||
maxAge = strconv.Itoa(int(irRoute.Cors.MaxAge.Seconds())) | ||
allowPrivateNetworkAccess = &wrappers.BoolValue{Value: irRoute.Cors.AllowPrivateNetworkAccess} | ||
|
||
routeCfgProto := &corsv3.CorsPolicy{ | ||
AllowOriginStringMatch: allowOrigins, | ||
AllowMethods: allowMethods, | ||
AllowHeaders: allowHeaders, | ||
ExposeHeaders: exposeHeaders, | ||
MaxAge: maxAge, | ||
AllowCredentials: allowCredentials, | ||
AllowPrivateNetworkAccess: allowPrivateNetworkAccess, | ||
} | ||
|
||
routeCfgAny, err := anypb.New(routeCfgProto) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if filterCfg == nil { | ||
route.TypedPerFilterConfig = make(map[string]*anypb.Any) | ||
} | ||
|
||
route.TypedPerFilterConfig[wellknown.CORS] = routeCfgAny | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright Envoy Gateway Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// The full text of the Apache license is available in the LICENSE file at | ||
// the root of the repo. | ||
|
||
package translator | ||
|
||
import ( | ||
"sort" | ||
|
||
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" | ||
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" | ||
"github.com/envoyproxy/go-control-plane/pkg/wellknown" | ||
|
||
"github.com/envoyproxy/gateway/internal/ir" | ||
xdsfilters "github.com/envoyproxy/gateway/internal/xds/filters" | ||
) | ||
|
||
type OrderedHTTPFilter struct { | ||
filter *hcmv3.HttpFilter | ||
order int | ||
} | ||
|
||
type OrderedHTTPFilters []*OrderedHTTPFilter | ||
|
||
// newOrderedHTTPFilter give each HTTP filter a rational order. | ||
// Note: please set the order for new filters here. | ||
// If the order is not set, the filter will be set to default order 50. | ||
func newOrderedHTTPFilter(filter *hcmv3.HttpFilter) *OrderedHTTPFilter { | ||
order := 50 | ||
|
||
// Set a rational order for all the filters. | ||
switch filter.Name { | ||
case wellknown.CORS: | ||
order = 1 | ||
case jwtAuthenFilter: | ||
order = 2 | ||
case wellknown.HTTPRateLimit: | ||
order = 3 | ||
case wellknown.Router: | ||
order = 100 | ||
} | ||
|
||
return &OrderedHTTPFilter{ | ||
filter: filter, | ||
order: order, | ||
} | ||
} | ||
|
||
// sort.Interface implementation. | ||
func (o OrderedHTTPFilters) Len() int { | ||
return len(o) | ||
} | ||
|
||
func (o OrderedHTTPFilters) Less(i, j int) bool { | ||
return o[i].order < o[j].order | ||
} | ||
|
||
func (o OrderedHTTPFilters) Swap(i, j int) { | ||
o[i], o[j] = o[j], o[i] | ||
} | ||
|
||
// sortHTTPFilters sorts the HTTP filters in the correct order. | ||
// This is needed because the order of the filters is important. | ||
// For example, the cors filter must be before the router filter. | ||
// The router filter must be the last one. | ||
func sortHTTPFilters(filters []*hcmv3.HttpFilter) []*hcmv3.HttpFilter { | ||
orderedFilters := make(OrderedHTTPFilters, len(filters)) | ||
for i := 0; i < len(filters); i++ { | ||
orderedFilters[i] = newOrderedHTTPFilter(filters[i]) | ||
} | ||
sort.Sort(orderedFilters) | ||
|
||
for i := 0; i < len(filters); i++ { | ||
filters[i] = orderedFilters[i].filter | ||
} | ||
return filters | ||
} | ||
|
||
// patchHCMWithFilters builds and appends HTTP Filters to the HTTP connection | ||
// manager. | ||
// Important: don't forget to set the order for new filters in the | ||
// newOrderedHTTPFilter method. | ||
func (t *Translator) patchHCMWithFilters( | ||
mgr *hcmv3.HttpConnectionManager, | ||
irListener *ir.HTTPListener) error { | ||
// The order of filter patching is not relevant here. | ||
// All the filters will be sorted in correct order after the patching is done. | ||
// Important: don't forget to set the order for new filters in the | ||
// newOrderedHTTPFilter method. | ||
// TODO: Make this a generic interface for all API Gateway features. | ||
// https://github.com/envoyproxy/gateway/issues/882 | ||
t.patchHCMWithRateLimit(mgr, irListener) | ||
|
||
// Add the jwt authn filter, if needed. | ||
if err := patchHCMWithJwtAuthnFilter(mgr, irListener); err != nil { | ||
return err | ||
} | ||
|
||
// Add the cors filter, if needed | ||
if err := patchHCMWithCorsFilter(mgr, irListener); err != nil { | ||
return err | ||
} | ||
|
||
// Add the router filter | ||
mgr.HttpFilters = append(mgr.HttpFilters, xdsfilters.HTTPRouter) | ||
|
||
// Sort the filters in the correct order. | ||
mgr.HttpFilters = sortHTTPFilters(mgr.HttpFilters) | ||
return nil | ||
} | ||
|
||
// patchRouteWithFilters appends per-route filter configurations to the route. | ||
func patchRouteWithFilters( | ||
route *routev3.Route, | ||
irRoute *ir.HTTPRoute) error { | ||
// TODO: Convert this into a generic interface for API Gateway features. | ||
// https://github.com/envoyproxy/gateway/issues/882 | ||
if err := | ||
patchRouteWithRateLimit(route.GetRoute(), irRoute); err != nil { | ||
return nil | ||
} | ||
|
||
// Add the jwt per route config to the route, if needed. | ||
if err := patchRouteWithJwtConfig(route, irRoute); err != nil { | ||
return nil | ||
} | ||
|
||
// Add the cors per route config to the route, if needed. | ||
if err := patchRouteWithCorsConfig(route, irRoute); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Oops, something went wrong.