@@ -2,6 +2,7 @@ package proxy
22
33import (
44 "regexp"
5+ "sync"
56 "time"
67
78 "golang.org/x/time/rate"
@@ -31,8 +32,23 @@ type RateLimit struct {
3132}
3233
3334type compiledRateLimit struct {
34- re * regexp.Regexp
35+ // protects the l402Limiters map.
36+ sync.Mutex
37+
38+ // re is the regular expression used to match the path of the URL.
39+ re * regexp.Regexp
40+
41+ // global limiter is used when no per-L402 key can be derived.
3542 limiter * rate.Limiter
43+
44+ // limiter per L402 key.
45+ limit rate.Limit
46+
47+ // burst is the burst size allowed in addition to steady rate.
48+ burst int
49+
50+ // l402Limiters is a map of per-L402 key limiters.
51+ l402Limiters map [string ]* rate.Limiter
3652}
3753
3854// compile prepares the regular expression and the limiter.
@@ -57,13 +73,62 @@ func (r *RateLimit) compile() error {
5773
5874 // rate.Every(per/requests) creates an average rate of requests
5975 // per 'per'.
60- lim := rate .NewLimiter (rate .Every (per / time .Duration (requests )), burst )
61- r .compiled = & compiledRateLimit {re : re , limiter : lim }
76+ limit := rate .Every (per / time .Duration (requests ))
77+ lim := rate .NewLimiter (limit , burst )
78+ r .compiled = & compiledRateLimit {
79+ re : re ,
80+ limiter : lim ,
81+ limit : limit ,
82+ burst : burst ,
83+ l402Limiters : make (map [string ]* rate.Limiter ),
84+ }
6285
6386 return nil
6487}
6588
66- // allow returns true if the rate limit permits an event now.
67- func (c * compiledRateLimit ) allow () bool {
68- return c .limiter .Allow ()
89+ // allowFor returns true if the rate limit permits an event now for the given
90+ // key. If the key is empty, the global limiter is used.
91+ func (c * compiledRateLimit ) allowFor (key string ) bool {
92+ if key == "" {
93+ return c .limiter .Allow ()
94+ }
95+ l := c .getOrCreate (key )
96+
97+ return l .Allow ()
98+ }
99+
100+ // reserveDelay reserves a token on the limiter for the given key and returns
101+ // the suggested delay. Callers can use the delay to set Retry-After without
102+ // consuming tokens.
103+ func (c * compiledRateLimit ) reserveDelay (key string ) (time.Duration , bool ) {
104+ var l * rate.Limiter
105+ if key == "" {
106+ l = c .limiter
107+ } else {
108+ l = c .getOrCreate (key )
109+ }
110+
111+ res := l .Reserve ()
112+ if ! res .OK () {
113+ return 0 , false
114+ }
115+
116+ delay := res .Delay ()
117+ res .CancelAt (time .Now ())
118+
119+ return delay , true
120+ }
121+
122+ func (c * compiledRateLimit ) getOrCreate (key string ) * rate.Limiter {
123+ c .Lock ()
124+ defer c .Unlock ()
125+
126+ if l , ok := c .l402Limiters [key ]; ok {
127+ return l
128+ }
129+
130+ l := rate .NewLimiter (c .limit , c .burst )
131+ c .l402Limiters [key ] = l
132+
133+ return l
69134}
0 commit comments