implement direct response

This commit is contained in:
Caleb Doxsey 2024-02-08 14:46:23 -07:00
parent 84448eb9e0
commit 99b3b73ab4
4 changed files with 51 additions and 19 deletions

View file

@ -14,13 +14,13 @@ const (
)
var (
errKeysMustBeStrings = errors.New("cannot convert nested map: all keys must be strings")
errZeroWeight = errors.New("zero load balancing weight not permitted")
errEndpointWeightsSpec = errors.New("either no weights should be provided, or all endpoints must have non-zero weight specified")
errHostnameMustBeSpecified = errors.New("endpoint hostname must be specified")
errSchemeMustBeSpecified = errors.New("url scheme must be provided")
errEmptyUrls = errors.New("url list is empty")
errEitherToOrRedirectRequired = errors.New("policy should have either `to` or `redirect` defined")
errKeysMustBeStrings = errors.New("cannot convert nested map: all keys must be strings")
errZeroWeight = errors.New("zero load balancing weight not permitted")
errEndpointWeightsSpec = errors.New("either no weights should be provided, or all endpoints must have non-zero weight specified")
errHostnameMustBeSpecified = errors.New("endpoint hostname must be specified")
errSchemeMustBeSpecified = errors.New("url scheme must be provided")
errEmptyUrls = errors.New("url list is empty")
errEitherToOrRedirectOrResponseRequired = errors.New("policy should have either `to` or `redirect` or `response` defined")
)
var protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}

View file

@ -282,6 +282,9 @@ func (b *Builder) buildRouteForPolicyAndMatch(
return nil, err
}
route.Action = &envoy_config_route_v3.Route_Redirect{Redirect: action}
} else if policy.Response != nil {
action := b.buildPolicyRouteDirectResponseAction(policy.Response)
route.Action = &envoy_config_route_v3.Route_DirectResponse{DirectResponse: action}
} else {
action, err := b.buildPolicyRouteRouteAction(cfg.Options, policy)
if err != nil {
@ -343,6 +346,17 @@ func (b *Builder) buildRouteForPolicyAndMatch(
return route, nil
}
func (b *Builder) buildPolicyRouteDirectResponseAction(r *config.DirectResponse) *envoy_config_route_v3.DirectResponseAction {
return &envoy_config_route_v3.DirectResponseAction{
Status: uint32(r.Status),
Body: &envoy_config_core_v3.DataSource{
Specifier: &envoy_config_core_v3.DataSource_InlineString{
InlineString: r.Body,
},
},
}
}
func (b *Builder) buildPolicyRouteRedirectAction(r *config.PolicyRedirect) (*envoy_config_route_v3.RedirectAction, error) {
action := &envoy_config_route_v3.RedirectAction{}
switch {

View file

@ -28,8 +28,9 @@ import (
type Policy struct {
ID string `mapstructure:"-" yaml:"-" json:"-"`
From string `mapstructure:"from" yaml:"from"`
To WeightedURLs `mapstructure:"to" yaml:"to"`
From string `mapstructure:"from" yaml:"from"`
To WeightedURLs `mapstructure:"to" yaml:"to"`
Response *DirectResponse `mapstructure:"response" yaml:"response,omitempty" json:"response,omitempty"`
// LbWeights are optional load balancing weights applied to endpoints specified in To
// this field exists for compatibility with mapstructure
@ -213,6 +214,12 @@ type PolicyRedirect struct {
StripQuery *bool `mapstructure:"strip_query" yaml:"strip_query,omitempty" json:"strip_query,omitempty"`
}
// A DirectResponse is the response to an HTTP request.
type DirectResponse struct {
Status int `mapstructure:"status" yaml:"status,omitempty" json:"status,omitempty"`
Body string `mapstructure:"body" yaml:"body,omitempty" json:"body,omitempty"`
}
// NewPolicyFromProto creates a new Policy from a protobuf policy config route.
func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
var timeout *time.Duration
@ -446,8 +453,8 @@ func (p *Policy) Validate() error {
source.String())
}
if len(p.To) == 0 && p.Redirect == nil {
return errEitherToOrRedirectRequired
if len(p.To) == 0 && p.Redirect == nil && p.Response == nil {
return errEitherToOrRedirectOrResponseRequired
}
for _, u := range p.To {
@ -567,8 +574,11 @@ func (p *Policy) RouteID() (uint64, error) {
id.To = dst
} else if p.Redirect != nil {
id.Redirect = p.Redirect
} else if p.Response != nil {
id.DirectResponseStatus = p.Response.Status
id.DirectResponseBody = p.Response.Body
} else {
return 0, errEitherToOrRedirectRequired
return 0, errEitherToOrRedirectOrResponseRequired
}
return hashutil.Hash(id)
@ -679,12 +689,14 @@ func (p *Policy) GetPassIdentityHeaders(options *Options) bool {
}
type routeID struct {
From string
To []string
Prefix string
Path string
Regex string
Redirect *PolicyRedirect
From string
To []string
Prefix string
Path string
Regex string
Redirect *PolicyRedirect
DirectResponseStatus int
DirectResponseBody string
}
/*

View file

@ -33,12 +33,18 @@ message RouteRedirect {
optional bool strip_query = 8;
}
// Next ID: 62.
message RouteDirectResponse {
uint32 status = 1;
string body = 2;
}
// Next ID: 63.
message Route {
string name = 1;
string from = 2;
repeated string to = 3;
RouteDirectResponse response = 62;
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/endpoint/v3/endpoint_components.proto#envoy-v3-api-msg-config-endpoint-v3-lbendpoint
// optional load balancing weights assigned to upstream servers defined in TO