From 1da36ecc872fd97a07fe93b4f2d9c64d33aeb328 Mon Sep 17 00:00:00 2001 From: Adam Snyder Date: Wed, 14 Nov 2018 19:59:43 -0800 Subject: [PATCH 1/3] Add X-RateLimit-* response headers as an opt-in feature --- glide.lock | 35 ++- glide.yaml | 4 +- proto/envoy/api/v2/ratelimit/ratelimit.pb.go | 28 +- proto/envoy/service/ratelimit/v2/rls.pb.go | 155 ++++++----- proto/ratelimit/ratelimit.pb.go | 145 +++++----- script/generate_proto | 2 +- src/service/ratelimit.go | 121 +++++++-- src/service/ratelimit_legacy.go | 4 +- src/service_cmd/runner/runner.go | 10 +- src/settings/settings.go | 1 + test/integration/integration_test.go | 43 +++ .../ratelimit/config/basic_headers.yaml | 6 + test/service/ratelimit_legacy_test.go | 32 ++- test/service/ratelimit_test.go | 254 +++++++++++++++++- 14 files changed, 648 insertions(+), 192 deletions(-) create mode 100644 test/integration/runtime/current/ratelimit/config/basic_headers.yaml diff --git a/glide.lock b/glide.lock index be3372681..de80bc036 100644 --- a/glide.lock +++ b/glide.lock @@ -1,22 +1,37 @@ -hash: fe989994477e78bb58fd50892030bc6c0d0bbbf95b50012e7e9b10e2b2bd0f8d -updated: 2018-06-07T16:52:24.670498-07:00 +hash: 0d0716e157a0c117523de1fc32cbe28730684a2fc451716cee3ee9612a6ce0a1 +updated: 2018-11-14T19:44:31.103009-08:00 imports: - name: github.com/envoyproxy/data-plane-api - version: 0f8a2a3d456de4d3a29f4e8f7a641fef011d1394 + version: ea5bba9e8ebb84d31c4967c0ee3e16a8f36bc39d +- name: github.com/envoyproxy/go-control-plane + version: 676016db8193d83324a507a986a78cd3642ad713 + subpackages: + - envoy/api/v2/core - name: github.com/fsnotify/fsnotify version: 629574ca2a5df945712d3079857300b5e4da0236 +- name: github.com/gogo/protobuf + version: 636bf0302bc95575d69441b25a2603156ffdddf1 + subpackages: + - gogoproto + - proto + - protoc-gen-gogo/descriptor + - protoc-gen-gogofast + - sortkeys + - types - name: github.com/golang/mock - version: 22bbf0ddf08105dfa364d0a2fa619dfa71014af5 + version: 8a44ef6e8be577e050008c7886f24fc705d709fb subpackages: - gomock - name: github.com/golang/protobuf version: b4deda0973fb4c70b50d226b1af49f3da59f5265 subpackages: + - jsonpb - proto - protoc-gen-go/descriptor - ptypes - ptypes/any - ptypes/duration + - ptypes/struct - ptypes/timestamp - name: github.com/google/protobuf version: 106ffc04be1abf3ff3399f54ccf149815b287dd9 @@ -28,6 +43,8 @@ imports: version: 3d6c1e425f717ee59152524e73b904b67705eeb8 - name: github.com/kelseyhightower/envconfig version: ac12b1f15efba734211a556d8b125110dc538016 +- name: github.com/konsorten/go-windows-terminal-sequences + version: 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242 - name: github.com/lyft/goruntime version: 426ae3581aca6d8b997300525e0d075444bd68d1 subpackages: @@ -41,12 +58,12 @@ imports: subpackages: - validate - name: github.com/mediocregopher/radix.v2 - version: 94360be262532d465b7e4760c7a67195d3319a87 + version: b67df6e626f993b64b3ca9f4b8630900e61002e3 subpackages: - pool - redis - name: github.com/sirupsen/logrus - version: c155da19408a8799da419ed3eeb0cb5db0ad5dbc + version: bcd833dfe83d3cebad139e4a29ed79cb2318bf95 - name: github.com/stretchr/testify version: f390dcf405f7b83c997eac1b06768bb9f44dec18 subpackages: @@ -56,7 +73,7 @@ imports: subpackages: - ssh/terminal - name: golang.org/x/net - version: 1e491301e022f8f977054da4c2d852decd59571f + version: adae6a3d119ae4890b46832a2e88a95adc62b8e7 subpackages: - context - http/httpguts @@ -66,7 +83,7 @@ imports: - internal/timeseries - trace - name: golang.org/x/sys - version: f6cff0780e542efa0c8e864dc8fa522808f6a598 + version: acbc56fc7007d2a01796d5bde54f39e3b3e95945 subpackages: - unix - windows @@ -95,6 +112,8 @@ imports: - encoding/proto - grpclb/grpc_lb_v1/messages - grpclog + - health + - health/grpc_health_v1 - internal - keepalive - metadata diff --git a/glide.yaml b/glide.yaml index f06c71abc..a0ad7d824 100644 --- a/glide.yaml +++ b/glide.yaml @@ -30,10 +30,12 @@ import: - package: github.com/kavu/go_reuseport version: v1.2.0 - package: github.com/envoyproxy/data-plane-api - version: 0f8a2a3d456de4d3a29f4e8f7a641fef011d1394 + version: ea5bba9e8ebb84d31c4967c0ee3e16a8f36bc39d - package: github.com/lyft/protoc-gen-validate version: cae364899cd8c08b83bfdcabf4ce4dd4a68ca6da - package: github.com/google/protobuf version: v3.5.1 - package: github.com/golang/protobuf/proto version: v1.1.0 +- package: github.com/envoyproxy/go-control-plane + version: v0.5.0 diff --git a/proto/envoy/api/v2/ratelimit/ratelimit.pb.go b/proto/envoy/api/v2/ratelimit/ratelimit.pb.go index 51c6cee62..7b9a5f17a 100644 --- a/proto/envoy/api/v2/ratelimit/ratelimit.pb.go +++ b/proto/envoy/api/v2/ratelimit/ratelimit.pb.go @@ -3,10 +3,12 @@ package ratelimit -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/lyft/protoc-gen-validate/validate" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + _ "github.com/lyft/protoc-gen-validate/validate" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -72,16 +74,17 @@ func (m *RateLimitDescriptor) Reset() { *m = RateLimitDescriptor{} } func (m *RateLimitDescriptor) String() string { return proto.CompactTextString(m) } func (*RateLimitDescriptor) ProtoMessage() {} func (*RateLimitDescriptor) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_0246c3aad2c335eb, []int{0} + return fileDescriptor_5684844e04543b8d, []int{0} } + func (m *RateLimitDescriptor) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitDescriptor.Unmarshal(m, b) } func (m *RateLimitDescriptor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitDescriptor.Marshal(b, m, deterministic) } -func (dst *RateLimitDescriptor) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitDescriptor.Merge(dst, src) +func (m *RateLimitDescriptor) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitDescriptor.Merge(m, src) } func (m *RateLimitDescriptor) XXX_Size() int { return xxx_messageInfo_RateLimitDescriptor.Size(m) @@ -113,16 +116,17 @@ func (m *RateLimitDescriptor_Entry) Reset() { *m = RateLimitDescriptor_E func (m *RateLimitDescriptor_Entry) String() string { return proto.CompactTextString(m) } func (*RateLimitDescriptor_Entry) ProtoMessage() {} func (*RateLimitDescriptor_Entry) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_0246c3aad2c335eb, []int{0, 0} + return fileDescriptor_5684844e04543b8d, []int{0, 0} } + func (m *RateLimitDescriptor_Entry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitDescriptor_Entry.Unmarshal(m, b) } func (m *RateLimitDescriptor_Entry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitDescriptor_Entry.Marshal(b, m, deterministic) } -func (dst *RateLimitDescriptor_Entry) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitDescriptor_Entry.Merge(dst, src) +func (m *RateLimitDescriptor_Entry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitDescriptor_Entry.Merge(m, src) } func (m *RateLimitDescriptor_Entry) XXX_Size() int { return xxx_messageInfo_RateLimitDescriptor_Entry.Size(m) @@ -153,10 +157,10 @@ func init() { } func init() { - proto.RegisterFile("envoy/api/v2/ratelimit/ratelimit.proto", fileDescriptor_ratelimit_0246c3aad2c335eb) + proto.RegisterFile("envoy/api/v2/ratelimit/ratelimit.proto", fileDescriptor_5684844e04543b8d) } -var fileDescriptor_ratelimit_0246c3aad2c335eb = []byte{ +var fileDescriptor_5684844e04543b8d = []byte{ // 211 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4b, 0xcd, 0x2b, 0xcb, 0xaf, 0xd4, 0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0x33, 0xd2, 0x2f, 0x4a, 0x2c, 0x49, 0xcd, 0xc9, 0xcc, diff --git a/proto/envoy/service/ratelimit/v2/rls.pb.go b/proto/envoy/service/ratelimit/v2/rls.pb.go index c2479788e..632c3be3d 100644 --- a/proto/envoy/service/ratelimit/v2/rls.pb.go +++ b/proto/envoy/service/ratelimit/v2/rls.pb.go @@ -3,15 +3,15 @@ package v2 -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/lyft/protoc-gen-validate/validate" -import ratelimit "github.com/lyft/ratelimit/proto/envoy/api/v2/ratelimit" - import ( - context "golang.org/x/net/context" + context "context" + fmt "fmt" + core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" + proto "github.com/golang/protobuf/proto" + _ "github.com/lyft/protoc-gen-validate/validate" + ratelimit "github.com/lyft/ratelimit/proto/envoy/api/v2/ratelimit" grpc "google.golang.org/grpc" + math "math" ) // Reference imports to suppress errors if they are not otherwise used. @@ -38,6 +38,7 @@ var RateLimitResponse_Code_name = map[int32]string{ 1: "OK", 2: "OVER_LIMIT", } + var RateLimitResponse_Code_value = map[string]int32{ "UNKNOWN": 0, "OK": 1, @@ -47,8 +48,9 @@ var RateLimitResponse_Code_value = map[string]int32{ func (x RateLimitResponse_Code) String() string { return proto.EnumName(RateLimitResponse_Code_name, int32(x)) } + func (RateLimitResponse_Code) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{1, 0} + return fileDescriptor_1de95711edb19ee8, []int{1, 0} } type RateLimitResponse_RateLimit_Unit int32 @@ -68,6 +70,7 @@ var RateLimitResponse_RateLimit_Unit_name = map[int32]string{ 3: "HOUR", 4: "DAY", } + var RateLimitResponse_RateLimit_Unit_value = map[string]int32{ "UNKNOWN": 0, "SECOND": 1, @@ -79,8 +82,9 @@ var RateLimitResponse_RateLimit_Unit_value = map[string]int32{ func (x RateLimitResponse_RateLimit_Unit) String() string { return proto.EnumName(RateLimitResponse_RateLimit_Unit_name, int32(x)) } + func (RateLimitResponse_RateLimit_Unit) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{1, 0, 0} + return fileDescriptor_1de95711edb19ee8, []int{1, 0, 0} } // Main message for a rate limit request. The rate limit service is designed to be fully generic @@ -111,16 +115,17 @@ func (m *RateLimitRequest) Reset() { *m = RateLimitRequest{} } func (m *RateLimitRequest) String() string { return proto.CompactTextString(m) } func (*RateLimitRequest) ProtoMessage() {} func (*RateLimitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{0} + return fileDescriptor_1de95711edb19ee8, []int{0} } + func (m *RateLimitRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitRequest.Unmarshal(m, b) } func (m *RateLimitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitRequest.Marshal(b, m, deterministic) } -func (dst *RateLimitRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitRequest.Merge(dst, src) +func (m *RateLimitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitRequest.Merge(m, src) } func (m *RateLimitRequest) XXX_Size() int { return xxx_messageInfo_RateLimitRequest.Size(m) @@ -161,26 +166,29 @@ type RateLimitResponse struct { // A list of DescriptorStatus messages which matches the length of the descriptor list passed // in the RateLimitRequest. This can be used by the caller to determine which individual // descriptors failed and/or what the currently configured limits are for all of them. - Statuses []*RateLimitResponse_DescriptorStatus `protobuf:"bytes,2,rep,name=statuses,proto3" json:"statuses,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Statuses []*RateLimitResponse_DescriptorStatus `protobuf:"bytes,2,rep,name=statuses,proto3" json:"statuses,omitempty"` + // A list of headers to add to the response + Headers []*core.HeaderValue `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *RateLimitResponse) Reset() { *m = RateLimitResponse{} } func (m *RateLimitResponse) String() string { return proto.CompactTextString(m) } func (*RateLimitResponse) ProtoMessage() {} func (*RateLimitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{1} + return fileDescriptor_1de95711edb19ee8, []int{1} } + func (m *RateLimitResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitResponse.Unmarshal(m, b) } func (m *RateLimitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitResponse.Marshal(b, m, deterministic) } -func (dst *RateLimitResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitResponse.Merge(dst, src) +func (m *RateLimitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitResponse.Merge(m, src) } func (m *RateLimitResponse) XXX_Size() int { return xxx_messageInfo_RateLimitResponse.Size(m) @@ -205,6 +213,13 @@ func (m *RateLimitResponse) GetStatuses() []*RateLimitResponse_DescriptorStatus return nil } +func (m *RateLimitResponse) GetHeaders() []*core.HeaderValue { + if m != nil { + return m.Headers + } + return nil +} + // Defines an actual rate limit in terms of requests per unit of time and the unit itself. type RateLimitResponse_RateLimit struct { RequestsPerUnit uint32 `protobuf:"varint,1,opt,name=requests_per_unit,json=requestsPerUnit,proto3" json:"requests_per_unit,omitempty"` @@ -218,16 +233,17 @@ func (m *RateLimitResponse_RateLimit) Reset() { *m = RateLimitResponse_R func (m *RateLimitResponse_RateLimit) String() string { return proto.CompactTextString(m) } func (*RateLimitResponse_RateLimit) ProtoMessage() {} func (*RateLimitResponse_RateLimit) Descriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{1, 0} + return fileDescriptor_1de95711edb19ee8, []int{1, 0} } + func (m *RateLimitResponse_RateLimit) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitResponse_RateLimit.Unmarshal(m, b) } func (m *RateLimitResponse_RateLimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitResponse_RateLimit.Marshal(b, m, deterministic) } -func (dst *RateLimitResponse_RateLimit) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitResponse_RateLimit.Merge(dst, src) +func (m *RateLimitResponse_RateLimit) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitResponse_RateLimit.Merge(m, src) } func (m *RateLimitResponse_RateLimit) XXX_Size() int { return xxx_messageInfo_RateLimitResponse_RateLimit.Size(m) @@ -268,16 +284,17 @@ func (m *RateLimitResponse_DescriptorStatus) Reset() { *m = RateLimitRes func (m *RateLimitResponse_DescriptorStatus) String() string { return proto.CompactTextString(m) } func (*RateLimitResponse_DescriptorStatus) ProtoMessage() {} func (*RateLimitResponse_DescriptorStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_rls_3ed4bc1687205314, []int{1, 1} + return fileDescriptor_1de95711edb19ee8, []int{1, 1} } + func (m *RateLimitResponse_DescriptorStatus) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Unmarshal(m, b) } func (m *RateLimitResponse_DescriptorStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Marshal(b, m, deterministic) } -func (dst *RateLimitResponse_DescriptorStatus) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitResponse_DescriptorStatus.Merge(dst, src) +func (m *RateLimitResponse_DescriptorStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitResponse_DescriptorStatus.Merge(m, src) } func (m *RateLimitResponse_DescriptorStatus) XXX_Size() int { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Size(m) @@ -310,12 +327,55 @@ func (m *RateLimitResponse_DescriptorStatus) GetLimitRemaining() uint32 { } func init() { + proto.RegisterEnum("envoy.service.ratelimit.v2.RateLimitResponse_Code", RateLimitResponse_Code_name, RateLimitResponse_Code_value) + proto.RegisterEnum("envoy.service.ratelimit.v2.RateLimitResponse_RateLimit_Unit", RateLimitResponse_RateLimit_Unit_name, RateLimitResponse_RateLimit_Unit_value) proto.RegisterType((*RateLimitRequest)(nil), "envoy.service.ratelimit.v2.RateLimitRequest") proto.RegisterType((*RateLimitResponse)(nil), "envoy.service.ratelimit.v2.RateLimitResponse") proto.RegisterType((*RateLimitResponse_RateLimit)(nil), "envoy.service.ratelimit.v2.RateLimitResponse.RateLimit") proto.RegisterType((*RateLimitResponse_DescriptorStatus)(nil), "envoy.service.ratelimit.v2.RateLimitResponse.DescriptorStatus") - proto.RegisterEnum("envoy.service.ratelimit.v2.RateLimitResponse_Code", RateLimitResponse_Code_name, RateLimitResponse_Code_value) - proto.RegisterEnum("envoy.service.ratelimit.v2.RateLimitResponse_RateLimit_Unit", RateLimitResponse_RateLimit_Unit_name, RateLimitResponse_RateLimit_Unit_value) +} + +func init() { + proto.RegisterFile("envoy/service/ratelimit/v2/rls.proto", fileDescriptor_1de95711edb19ee8) +} + +var fileDescriptor_1de95711edb19ee8 = []byte{ + // 560 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0x6b, 0xc7, 0xa4, 0xed, 0xb8, 0x7f, 0xdc, 0x3d, 0x40, 0x64, 0x21, 0xa8, 0x22, 0x04, + 0x15, 0x05, 0x5b, 0x32, 0x07, 0x38, 0xa0, 0x4a, 0xa5, 0x2d, 0x6a, 0xd5, 0x36, 0xa9, 0x36, 0x4d, + 0x11, 0x15, 0x92, 0xb5, 0x8d, 0x47, 0x64, 0x25, 0xd7, 0x36, 0xbb, 0x6b, 0x4b, 0xdc, 0x79, 0x0a, + 0xde, 0x88, 0x87, 0xe0, 0xc0, 0x9b, 0x20, 0xaf, 0x1d, 0x27, 0x01, 0x21, 0x11, 0xb8, 0x79, 0x67, + 0xe6, 0xfb, 0x65, 0xbe, 0x99, 0xdd, 0xc0, 0x23, 0x4c, 0x8a, 0xf4, 0xb3, 0x2f, 0x51, 0x14, 0x7c, + 0x84, 0xbe, 0x60, 0x0a, 0x63, 0x7e, 0xcb, 0x95, 0x5f, 0x04, 0xbe, 0x88, 0xa5, 0x97, 0x89, 0x54, + 0xa5, 0xc4, 0xd5, 0x55, 0x5e, 0x5d, 0xe5, 0x35, 0x55, 0x5e, 0x11, 0xb8, 0xf7, 0x2b, 0x02, 0xcb, + 0x78, 0xa9, 0x19, 0xa5, 0x02, 0xfd, 0x1b, 0x26, 0xb1, 0x52, 0xba, 0x8f, 0xe7, 0xb2, 0x53, 0xfc, + 0x14, 0x51, 0xd5, 0xdd, 0x2b, 0x58, 0xcc, 0x23, 0xa6, 0xd0, 0x9f, 0x7c, 0x54, 0x89, 0xee, 0x57, + 0x03, 0x1c, 0xca, 0x14, 0x9e, 0x95, 0xc5, 0x14, 0x3f, 0xe5, 0x28, 0x15, 0xb9, 0x0b, 0xed, 0x28, + 0xbd, 0x65, 0x3c, 0xe9, 0x18, 0xdb, 0xc6, 0xce, 0x2a, 0xad, 0x4f, 0xe4, 0x1c, 0xec, 0x08, 0xe5, + 0x48, 0xf0, 0x4c, 0xa5, 0x42, 0x76, 0xcc, 0xed, 0xd6, 0x8e, 0x1d, 0xec, 0x7a, 0x55, 0xf7, 0x2c, + 0xe3, 0x5e, 0x11, 0xcc, 0x34, 0xdf, 0x60, 0x0f, 0x1b, 0x0d, 0x9d, 0xd5, 0x93, 0x87, 0x60, 0x8f, + 0xb9, 0x92, 0x21, 0x8b, 0x22, 0x4c, 0xa2, 0x4e, 0x6b, 0xdb, 0xd8, 0x59, 0xa7, 0x50, 0x86, 0xf6, + 0x75, 0xa4, 0xfb, 0xfd, 0x0e, 0x6c, 0xcd, 0x34, 0x27, 0xb3, 0x34, 0x91, 0x48, 0x86, 0xb0, 0x96, + 0x16, 0x28, 0x58, 0x1c, 0x87, 0xa3, 0x34, 0x42, 0xdd, 0xe3, 0x46, 0x10, 0x78, 0x7f, 0x1e, 0xa2, + 0xf7, 0x1b, 0xc4, 0x3b, 0x48, 0x23, 0xa4, 0x76, 0xcd, 0x29, 0x0f, 0xe4, 0x1a, 0x56, 0xa4, 0x62, + 0x2a, 0x97, 0x38, 0x71, 0xb6, 0xb7, 0x18, 0x72, 0x6a, 0x73, 0xa0, 0x39, 0xb4, 0xe1, 0x91, 0x57, + 0xb0, 0x3c, 0x46, 0x16, 0xa1, 0x90, 0x9d, 0x96, 0x46, 0x3f, 0x98, 0x1f, 0x5a, 0xb9, 0x56, 0xef, + 0x58, 0x57, 0x5c, 0xb1, 0x38, 0x47, 0x3a, 0x29, 0x77, 0xbf, 0x19, 0xb0, 0xda, 0xfc, 0x14, 0x79, + 0x0a, 0x5b, 0xa2, 0xda, 0x91, 0x0c, 0x33, 0x14, 0x61, 0x9e, 0x70, 0xa5, 0xfd, 0xaf, 0xd3, 0xcd, + 0x49, 0xe2, 0x02, 0xc5, 0x30, 0xe1, 0x8a, 0x5c, 0x80, 0xa5, 0xd3, 0xa6, 0x1e, 0xcf, 0xeb, 0xc5, + 0xbc, 0x34, 0x11, 0xaf, 0x64, 0x51, 0x4d, 0xea, 0xee, 0x81, 0xa5, 0xc9, 0x36, 0x2c, 0x0f, 0x7b, + 0xa7, 0xbd, 0xfe, 0xbb, 0x9e, 0xb3, 0x44, 0x00, 0xda, 0x83, 0xa3, 0x83, 0x7e, 0xef, 0xd0, 0x31, + 0xca, 0xef, 0xf3, 0x93, 0xde, 0xf0, 0xf2, 0xc8, 0x31, 0xc9, 0x0a, 0x58, 0xc7, 0xfd, 0x21, 0x75, + 0x5a, 0x64, 0x19, 0x5a, 0x87, 0xfb, 0xef, 0x1d, 0xcb, 0xfd, 0x61, 0x80, 0xf3, 0xeb, 0x90, 0xc8, + 0x5b, 0xb0, 0xfe, 0x73, 0x8b, 0x5a, 0x4f, 0x3e, 0xc0, 0xfa, 0x28, 0x17, 0x02, 0x13, 0x15, 0x6a, + 0x81, 0xf6, 0x6d, 0x07, 0x2f, 0xff, 0xd1, 0x37, 0x5d, 0xab, 0x69, 0xd5, 0xe0, 0x9f, 0xc0, 0xa6, + 0x56, 0x85, 0x02, 0xcb, 0x97, 0xc0, 0x93, 0x8f, 0xf5, 0x75, 0xdd, 0x88, 0x2b, 0x7d, 0x1d, 0xed, + 0xee, 0x82, 0xa5, 0x6f, 0xd3, 0xdc, 0x8c, 0xda, 0x60, 0xf6, 0x4f, 0x1d, 0x83, 0x6c, 0x00, 0xf4, + 0xaf, 0x8e, 0x68, 0x78, 0x76, 0x72, 0x7e, 0x72, 0xe9, 0x98, 0xc1, 0x97, 0xd9, 0xc7, 0x37, 0xa8, + 0x3a, 0x24, 0x19, 0x6c, 0x0e, 0xc6, 0x69, 0x1e, 0x47, 0xd3, 0xb5, 0x3f, 0xfb, 0x4b, 0x13, 0xfa, + 0x02, 0xb8, 0xcf, 0x17, 0xb2, 0xdc, 0x5d, 0x7a, 0x63, 0x5d, 0x9b, 0x45, 0x70, 0xd3, 0xd6, 0x7f, + 0x08, 0x2f, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0xa5, 0x27, 0x04, 0x4f, 0xb3, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -391,44 +451,3 @@ var _RateLimitService_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "envoy/service/ratelimit/v2/rls.proto", } - -func init() { - proto.RegisterFile("envoy/service/ratelimit/v2/rls.proto", fileDescriptor_rls_3ed4bc1687205314) -} - -var fileDescriptor_rls_3ed4bc1687205314 = []byte{ - // 518 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0x41, 0x6f, 0xd3, 0x30, - 0x14, 0xc7, 0x97, 0x34, 0x74, 0xdb, 0xcb, 0xda, 0x66, 0x3e, 0x40, 0x95, 0x0b, 0x55, 0x85, 0xa0, - 0x62, 0xe0, 0x4a, 0xe1, 0xc0, 0x05, 0x4d, 0x1a, 0x6b, 0x11, 0xd5, 0xd6, 0x76, 0x72, 0x57, 0x10, - 0x13, 0x52, 0x64, 0x1a, 0x8b, 0x59, 0xca, 0xe2, 0x60, 0xbb, 0x91, 0xb8, 0xf3, 0x29, 0xb8, 0xf2, - 0x69, 0xf8, 0x18, 0x7c, 0x13, 0x14, 0x27, 0x4d, 0x0b, 0x08, 0x89, 0xb2, 0x9b, 0xfd, 0xde, 0xfb, - 0xff, 0xf4, 0xfe, 0xcf, 0xcf, 0xf0, 0x80, 0x25, 0x99, 0xf8, 0xdc, 0x57, 0x4c, 0x66, 0x7c, 0xc1, - 0xfa, 0x92, 0x6a, 0x16, 0xf3, 0x1b, 0xae, 0xfb, 0x59, 0xd0, 0x97, 0xb1, 0xc2, 0xa9, 0x14, 0x5a, - 0x20, 0xdf, 0x54, 0xe1, 0xb2, 0x0a, 0x57, 0x55, 0x38, 0x0b, 0xfc, 0x87, 0x05, 0x81, 0xa6, 0xdc, - 0x68, 0x2a, 0xc0, 0xba, 0xc8, 0x30, 0xfc, 0x7b, 0x19, 0x8d, 0x79, 0x44, 0x35, 0xeb, 0xaf, 0x0e, - 0x45, 0xa2, 0xfb, 0xd5, 0x02, 0x8f, 0x50, 0xcd, 0xce, 0xf3, 0x62, 0xc2, 0x3e, 0x2d, 0x99, 0xd2, - 0xe8, 0x2e, 0xd4, 0x23, 0x71, 0x43, 0x79, 0xd2, 0xb6, 0x3a, 0x56, 0x6f, 0x9f, 0x94, 0x37, 0x34, - 0x06, 0x37, 0x62, 0x6a, 0x21, 0x79, 0xaa, 0x85, 0x54, 0x6d, 0xbb, 0x53, 0xeb, 0xb9, 0xc1, 0x11, - 0x2e, 0xfa, 0xa3, 0x29, 0xc7, 0x59, 0xb0, 0xd1, 0x5e, 0x85, 0x1d, 0x54, 0x1a, 0xb2, 0xa9, 0x47, - 0xf7, 0xc1, 0xbd, 0xe6, 0x5a, 0x85, 0x34, 0x8a, 0x58, 0x12, 0xb5, 0x6b, 0x1d, 0xab, 0xd7, 0x20, - 0x90, 0x87, 0x4e, 0x4c, 0xa4, 0xfb, 0xed, 0x0e, 0x1c, 0x6e, 0x34, 0xa7, 0x52, 0x91, 0x28, 0x86, - 0xe6, 0x70, 0x20, 0x32, 0x26, 0x69, 0x1c, 0x87, 0x0b, 0x11, 0x31, 0xd3, 0x63, 0x33, 0x08, 0xf0, - 0xdf, 0xc7, 0x84, 0xff, 0x80, 0xe0, 0x53, 0x11, 0x31, 0xe2, 0x96, 0x9c, 0xfc, 0x82, 0xae, 0x60, - 0x4f, 0x69, 0xaa, 0x97, 0x8a, 0xad, 0x9c, 0x1d, 0x6f, 0x87, 0x5c, 0xdb, 0x9c, 0x19, 0x0e, 0xa9, - 0x78, 0xfe, 0x77, 0x0b, 0xf6, 0x2b, 0x01, 0x7a, 0x0c, 0x87, 0xb2, 0x98, 0xb4, 0x0a, 0x53, 0x26, - 0xc3, 0x65, 0xc2, 0xb5, 0x71, 0xd1, 0x20, 0xad, 0x55, 0xe2, 0x82, 0xc9, 0x79, 0xc2, 0x35, 0xba, - 0x00, 0xc7, 0xa4, 0x6d, 0x63, 0xf2, 0xc5, 0x76, 0x1d, 0x55, 0x11, 0x9c, 0xb3, 0x88, 0x21, 0x75, - 0x8f, 0xc1, 0x31, 0x64, 0x17, 0x76, 0xe7, 0x93, 0xb3, 0xc9, 0xf4, 0xed, 0xc4, 0xdb, 0x41, 0x00, - 0xf5, 0xd9, 0xf0, 0x74, 0x3a, 0x19, 0x78, 0x56, 0x7e, 0x1e, 0x8f, 0x26, 0xf3, 0xcb, 0xa1, 0x67, - 0xa3, 0x3d, 0x70, 0x5e, 0x4f, 0xe7, 0xc4, 0xab, 0xa1, 0x5d, 0xa8, 0x0d, 0x4e, 0xde, 0x79, 0x8e, - 0xff, 0xc3, 0x02, 0xef, 0x77, 0xab, 0xe8, 0x15, 0x38, 0xb7, 0x7c, 0x0b, 0xa3, 0x47, 0xef, 0xa1, - 0xb1, 0x58, 0x4a, 0xc9, 0x12, 0x1d, 0x1a, 0x81, 0xf1, 0xed, 0x06, 0xcf, 0xff, 0xd3, 0x37, 0x39, - 0x28, 0x69, 0xc5, 0xe0, 0x1f, 0x41, 0xcb, 0xa8, 0x42, 0xc9, 0xf2, 0x7d, 0xe6, 0xc9, 0xc7, 0x72, - 0xe9, 0x9a, 0x71, 0xa1, 0x2f, 0xa3, 0xdd, 0x23, 0x70, 0xcc, 0x4e, 0xfc, 0x32, 0xa3, 0x3a, 0xd8, - 0xd3, 0x33, 0xcf, 0x42, 0x4d, 0x80, 0xe9, 0x9b, 0x21, 0x09, 0xcf, 0x47, 0xe3, 0xd1, 0xa5, 0x67, - 0x07, 0x5f, 0x36, 0xbf, 0xd0, 0xac, 0xe8, 0x10, 0xa5, 0xd0, 0x9a, 0x5d, 0x8b, 0x65, 0x1c, 0xad, - 0x9f, 0xfd, 0xc9, 0x3f, 0x9a, 0x30, 0x0b, 0xe0, 0x3f, 0xdd, 0xca, 0x72, 0x77, 0xe7, 0xa5, 0x73, - 0x65, 0x67, 0xc1, 0x87, 0xba, 0xf9, 0xd6, 0xcf, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x04, - 0xd1, 0xd9, 0x5b, 0x04, 0x00, 0x00, -} diff --git a/proto/ratelimit/ratelimit.pb.go b/proto/ratelimit/ratelimit.pb.go index 9c44edff9..d39948a9f 100644 --- a/proto/ratelimit/ratelimit.pb.go +++ b/proto/ratelimit/ratelimit.pb.go @@ -3,13 +3,12 @@ package ratelimit -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + math "math" ) // Reference imports to suppress errors if they are not otherwise used. @@ -40,6 +39,7 @@ var RateLimit_Unit_name = map[int32]string{ 3: "HOUR", 4: "DAY", } + var RateLimit_Unit_value = map[string]int32{ "UNKNOWN": 0, "SECOND": 1, @@ -51,8 +51,9 @@ var RateLimit_Unit_value = map[string]int32{ func (x RateLimit_Unit) String() string { return proto.EnumName(RateLimit_Unit_name, int32(x)) } + func (RateLimit_Unit) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{2, 0} + return fileDescriptor_5009d3be233d3ead, []int{2, 0} } type RateLimitResponse_Code int32 @@ -68,6 +69,7 @@ var RateLimitResponse_Code_name = map[int32]string{ 1: "OK", 2: "OVER_LIMIT", } + var RateLimitResponse_Code_value = map[string]int32{ "UNKNOWN": 0, "OK": 1, @@ -77,8 +79,9 @@ var RateLimitResponse_Code_value = map[string]int32{ func (x RateLimitResponse_Code) String() string { return proto.EnumName(RateLimitResponse_Code_name, int32(x)) } + func (RateLimitResponse_Code) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{3, 0} + return fileDescriptor_5009d3be233d3ead, []int{3, 0} } // Main message for a rate limit request. The rate limit service is designed to be fully generic @@ -108,16 +111,17 @@ func (m *RateLimitRequest) Reset() { *m = RateLimitRequest{} } func (m *RateLimitRequest) String() string { return proto.CompactTextString(m) } func (*RateLimitRequest) ProtoMessage() {} func (*RateLimitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{0} + return fileDescriptor_5009d3be233d3ead, []int{0} } + func (m *RateLimitRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitRequest.Unmarshal(m, b) } func (m *RateLimitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitRequest.Marshal(b, m, deterministic) } -func (dst *RateLimitRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitRequest.Merge(dst, src) +func (m *RateLimitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitRequest.Merge(m, src) } func (m *RateLimitRequest) XXX_Size() int { return xxx_messageInfo_RateLimitRequest.Size(m) @@ -181,16 +185,17 @@ func (m *RateLimitDescriptor) Reset() { *m = RateLimitDescriptor{} } func (m *RateLimitDescriptor) String() string { return proto.CompactTextString(m) } func (*RateLimitDescriptor) ProtoMessage() {} func (*RateLimitDescriptor) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{1} + return fileDescriptor_5009d3be233d3ead, []int{1} } + func (m *RateLimitDescriptor) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitDescriptor.Unmarshal(m, b) } func (m *RateLimitDescriptor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitDescriptor.Marshal(b, m, deterministic) } -func (dst *RateLimitDescriptor) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitDescriptor.Merge(dst, src) +func (m *RateLimitDescriptor) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitDescriptor.Merge(m, src) } func (m *RateLimitDescriptor) XXX_Size() int { return xxx_messageInfo_RateLimitDescriptor.Size(m) @@ -220,16 +225,17 @@ func (m *RateLimitDescriptor_Entry) Reset() { *m = RateLimitDescriptor_E func (m *RateLimitDescriptor_Entry) String() string { return proto.CompactTextString(m) } func (*RateLimitDescriptor_Entry) ProtoMessage() {} func (*RateLimitDescriptor_Entry) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{1, 0} + return fileDescriptor_5009d3be233d3ead, []int{1, 0} } + func (m *RateLimitDescriptor_Entry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitDescriptor_Entry.Unmarshal(m, b) } func (m *RateLimitDescriptor_Entry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitDescriptor_Entry.Marshal(b, m, deterministic) } -func (dst *RateLimitDescriptor_Entry) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitDescriptor_Entry.Merge(dst, src) +func (m *RateLimitDescriptor_Entry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitDescriptor_Entry.Merge(m, src) } func (m *RateLimitDescriptor_Entry) XXX_Size() int { return xxx_messageInfo_RateLimitDescriptor_Entry.Size(m) @@ -267,16 +273,17 @@ func (m *RateLimit) Reset() { *m = RateLimit{} } func (m *RateLimit) String() string { return proto.CompactTextString(m) } func (*RateLimit) ProtoMessage() {} func (*RateLimit) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{2} + return fileDescriptor_5009d3be233d3ead, []int{2} } + func (m *RateLimit) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimit.Unmarshal(m, b) } func (m *RateLimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimit.Marshal(b, m, deterministic) } -func (dst *RateLimit) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimit.Merge(dst, src) +func (m *RateLimit) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimit.Merge(m, src) } func (m *RateLimit) XXX_Size() int { return xxx_messageInfo_RateLimit.Size(m) @@ -319,16 +326,17 @@ func (m *RateLimitResponse) Reset() { *m = RateLimitResponse{} } func (m *RateLimitResponse) String() string { return proto.CompactTextString(m) } func (*RateLimitResponse) ProtoMessage() {} func (*RateLimitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{3} + return fileDescriptor_5009d3be233d3ead, []int{3} } + func (m *RateLimitResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitResponse.Unmarshal(m, b) } func (m *RateLimitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitResponse.Marshal(b, m, deterministic) } -func (dst *RateLimitResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitResponse.Merge(dst, src) +func (m *RateLimitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitResponse.Merge(m, src) } func (m *RateLimitResponse) XXX_Size() int { return xxx_messageInfo_RateLimitResponse.Size(m) @@ -369,16 +377,17 @@ func (m *RateLimitResponse_DescriptorStatus) Reset() { *m = RateLimitRes func (m *RateLimitResponse_DescriptorStatus) String() string { return proto.CompactTextString(m) } func (*RateLimitResponse_DescriptorStatus) ProtoMessage() {} func (*RateLimitResponse_DescriptorStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_ratelimit_8ec600a45de499be, []int{3, 0} + return fileDescriptor_5009d3be233d3ead, []int{3, 0} } + func (m *RateLimitResponse_DescriptorStatus) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Unmarshal(m, b) } func (m *RateLimitResponse_DescriptorStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Marshal(b, m, deterministic) } -func (dst *RateLimitResponse_DescriptorStatus) XXX_Merge(src proto.Message) { - xxx_messageInfo_RateLimitResponse_DescriptorStatus.Merge(dst, src) +func (m *RateLimitResponse_DescriptorStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_RateLimitResponse_DescriptorStatus.Merge(m, src) } func (m *RateLimitResponse_DescriptorStatus) XXX_Size() int { return xxx_messageInfo_RateLimitResponse_DescriptorStatus.Size(m) @@ -411,14 +420,54 @@ func (m *RateLimitResponse_DescriptorStatus) GetLimitRemaining() uint32 { } func init() { + proto.RegisterEnum("pb.lyft.ratelimit.RateLimit_Unit", RateLimit_Unit_name, RateLimit_Unit_value) + proto.RegisterEnum("pb.lyft.ratelimit.RateLimitResponse_Code", RateLimitResponse_Code_name, RateLimitResponse_Code_value) proto.RegisterType((*RateLimitRequest)(nil), "pb.lyft.ratelimit.RateLimitRequest") proto.RegisterType((*RateLimitDescriptor)(nil), "pb.lyft.ratelimit.RateLimitDescriptor") proto.RegisterType((*RateLimitDescriptor_Entry)(nil), "pb.lyft.ratelimit.RateLimitDescriptor.Entry") proto.RegisterType((*RateLimit)(nil), "pb.lyft.ratelimit.RateLimit") proto.RegisterType((*RateLimitResponse)(nil), "pb.lyft.ratelimit.RateLimitResponse") proto.RegisterType((*RateLimitResponse_DescriptorStatus)(nil), "pb.lyft.ratelimit.RateLimitResponse.DescriptorStatus") - proto.RegisterEnum("pb.lyft.ratelimit.RateLimit_Unit", RateLimit_Unit_name, RateLimit_Unit_value) - proto.RegisterEnum("pb.lyft.ratelimit.RateLimitResponse_Code", RateLimitResponse_Code_name, RateLimitResponse_Code_value) +} + +func init() { proto.RegisterFile("proto/ratelimit/ratelimit.proto", fileDescriptor_5009d3be233d3ead) } + +var fileDescriptor_5009d3be233d3ead = []byte{ + // 532 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xdd, 0x8e, 0xd2, 0x40, + 0x14, 0xde, 0xa1, 0x5d, 0x58, 0x4e, 0x17, 0x28, 0xa3, 0x31, 0x84, 0x98, 0x2c, 0x56, 0xa3, 0xf8, + 0x93, 0x6e, 0x82, 0xd9, 0x4b, 0x4d, 0x70, 0xc1, 0x2c, 0x59, 0x16, 0x74, 0x58, 0x34, 0x7a, 0x61, + 0xd3, 0xa5, 0x47, 0xb7, 0xb1, 0xdb, 0xe2, 0xcc, 0x94, 0x84, 0x3b, 0x9f, 0xc0, 0x3b, 0x1f, 0xc0, + 0x17, 0xf0, 0x0d, 0x7c, 0x37, 0xd3, 0xa1, 0x14, 0xfc, 0x09, 0x21, 0x7b, 0x77, 0xfe, 0xbe, 0xef, + 0x9c, 0x9e, 0xef, 0x4c, 0xe1, 0x60, 0xca, 0x23, 0x19, 0x1d, 0x72, 0x57, 0x62, 0xe0, 0x5f, 0xf9, + 0x72, 0x65, 0xd9, 0x2a, 0x43, 0xab, 0xd3, 0x0b, 0x3b, 0x98, 0x7f, 0x94, 0x76, 0x96, 0xb0, 0xbe, + 0x13, 0x30, 0x99, 0x2b, 0xb1, 0x9f, 0x78, 0x0c, 0xbf, 0xc4, 0x28, 0x24, 0xbd, 0x05, 0x79, 0x2f, + 0xba, 0x72, 0xfd, 0xb0, 0x46, 0x1a, 0xa4, 0x59, 0x64, 0xa9, 0x47, 0x4f, 0xc0, 0xf0, 0x50, 0x4c, + 0xb8, 0x3f, 0x95, 0x11, 0x17, 0xb5, 0x5c, 0x43, 0x6b, 0x1a, 0xad, 0xfb, 0xf6, 0x3f, 0xac, 0x76, + 0xc6, 0xd8, 0xc9, 0xca, 0xd9, 0x3a, 0x94, 0x1e, 0x80, 0x71, 0xe9, 0x4b, 0xe1, 0xb8, 0x9e, 0x87, + 0xa1, 0x57, 0xd3, 0x1a, 0xa4, 0x59, 0x62, 0x90, 0x84, 0xda, 0x2a, 0x62, 0x7d, 0x23, 0x70, 0xe3, + 0x3f, 0x2c, 0xf4, 0x25, 0x14, 0x30, 0x94, 0xdc, 0x47, 0x51, 0x23, 0xaa, 0xfd, 0x93, 0xed, 0xda, + 0xdb, 0xdd, 0x50, 0xf2, 0x39, 0x5b, 0x82, 0xeb, 0x87, 0xb0, 0xab, 0x22, 0xd4, 0x04, 0xed, 0x33, + 0xce, 0xd3, 0x0f, 0x4d, 0x4c, 0x7a, 0x13, 0x76, 0x67, 0x6e, 0x10, 0x63, 0x2d, 0xa7, 0x62, 0x0b, + 0xc7, 0xfa, 0x49, 0xa0, 0x98, 0xf1, 0xd2, 0x47, 0x50, 0xe5, 0x8b, 0x65, 0x09, 0x67, 0x8a, 0xdc, + 0x89, 0x43, 0x5f, 0x2a, 0x8e, 0x12, 0xab, 0x2c, 0x13, 0xaf, 0x90, 0x8f, 0x43, 0x5f, 0xd2, 0x23, + 0xd0, 0x55, 0x3a, 0xa1, 0x2b, 0xb7, 0xee, 0x6c, 0x9a, 0xd7, 0x4e, 0x00, 0x4c, 0x95, 0x5b, 0xcf, + 0x41, 0x57, 0x70, 0x03, 0x0a, 0xe3, 0xc1, 0xe9, 0x60, 0xf8, 0x76, 0x60, 0xee, 0x50, 0x80, 0xfc, + 0xa8, 0x7b, 0x3c, 0x1c, 0x74, 0x4c, 0x92, 0xd8, 0x67, 0xbd, 0xc1, 0xf8, 0xbc, 0x6b, 0xe6, 0xe8, + 0x1e, 0xe8, 0x27, 0xc3, 0x31, 0x33, 0x35, 0x5a, 0x00, 0xad, 0xd3, 0x7e, 0x67, 0xea, 0xd6, 0x0f, + 0x0d, 0xaa, 0x6b, 0xca, 0x8a, 0x69, 0x14, 0x0a, 0xa4, 0x7d, 0xd8, 0x8f, 0x66, 0xc8, 0xdd, 0x20, + 0x70, 0x26, 0x91, 0x87, 0x6a, 0xe6, 0x72, 0xeb, 0xe1, 0xa6, 0xa1, 0x96, 0x58, 0xfb, 0x38, 0xf2, + 0x90, 0x19, 0x29, 0x3c, 0x71, 0xe8, 0x6b, 0xd8, 0x13, 0xd2, 0x95, 0xb1, 0xc0, 0xe5, 0x35, 0x1c, + 0x6d, 0xc5, 0xb4, 0xd2, 0x65, 0xa4, 0xe0, 0x2c, 0xa3, 0xa9, 0xff, 0x22, 0x60, 0xfe, 0x9d, 0xa6, + 0xcf, 0x40, 0xbf, 0xde, 0xb4, 0x0a, 0x46, 0xdb, 0x50, 0x9a, 0xc4, 0x9c, 0x63, 0x28, 0x1d, 0x55, + 0xad, 0xa4, 0x30, 0x5a, 0xb7, 0x37, 0xf2, 0xec, 0xa7, 0x90, 0x85, 0xe0, 0x0f, 0xa0, 0xa2, 0x0a, + 0x1c, 0x8e, 0xc9, 0x53, 0xf0, 0xc3, 0x4f, 0xe9, 0xd1, 0x96, 0x83, 0x45, 0xd7, 0x34, 0x6a, 0x3d, + 0x06, 0x5d, 0xad, 0xe6, 0x0f, 0xd9, 0xf2, 0x90, 0x1b, 0x9e, 0x9a, 0x84, 0x96, 0x01, 0x86, 0x6f, + 0xba, 0xcc, 0xe9, 0xf7, 0xce, 0x7a, 0xe7, 0x66, 0xae, 0xc5, 0xd7, 0x1e, 0xdf, 0x08, 0xf9, 0xcc, + 0x9f, 0x20, 0xfd, 0x00, 0x95, 0xd1, 0x65, 0x14, 0x07, 0xde, 0xea, 0xda, 0xee, 0x6e, 0xfe, 0x60, + 0x75, 0x6e, 0xf5, 0x7b, 0xdb, 0x6c, 0xc5, 0xda, 0x79, 0x51, 0x7e, 0x5f, 0xcc, 0x0a, 0xbe, 0x12, + 0x72, 0x91, 0x57, 0xff, 0x86, 0xa7, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x0e, 0xa0, 0x99, + 0x3e, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -494,45 +543,3 @@ var _RateLimitService_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "proto/ratelimit/ratelimit.proto", } - -func init() { - proto.RegisterFile("proto/ratelimit/ratelimit.proto", fileDescriptor_ratelimit_8ec600a45de499be) -} - -var fileDescriptor_ratelimit_8ec600a45de499be = []byte{ - // 532 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xdd, 0x8e, 0xd2, 0x40, - 0x14, 0xde, 0xa1, 0x5d, 0x58, 0x4e, 0x17, 0x28, 0xa3, 0x31, 0x84, 0x98, 0x2c, 0x56, 0xa3, 0xf8, - 0x93, 0x6e, 0x82, 0xd9, 0x4b, 0x4d, 0x70, 0xc1, 0x2c, 0x59, 0x16, 0x74, 0x58, 0x34, 0x7a, 0x61, - 0xd3, 0xa5, 0x47, 0xb7, 0xb1, 0xdb, 0xe2, 0xcc, 0x94, 0x84, 0x3b, 0x9f, 0xc0, 0x3b, 0x1f, 0xc0, - 0x17, 0xf0, 0x0d, 0x7c, 0x37, 0xd3, 0xa1, 0x14, 0xfc, 0x09, 0x21, 0x7b, 0x77, 0xfe, 0xbe, 0xef, - 0x9c, 0x9e, 0xef, 0x4c, 0xe1, 0x60, 0xca, 0x23, 0x19, 0x1d, 0x72, 0x57, 0x62, 0xe0, 0x5f, 0xf9, - 0x72, 0x65, 0xd9, 0x2a, 0x43, 0xab, 0xd3, 0x0b, 0x3b, 0x98, 0x7f, 0x94, 0x76, 0x96, 0xb0, 0xbe, - 0x13, 0x30, 0x99, 0x2b, 0xb1, 0x9f, 0x78, 0x0c, 0xbf, 0xc4, 0x28, 0x24, 0xbd, 0x05, 0x79, 0x2f, - 0xba, 0x72, 0xfd, 0xb0, 0x46, 0x1a, 0xa4, 0x59, 0x64, 0xa9, 0x47, 0x4f, 0xc0, 0xf0, 0x50, 0x4c, - 0xb8, 0x3f, 0x95, 0x11, 0x17, 0xb5, 0x5c, 0x43, 0x6b, 0x1a, 0xad, 0xfb, 0xf6, 0x3f, 0xac, 0x76, - 0xc6, 0xd8, 0xc9, 0xca, 0xd9, 0x3a, 0x94, 0x1e, 0x80, 0x71, 0xe9, 0x4b, 0xe1, 0xb8, 0x9e, 0x87, - 0xa1, 0x57, 0xd3, 0x1a, 0xa4, 0x59, 0x62, 0x90, 0x84, 0xda, 0x2a, 0x62, 0x7d, 0x23, 0x70, 0xe3, - 0x3f, 0x2c, 0xf4, 0x25, 0x14, 0x30, 0x94, 0xdc, 0x47, 0x51, 0x23, 0xaa, 0xfd, 0x93, 0xed, 0xda, - 0xdb, 0xdd, 0x50, 0xf2, 0x39, 0x5b, 0x82, 0xeb, 0x87, 0xb0, 0xab, 0x22, 0xd4, 0x04, 0xed, 0x33, - 0xce, 0xd3, 0x0f, 0x4d, 0x4c, 0x7a, 0x13, 0x76, 0x67, 0x6e, 0x10, 0x63, 0x2d, 0xa7, 0x62, 0x0b, - 0xc7, 0xfa, 0x49, 0xa0, 0x98, 0xf1, 0xd2, 0x47, 0x50, 0xe5, 0x8b, 0x65, 0x09, 0x67, 0x8a, 0xdc, - 0x89, 0x43, 0x5f, 0x2a, 0x8e, 0x12, 0xab, 0x2c, 0x13, 0xaf, 0x90, 0x8f, 0x43, 0x5f, 0xd2, 0x23, - 0xd0, 0x55, 0x3a, 0xa1, 0x2b, 0xb7, 0xee, 0x6c, 0x9a, 0xd7, 0x4e, 0x00, 0x4c, 0x95, 0x5b, 0xcf, - 0x41, 0x57, 0x70, 0x03, 0x0a, 0xe3, 0xc1, 0xe9, 0x60, 0xf8, 0x76, 0x60, 0xee, 0x50, 0x80, 0xfc, - 0xa8, 0x7b, 0x3c, 0x1c, 0x74, 0x4c, 0x92, 0xd8, 0x67, 0xbd, 0xc1, 0xf8, 0xbc, 0x6b, 0xe6, 0xe8, - 0x1e, 0xe8, 0x27, 0xc3, 0x31, 0x33, 0x35, 0x5a, 0x00, 0xad, 0xd3, 0x7e, 0x67, 0xea, 0xd6, 0x0f, - 0x0d, 0xaa, 0x6b, 0xca, 0x8a, 0x69, 0x14, 0x0a, 0xa4, 0x7d, 0xd8, 0x8f, 0x66, 0xc8, 0xdd, 0x20, - 0x70, 0x26, 0x91, 0x87, 0x6a, 0xe6, 0x72, 0xeb, 0xe1, 0xa6, 0xa1, 0x96, 0x58, 0xfb, 0x38, 0xf2, - 0x90, 0x19, 0x29, 0x3c, 0x71, 0xe8, 0x6b, 0xd8, 0x13, 0xd2, 0x95, 0xb1, 0xc0, 0xe5, 0x35, 0x1c, - 0x6d, 0xc5, 0xb4, 0xd2, 0x65, 0xa4, 0xe0, 0x2c, 0xa3, 0xa9, 0xff, 0x22, 0x60, 0xfe, 0x9d, 0xa6, - 0xcf, 0x40, 0xbf, 0xde, 0xb4, 0x0a, 0x46, 0xdb, 0x50, 0x9a, 0xc4, 0x9c, 0x63, 0x28, 0x1d, 0x55, - 0xad, 0xa4, 0x30, 0x5a, 0xb7, 0x37, 0xf2, 0xec, 0xa7, 0x90, 0x85, 0xe0, 0x0f, 0xa0, 0xa2, 0x0a, - 0x1c, 0x8e, 0xc9, 0x53, 0xf0, 0xc3, 0x4f, 0xe9, 0xd1, 0x96, 0x83, 0x45, 0xd7, 0x34, 0x6a, 0x3d, - 0x06, 0x5d, 0xad, 0xe6, 0x0f, 0xd9, 0xf2, 0x90, 0x1b, 0x9e, 0x9a, 0x84, 0x96, 0x01, 0x86, 0x6f, - 0xba, 0xcc, 0xe9, 0xf7, 0xce, 0x7a, 0xe7, 0x66, 0xae, 0xc5, 0xd7, 0x1e, 0xdf, 0x08, 0xf9, 0xcc, - 0x9f, 0x20, 0xfd, 0x00, 0x95, 0xd1, 0x65, 0x14, 0x07, 0xde, 0xea, 0xda, 0xee, 0x6e, 0xfe, 0x60, - 0x75, 0x6e, 0xf5, 0x7b, 0xdb, 0x6c, 0xc5, 0xda, 0x79, 0x51, 0x7e, 0x5f, 0xcc, 0x0a, 0xbe, 0x12, - 0x72, 0x91, 0x57, 0xff, 0x86, 0xa7, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x0e, 0xa0, 0x99, - 0x3e, 0x04, 0x00, 0x00, -} diff --git a/script/generate_proto b/script/generate_proto index e0162ca16..2cdc0fb16 100755 --- a/script/generate_proto +++ b/script/generate_proto @@ -7,6 +7,6 @@ $protoc --version protoc --go_out=plugins=grpc:. proto/ratelimit/*.proto # Data-plane-api proto -data_plane_cmd="protoc --go_out=plugins=grpc,Menvoy/api/v2/ratelimit/ratelimit.proto=github.com/lyft/ratelimit/proto/envoy/api/v2/ratelimit:proto/. --proto_path=vendor/github.com/envoyproxy/data-plane-api --proto_path=vendor/github.com/lyft/protoc-gen-validate --proto_path=vendor/github.com/google/protobuf/src " +data_plane_cmd="protoc --go_out=plugins=grpc,Menvoy/api/v2/ratelimit/ratelimit.proto=github.com/lyft/ratelimit/proto/envoy/api/v2/ratelimit,Menvoy/api/v2/core/base.proto=github.com/envoyproxy/go-control-plane/envoy/api/v2/core:proto/. --proto_path=vendor/github.com/envoyproxy/data-plane-api --proto_path=vendor/github.com/lyft/protoc-gen-validate --proto_path=vendor/github.com/google/protobuf/src " ${data_plane_cmd} vendor/github.com/envoyproxy/data-plane-api/envoy/service/ratelimit/v2/rls.proto ${data_plane_cmd} vendor/github.com/envoyproxy/data-plane-api/envoy/api/v2/ratelimit/ratelimit.proto diff --git a/src/service/ratelimit.go b/src/service/ratelimit.go index 447167976..50505b961 100644 --- a/src/service/ratelimit.go +++ b/src/service/ratelimit.go @@ -12,6 +12,8 @@ import ( "github.com/lyft/ratelimit/src/redis" logger "github.com/sirupsen/logrus" "golang.org/x/net/context" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" + "strconv" ) type shouldRateLimitStats struct { @@ -19,6 +21,10 @@ type shouldRateLimitStats struct { serviceError stats.Counter } +type Clock struct { + UnixSeconds func() int64 +} + func newShouldRateLimitStats(scope stats.Scope) shouldRateLimitStats { ret := shouldRateLimitStats{} ret.redisError = scope.NewCounter("redis_error") @@ -47,15 +53,17 @@ type RateLimitServiceServer interface { } type service struct { - runtime loader.IFace - configLock sync.RWMutex - configLoader config.RateLimitConfigLoader - config config.RateLimitConfig - runtimeUpdateEvent chan int - cache redis.RateLimitCache - stats serviceStats - rlStatsScope stats.Scope - legacy *legacyService + runtime loader.IFace + configLock sync.RWMutex + configLoader config.RateLimitConfigLoader + config config.RateLimitConfig + runtimeUpdateEvent chan int + cache redis.RateLimitCache + stats serviceStats + rlStatsScope stats.Scope + legacy *legacyService + responseHeadersEnabled bool + clock Clock } func (this *service) reloadConfig() { @@ -126,11 +134,85 @@ func (this *service) shouldRateLimitWorker( finalCode = descriptorStatus.Code } } + if this.responseHeadersEnabled { + now := this.clock.UnixSeconds() + var limitingDescriptor *pb.RateLimitResponse_DescriptorStatus + limitCount := 0 + for _, descriptor := range responseDescriptorStatuses { + if descriptor.CurrentLimit == nil { + continue + } + limitCount++ + if limitingDescriptor == nil || + descriptor.LimitRemaining < limitingDescriptor.LimitRemaining || + descriptor.LimitRemaining == limitingDescriptor.LimitRemaining && + calculateReset(descriptor, now) > calculateReset(limitingDescriptor, now) { + limitingDescriptor = descriptor + } + } + if limitCount == 1 { + response.Headers = []*core.HeaderValue{ + rateLimitLimitHeader(limitingDescriptor), + rateLimitRemainingHeader(limitingDescriptor), + rateLimitResetHeader(limitingDescriptor, now), + } + } else if limitCount > 1 { + // If there is more than one limit, then picking one of them for the "X-RateLimit-Limit" + // header value would be arbitrary, so we omit it completely. + response.Headers = []*core.HeaderValue{ + rateLimitRemainingHeader(limitingDescriptor), + rateLimitResetHeader(limitingDescriptor, now), + } + } + } response.OverallCode = finalCode return response } +func rateLimitLimitHeader(descriptor *pb.RateLimitResponse_DescriptorStatus) *core.HeaderValue { + return &core.HeaderValue{ + Key: "X-RateLimit-Limit", + Value: strconv.FormatUint(uint64(descriptor.CurrentLimit.RequestsPerUnit), 10), + } +} + +func rateLimitRemainingHeader(descriptor *pb.RateLimitResponse_DescriptorStatus) *core.HeaderValue { + return &core.HeaderValue{ + Key: "X-RateLimit-Remaining", + Value: strconv.FormatUint(uint64(descriptor.LimitRemaining), 10), + } +} + +func rateLimitResetHeader( + descriptor *pb.RateLimitResponse_DescriptorStatus, now int64) *core.HeaderValue { + + return &core.HeaderValue{ + Key: "X-RateLimit-Reset", + Value: strconv.FormatInt(calculateReset(descriptor, now), 10), + } +} + +func calculateReset(descriptor *pb.RateLimitResponse_DescriptorStatus, now int64) int64 { + sec := unitInSeconds(descriptor.CurrentLimit.Unit) + return sec - now%sec +} + +func unitInSeconds(unit pb.RateLimitResponse_RateLimit_Unit) int64 { + switch unit { + case pb.RateLimitResponse_RateLimit_SECOND: + return 1 + case pb.RateLimitResponse_RateLimit_MINUTE: + return 60 + case pb.RateLimitResponse_RateLimit_HOUR: + return 60 * 60 + case pb.RateLimitResponse_RateLimit_DAY: + return 60 * 60 * 24 + default: + panic("unknown rate limit unit") + } +} + func (this *service) ShouldRateLimit( ctx context.Context, request *pb.RateLimitRequest) (finalResponse *pb.RateLimitResponse, finalError error) { @@ -175,17 +257,20 @@ func (this *service) GetCurrentConfig() config.RateLimitConfig { } func NewService(runtime loader.IFace, cache redis.RateLimitCache, - configLoader config.RateLimitConfigLoader, stats stats.Scope) RateLimitServiceServer { + configLoader config.RateLimitConfigLoader, stats stats.Scope, + responseHeadersEnabled bool, clock Clock) RateLimitServiceServer { newService := &service{ - runtime: runtime, - configLock: sync.RWMutex{}, - configLoader: configLoader, - config: nil, - runtimeUpdateEvent: make(chan int), - cache: cache, - stats: newServiceStats(stats), - rlStatsScope: stats.Scope("rate_limit"), + runtime: runtime, + configLock: sync.RWMutex{}, + configLoader: configLoader, + config: nil, + runtimeUpdateEvent: make(chan int), + cache: cache, + stats: newServiceStats(stats), + rlStatsScope: stats.Scope("rate_limit"), + responseHeadersEnabled: responseHeadersEnabled, + clock: clock, } newService.legacy = &legacyService{ s: newService, diff --git a/src/service/ratelimit_legacy.go b/src/service/ratelimit_legacy.go index f9f43b82a..77220076f 100644 --- a/src/service/ratelimit_legacy.go +++ b/src/service/ratelimit_legacy.go @@ -6,6 +6,7 @@ import ( pb "github.com/lyft/ratelimit/proto/envoy/service/ratelimit/v2" pb_legacy "github.com/lyft/ratelimit/proto/ratelimit" "golang.org/x/net/context" + "strings" ) type RateLimitLegacyServiceServer interface { @@ -90,7 +91,8 @@ func ConvertResponse(response *pb.RateLimitResponse) (*pb_legacy.RateLimitRespon } resp := &pb_legacy.RateLimitResponse{} - err = jsonpb.UnmarshalString(s, resp) + u := jsonpb.Unmarshaler{AllowUnknownFields: true} + err = u.Unmarshal(strings.NewReader(s), resp) if err != nil { return nil, err } diff --git a/src/service_cmd/runner/runner.go b/src/service_cmd/runner/runner.go index 3a8c322a3..207a86ceb 100644 --- a/src/service_cmd/runner/runner.go +++ b/src/service_cmd/runner/runner.go @@ -44,8 +44,10 @@ func Run() { rand.New(redis.NewLockedSource(time.Now().Unix())), s.ExpirationJitterMaxSeconds), config.NewRateLimitConfigLoaderImpl(), - srv.Scope().Scope("service")) - + srv.Scope().Scope("service"), + s.ResponseHeadersEnabled, + systemClock(), + ) srv.AddDebugHttpEndpoint( "/rlconfig", "print out the currently loaded configuration for debugging", @@ -62,3 +64,7 @@ func Run() { srv.Start() } + +func systemClock() ratelimit.Clock { + return ratelimit.Clock{UnixSeconds: func() int64 { return time.Now().Unix() }} +} diff --git a/src/settings/settings.go b/src/settings/settings.go index 2d73447ee..a6b281a04 100644 --- a/src/settings/settings.go +++ b/src/settings/settings.go @@ -26,6 +26,7 @@ type Settings struct { RedisPerSecondUrl string `envconfig:"REDIS_PERSECOND_URL" default:"/var/run/nutcracker/ratelimitpersecond.sock"` RedisPerSecondPoolSize int `envconfig:"REDIS_PERSECOND_POOL_SIZE" default:"10"` ExpirationJitterMaxSeconds int64 `envconfig:"EXPIRATION_JITTER_MAX_SECONDS" default:"300"` + ResponseHeadersEnabled bool `envconfig:"RESPONSE_HEADERS_ENABLED" default:"false"` } type Option func(*Settings) diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go index 260da7386..ba14fbfd7 100644 --- a/test/integration/integration_test.go +++ b/test/integration/integration_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" "google.golang.org/grpc" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) func newDescriptorStatus( @@ -244,3 +245,45 @@ func TestBasicConfigLegacy(t *testing.T) { assert.NoError(err) } } + +func TestBasicConfigWithHeaders(t *testing.T) { + os.Setenv("RESPONSE_HEADERS_ENABLED", "true") + os.Setenv("REDIS_PERSECOND", "false") + os.Setenv("PORT", "8082") + os.Setenv("GRPC_PORT", "8086") + os.Setenv("DEBUG_PORT", "8084") + os.Setenv("RUNTIME_ROOT", "runtime/current") + os.Setenv("RUNTIME_SUBDIRECTORY", "ratelimit") + os.Setenv("REDIS_PERSECOND_SOCKET_TYPE", "tcp") + os.Setenv("REDIS_SOCKET_TYPE", "tcp") + os.Setenv("REDIS_URL", "localhost:6379") + + go func() { + runner.Run() + }() + + // HACK: Wait for the server to come up. Make a hook that we can wait on. + time.Sleep(100 * time.Millisecond) + + assert := assert.New(t) + conn, err := grpc.Dial("localhost:8086", grpc.WithInsecure()) + assert.NoError(err) + defer conn.Close() + c := pb.NewRateLimitServiceClient(conn) + + response, err := c.ShouldRateLimit( + context.Background(), + common.NewRateLimitRequest("basic_headers", [][][2]string{{{"key1", "foo"}}}, 1)) + assert.Equal( + &pb.RateLimitResponse{ + OverallCode: pb.RateLimitResponse_OK, + Statuses: []*pb.RateLimitResponse_DescriptorStatus{newDescriptorStatus( + pb.RateLimitResponse_OK, 50, pb.RateLimitResponse_RateLimit_SECOND, 49)}, + Headers: []*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "50"}, + {Key: "X-RateLimit-Remaining", Value: "49"}, + {Key: "X-RateLimit-Reset", Value: "1"}, + }}, + response) + assert.NoError(err) +} diff --git a/test/integration/runtime/current/ratelimit/config/basic_headers.yaml b/test/integration/runtime/current/ratelimit/config/basic_headers.yaml new file mode 100644 index 000000000..7f1b5f10a --- /dev/null +++ b/test/integration/runtime/current/ratelimit/config/basic_headers.yaml @@ -0,0 +1,6 @@ +domain: basic_headers +descriptors: + - key: key1 + rate_limit: + unit: second + requests_per_unit: 50 diff --git a/test/service/ratelimit_legacy_test.go b/test/service/ratelimit_legacy_test.go index 29876a03d..c9b2fed4a 100644 --- a/test/service/ratelimit_legacy_test.go +++ b/test/service/ratelimit_legacy_test.go @@ -15,6 +15,7 @@ import ( "github.com/lyft/ratelimit/test/common" "github.com/stretchr/testify/assert" "golang.org/x/net/context" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) func convertRatelimit(ratelimit *pb.RateLimitResponse_RateLimit) (*pb_legacy.RateLimit, error) { @@ -224,7 +225,8 @@ func TestInitialLoadErrorLegacy(test *testing.T) { func([]config.RateLimitConfigToLoad, stats.Scope) { panic(config.RateLimitConfigError("load error")) }) - service := ratelimit.NewService(t.runtime, t.cache, t.configLoader, t.statStore) + service := ratelimit.NewService(t.runtime, t.cache, t.configLoader, t.statStore, false, + ratelimit.Clock{UnixSeconds: func() int64 { return 0 }}) request := common.NewRateLimitRequestLegacy("test-domain", [][][2]string{{{"hello", "world"}}}, 1) response, err := service.GetLegacyService().ShouldRateLimit(nil, request) @@ -406,3 +408,31 @@ func TestConvertResponse(test *testing.T) { assert.Equal(test, expectedResponse, resp) } + +func TestConvertResponseWithHeaders(t *testing.T) { + response := &pb.RateLimitResponse{ + OverallCode: pb.RateLimitResponse_OVER_LIMIT, + Statuses: []*pb.RateLimitResponse_DescriptorStatus{{ + Code: pb.RateLimitResponse_OK, + CurrentLimit: nil, + LimitRemaining: 9, + }}, + Headers: []*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "5"}, + {Key: "X-RateLimit-Remaining", Value: "4"}, + {Key: "X-RateLimit-Reset", Value: "38"}, + }, + } + legacyResponse, err := ratelimit.ConvertResponse(response) + if err != nil { + assert.FailNow(t, err.Error()) + } + assert.Equal(t, &pb_legacy.RateLimitResponse{ + OverallCode: pb_legacy.RateLimitResponse_OVER_LIMIT, + Statuses: []*pb_legacy.RateLimitResponse_DescriptorStatus{{ + Code: pb_legacy.RateLimitResponse_OK, + CurrentLimit: nil, + LimitRemaining: 9, + }}, + }, legacyResponse) +} diff --git a/test/service/ratelimit_test.go b/test/service/ratelimit_test.go index e3829c5e9..11a8dcb0c 100644 --- a/test/service/ratelimit_test.go +++ b/test/service/ratelimit_test.go @@ -17,6 +17,7 @@ import ( "github.com/lyft/ratelimit/test/mocks/runtime/snapshot" "github.com/stretchr/testify/assert" "golang.org/x/net/context" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) type barrier struct { @@ -47,15 +48,17 @@ func newBarrier() barrier { } type rateLimitServiceTestSuite struct { - assert *assert.Assertions - controller *gomock.Controller - runtime *mock_loader.MockIFace - snapshot *mock_snapshot.MockIFace - cache *mock_redis.MockRateLimitCache - configLoader *mock_config.MockRateLimitConfigLoader - config *mock_config.MockRateLimitConfig - runtimeUpdateCallback chan<- int - statStore stats.Store + assert *assert.Assertions + controller *gomock.Controller + runtime *mock_loader.MockIFace + snapshot *mock_snapshot.MockIFace + cache *mock_redis.MockRateLimitCache + configLoader *mock_config.MockRateLimitConfigLoader + config *mock_config.MockRateLimitConfig + runtimeUpdateCallback chan<- int + statStore stats.Store + responseHeadersEnabled bool + clock ratelimit.Clock } func commonSetup(t *testing.T) rateLimitServiceTestSuite { @@ -82,7 +85,7 @@ func (this *rateLimitServiceTestSuite) setupBasicService() ratelimit.RateLimitSe this.configLoader.EXPECT().Load( []config.RateLimitConfigToLoad{{"config.basic_config", "fake_yaml"}}, gomock.Any()).Return(this.config) - return ratelimit.NewService(this.runtime, this.cache, this.configLoader, this.statStore) + return ratelimit.NewService(this.runtime, this.cache, this.configLoader, this.statStore, this.responseHeadersEnabled, this.clock) } func TestService(test *testing.T) { @@ -225,7 +228,8 @@ func TestInitialLoadError(test *testing.T) { func([]config.RateLimitConfigToLoad, stats.Scope) { panic(config.RateLimitConfigError("load error")) }) - service := ratelimit.NewService(t.runtime, t.cache, t.configLoader, t.statStore) + service := ratelimit.NewService(t.runtime, t.cache, t.configLoader, t.statStore, + t.responseHeadersEnabled, t.clock) request := common.NewRateLimitRequest("test-domain", [][][2]string{{{"hello", "world"}}}, 1) response, err := service.ShouldRateLimit(nil, request) @@ -233,3 +237,231 @@ func TestInitialLoadError(test *testing.T) { t.assert.Equal("no rate limit configuration loaded", err.Error()) t.assert.EqualValues(1, t.statStore.NewCounter("call.should_rate_limit.service_error").Value()) } + +func TestHeaders(test *testing.T) { + t := commonSetup(test) + currentTime := 123 + t.responseHeadersEnabled = true + t.clock = ratelimit.Clock{UnixSeconds: func() int64 { return int64(currentTime) }} + defer t.controller.Finish() + + service := t.setupBasicService() + + request := common.NewRateLimitRequest("test-domain", [][][2]string{{{"hello", "world"}}}, 1) + limits := []*config.RateLimit{ + config.NewRateLimit(10, pb.RateLimitResponse_RateLimit_MINUTE, "key", t.statStore), + } + + // Under limit + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OK, + CurrentLimit: limits[0].Limit, LimitRemaining: 6}, + }) + response, err := service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "6"}, + {Key: "X-RateLimit-Reset", Value: "57"}, + }, + response.Headers) + t.assert.Nil(err) + + // Last request under limit + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OK, + CurrentLimit: limits[0].Limit, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "0"}, + {Key: "X-RateLimit-Reset", Value: "57"}, + }, + response.Headers) + t.assert.Nil(err) + + // Over limit + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OVER_LIMIT, + CurrentLimit: limits[0].Limit, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "0"}, + {Key: "X-RateLimit-Reset", Value: "57"}, + }, + response.Headers) + t.assert.Nil(err) + + // After time passes, the reset header should decrement + currentTime = 124 + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OVER_LIMIT, + CurrentLimit: limits[0].Limit, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "0"}, + {Key: "X-RateLimit-Reset", Value: "56"}, + }, response.Headers) + t.assert.Nil(err) + + // Last second before reset + currentTime = 179 + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OVER_LIMIT, + CurrentLimit: limits[0].Limit, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "0"}, + {Key: "X-RateLimit-Reset", Value: "1"}, + }, response.Headers) + t.assert.Nil(err) + + // Exact second when reset occurs + currentTime = 180 + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{{Code: pb.RateLimitResponse_OK, + CurrentLimit: limits[0].Limit, LimitRemaining: 9}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Limit", Value: "10"}, + {Key: "X-RateLimit-Remaining", Value: "9"}, + {Key: "X-RateLimit-Reset", Value: "60"}, + }, response.Headers) + t.assert.Nil(err) + + // Multiple descriptors + // (X-RateLimit-Limit omitted because choosing the limit of one descriptor would be arbitrary) + + currentTime = 200 + request = common.NewRateLimitRequest("test-domain", [][][2]string{ + {{"a", "b"}}, {{"c", "d"}}, {{"e", "f"}}, + }, 1) + limits = []*config.RateLimit{ + config.NewRateLimit(1000, pb.RateLimitResponse_RateLimit_HOUR, "key", t.statStore), + config.NewRateLimit(75, pb.RateLimitResponse_RateLimit_MINUTE, "key", t.statStore), + config.NewRateLimit(50, pb.RateLimitResponse_RateLimit_MINUTE, "key", t.statStore), + } + + // First descriptor is limiting factor + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(limits[1]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[2]).Return(limits[2]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[0].Limit, LimitRemaining: 3}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[1].Limit, LimitRemaining: 4}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[2].Limit, LimitRemaining: 5}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Remaining", Value: "3"}, + {Key: "X-RateLimit-Reset", Value: "3400"}, + }, response.Headers) + t.assert.Nil(err) + + // Second descriptor is limiting factor + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(limits[1]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[2]).Return(limits[2]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[0].Limit, LimitRemaining: 6}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[1].Limit, LimitRemaining: 4}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[2].Limit, LimitRemaining: 5}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Remaining", Value: "4"}, + {Key: "X-RateLimit-Reset", Value: "40"}, + }, response.Headers) + t.assert.Nil(err) + + // Third descriptor is limiting factor + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(limits[1]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[2]).Return(limits[2]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[0].Limit, LimitRemaining: 6}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[1].Limit, LimitRemaining: 7}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[2].Limit, LimitRemaining: 5}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Remaining", Value: "5"}, + {Key: "X-RateLimit-Reset", Value: "40"}, + }, response.Headers) + t.assert.Nil(err) + + // If there's a LimitRemaining tie, the highest Reset is returned + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(limits[1]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[2]).Return(limits[2]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[0].Limit, LimitRemaining: 6}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[1].Limit, LimitRemaining: 6}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[2].Limit, LimitRemaining: 7}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Remaining", Value: "6"}, + {Key: "X-RateLimit-Reset", Value: "3400"}, + }, response.Headers) + t.assert.Nil(err) + + // Same test with same expected result, but inverse descriptor order from cache + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(limits[0]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(limits[1]) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[2]).Return(limits[2]) + t.cache.EXPECT().DoLimit(nil, request, limits).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[2].Limit, LimitRemaining: 7}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[1].Limit, LimitRemaining: 6}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: limits[0].Limit, LimitRemaining: 6}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Equal([]*core.HeaderValue{ + {Key: "X-RateLimit-Remaining", Value: "6"}, + {Key: "X-RateLimit-Reset", Value: "3400"}, + }, response.Headers) + t.assert.Nil(err) + + // No headers if no limit, one descriptor + request = common.NewRateLimitRequest("test-domain", [][][2]string{{{"hello", "world"}}}, 1) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(nil) + t.cache.EXPECT().DoLimit(nil, request, []*config.RateLimit{nil}).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: nil, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Nil(response.Headers) + t.assert.Nil(err) + + // No headers if no limit, two descriptors + request = common.NewRateLimitRequest("test-domain", [][][2]string{ + {{"foo", "bar"}}, {{"hello", "world"}}}, 1) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[0]).Return(nil) + t.config.EXPECT().GetLimit(nil, "test-domain", request.Descriptors[1]).Return(nil) + t.cache.EXPECT().DoLimit(nil, request, []*config.RateLimit{nil, nil}).Return( + []*pb.RateLimitResponse_DescriptorStatus{ + {Code: pb.RateLimitResponse_OK, CurrentLimit: nil, LimitRemaining: 0}, + {Code: pb.RateLimitResponse_OK, CurrentLimit: nil, LimitRemaining: 0}, + }) + response, err = service.ShouldRateLimit(nil, request) + t.assert.Nil(response.Headers) + t.assert.Nil(err) +} From 08ae3b213f3c1a3e9fbf9a0e7c1a1b2d6667a688 Mon Sep 17 00:00:00 2001 From: Adam Snyder Date: Thu, 15 Nov 2018 13:48:08 -0800 Subject: [PATCH 2/3] Fix imports formatting --- src/service/ratelimit.go | 2 +- test/integration/integration_test.go | 2 +- test/service/ratelimit_legacy_test.go | 2 +- test/service/ratelimit_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/service/ratelimit.go b/src/service/ratelimit.go index 50505b961..65e3123bc 100644 --- a/src/service/ratelimit.go +++ b/src/service/ratelimit.go @@ -4,6 +4,7 @@ import ( "strings" "sync" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" "github.com/lyft/goruntime/loader" "github.com/lyft/gostats" pb "github.com/lyft/ratelimit/proto/envoy/service/ratelimit/v2" @@ -12,7 +13,6 @@ import ( "github.com/lyft/ratelimit/src/redis" logger "github.com/sirupsen/logrus" "golang.org/x/net/context" - "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" "strconv" ) diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go index ba14fbfd7..f27920b57 100644 --- a/test/integration/integration_test.go +++ b/test/integration/integration_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" pb "github.com/lyft/ratelimit/proto/envoy/service/ratelimit/v2" pb_legacy "github.com/lyft/ratelimit/proto/ratelimit" "github.com/lyft/ratelimit/src/service_cmd/runner" @@ -17,7 +18,6 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" "google.golang.org/grpc" - "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) func newDescriptorStatus( diff --git a/test/service/ratelimit_legacy_test.go b/test/service/ratelimit_legacy_test.go index c9b2fed4a..1a6aeb15c 100644 --- a/test/service/ratelimit_legacy_test.go +++ b/test/service/ratelimit_legacy_test.go @@ -3,6 +3,7 @@ package ratelimit_test import ( "testing" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" "github.com/golang/mock/gomock" "github.com/golang/protobuf/jsonpb" "github.com/lyft/gostats" @@ -15,7 +16,6 @@ import ( "github.com/lyft/ratelimit/test/common" "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) func convertRatelimit(ratelimit *pb.RateLimitResponse_RateLimit) (*pb_legacy.RateLimit, error) { diff --git a/test/service/ratelimit_test.go b/test/service/ratelimit_test.go index 11a8dcb0c..2690c947a 100644 --- a/test/service/ratelimit_test.go +++ b/test/service/ratelimit_test.go @@ -4,6 +4,7 @@ import ( "sync" "testing" + "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" "github.com/golang/mock/gomock" "github.com/lyft/gostats" pb "github.com/lyft/ratelimit/proto/envoy/service/ratelimit/v2" @@ -17,7 +18,6 @@ import ( "github.com/lyft/ratelimit/test/mocks/runtime/snapshot" "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" ) type barrier struct { From 0645ba18c0a39bd75b905c56d4792ef5716430d1 Mon Sep 17 00:00:00 2001 From: Adam Snyder Date: Thu, 15 Nov 2018 14:07:24 -0800 Subject: [PATCH 3/3] Revert golang.org/x/* glide lock version changes --- glide.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glide.lock b/glide.lock index de80bc036..7d8b971b7 100644 --- a/glide.lock +++ b/glide.lock @@ -73,7 +73,7 @@ imports: subpackages: - ssh/terminal - name: golang.org/x/net - version: adae6a3d119ae4890b46832a2e88a95adc62b8e7 + version: 1e491301e022f8f977054da4c2d852decd59571f subpackages: - context - http/httpguts @@ -83,7 +83,7 @@ imports: - internal/timeseries - trace - name: golang.org/x/sys - version: acbc56fc7007d2a01796d5bde54f39e3b3e95945 + version: f6cff0780e542efa0c8e864dc8fa522808f6a598 subpackages: - unix - windows