@@ -10,13 +10,15 @@ import (
1010 "go.opentelemetry.io/otel"
1111 "go.opentelemetry.io/otel/attribute"
1212 "go.opentelemetry.io/otel/trace"
13+ "google.golang.org/protobuf/types/known/structpb"
1314
1415 "github.com/envoyproxy/ratelimit/src/settings"
1516 "github.com/envoyproxy/ratelimit/src/stats"
1617
1718 "github.com/envoyproxy/ratelimit/src/utils"
1819
1920 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
21+ ratelimitv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3"
2022 pb "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v3"
2123 logger "github.com/sirupsen/logrus"
2224 "golang.org/x/net/context"
@@ -38,18 +40,19 @@ type RateLimitServiceServer interface {
3840}
3941
4042type service struct {
41- configLock sync.RWMutex
42- configUpdateEvent <- chan provider.ConfigUpdateEvent
43- config config.RateLimitConfig
44- cache limiter.RateLimitCache
45- stats stats.ServiceStats
46- health * server.HealthChecker
47- customHeadersEnabled bool
48- customHeaderLimitHeader string
49- customHeaderRemainingHeader string
50- customHeaderResetHeader string
51- customHeaderClock utils.TimeSource
52- globalShadowMode bool
43+ configLock sync.RWMutex
44+ configUpdateEvent <- chan provider.ConfigUpdateEvent
45+ config config.RateLimitConfig
46+ cache limiter.RateLimitCache
47+ stats stats.ServiceStats
48+ health * server.HealthChecker
49+ customHeadersEnabled bool
50+ customHeaderLimitHeader string
51+ customHeaderRemainingHeader string
52+ customHeaderResetHeader string
53+ customHeaderClock utils.TimeSource
54+ globalShadowMode bool
55+ responseDynamicMetadataEnabled bool
5356}
5457
5558func (this * service ) SetConfig (updateEvent provider.ConfigUpdateEvent , healthyWithAtLeastOneConfigLoad bool ) {
@@ -84,6 +87,7 @@ func (this *service) SetConfig(updateEvent provider.ConfigUpdateEvent, healthyWi
8487
8588 rlSettings := settings .NewSettings ()
8689 this .globalShadowMode = rlSettings .GlobalShadowMode
90+ this .responseDynamicMetadataEnabled = rlSettings .ResponseDynamicMetadata
8791
8892 if rlSettings .RateLimitResponseHeadersEnabled {
8993 this .customHeadersEnabled = true
@@ -239,10 +243,72 @@ func (this *service) shouldRateLimitWorker(
239243 this .stats .GlobalShadowMode .Inc ()
240244 }
241245
246+ // If response dynamic data enabled, set dynamic data on response.
247+ if this .responseDynamicMetadataEnabled {
248+ response .DynamicMetadata = ratelimitToMetadata (request )
249+ }
250+
242251 response .OverallCode = finalCode
243252 return response
244253}
245254
255+ func ratelimitToMetadata (req * pb.RateLimitRequest ) * structpb.Struct {
256+ fields := make (map [string ]* structpb.Value )
257+
258+ // Domain
259+ fields ["domain" ] = structpb .NewStringValue (req .Domain )
260+
261+ // Descriptors
262+ descriptorsValues := make ([]* structpb.Value , 0 , len (req .Descriptors ))
263+ for _ , descriptor := range req .Descriptors {
264+ s := descriptorToStruct (descriptor )
265+ if s == nil {
266+ continue
267+ }
268+ descriptorsValues = append (descriptorsValues , structpb .NewStructValue (s ))
269+ }
270+ fields ["descriptors" ] = structpb .NewListValue (& structpb.ListValue {
271+ Values : descriptorsValues ,
272+ })
273+
274+ // HitsAddend
275+ if hitsAddend := req .GetHitsAddend (); hitsAddend != 0 {
276+ fields ["hitsAddend" ] = structpb .NewNumberValue (float64 (hitsAddend ))
277+ }
278+
279+ return & structpb.Struct {Fields : fields }
280+ }
281+
282+ func descriptorToStruct (descriptor * ratelimitv3.RateLimitDescriptor ) * structpb.Struct {
283+ if descriptor == nil {
284+ return nil
285+ }
286+
287+ fields := make (map [string ]* structpb.Value )
288+
289+ // Entries
290+ entriesValues := make ([]* structpb.Value , 0 , len (descriptor .Entries ))
291+ for _ , entry := range descriptor .Entries {
292+ val := fmt .Sprintf ("%s=%s" , entry .GetKey (), entry .GetValue ())
293+ entriesValues = append (entriesValues , structpb .NewStringValue (val ))
294+ }
295+ fields ["entries" ] = structpb .NewListValue (& structpb.ListValue {
296+ Values : entriesValues ,
297+ })
298+
299+ // Limit
300+ if descriptor .GetLimit () != nil {
301+ fields ["limit" ] = structpb .NewStringValue (descriptor .Limit .String ())
302+ }
303+
304+ // HitsAddend
305+ if hitsAddend := descriptor .GetHitsAddend (); hitsAddend != nil {
306+ fields ["hitsAddend" ] = structpb .NewNumberValue (float64 (hitsAddend .GetValue ()))
307+ }
308+
309+ return & structpb.Struct {Fields : fields }
310+ }
311+
246312func (this * service ) rateLimitLimitHeader (descriptor * pb.RateLimitResponse_DescriptorStatus ) * core.HeaderValue {
247313 // Limit header only provides the mandatory part from the spec, the actual limit
248314 // the optional quota policy is currently not provided
0 commit comments