github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_api_rate_limit_test.go (about) 1 package gateway 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "net/url" 7 "sync" 8 "testing" 9 "time" 10 11 "github.com/justinas/alice" 12 uuid "github.com/satori/go.uuid" 13 14 "github.com/TykTechnologies/tyk/config" 15 "github.com/TykTechnologies/tyk/test" 16 "github.com/TykTechnologies/tyk/user" 17 ) 18 19 func createRLSession() *user.SessionState { 20 session := new(user.SessionState) 21 // essentially non-throttled 22 session.Rate = 100.0 23 session.Allowance = session.Rate 24 session.LastCheck = time.Now().Unix() 25 session.Per = 1.0 26 session.QuotaRenewalRate = 300 // 5 minutes 27 session.QuotaRenews = time.Now().Unix() 28 session.QuotaRemaining = 10 29 session.QuotaMax = 10 30 session.AccessRights = map[string]user.AccessDefinition{"31445455": {APIName: "Tyk Auth Key Test", APIID: "31445455", Versions: []string{"default"}}} 31 session.Mutex = &sync.RWMutex{} 32 return session 33 } 34 35 func getRLOpenChain(spec *APISpec) http.Handler { 36 remote, _ := url.Parse(spec.Proxy.TargetURL) 37 proxy := TykNewSingleHostReverseProxy(remote, spec, nil) 38 proxyHandler := ProxyHandler(proxy, spec) 39 baseMid := BaseMiddleware{Spec: spec, Proxy: proxy} 40 chain := alice.New(mwList( 41 &IPWhiteListMiddleware{baseMid}, 42 &IPBlackListMiddleware{BaseMiddleware: baseMid}, 43 &VersionCheck{BaseMiddleware: baseMid}, 44 &RateLimitForAPI{BaseMiddleware: baseMid}, 45 )...).Then(proxyHandler) 46 return chain 47 } 48 49 func getGlobalRLAuthKeyChain(spec *APISpec) http.Handler { 50 remote, _ := url.Parse(spec.Proxy.TargetURL) 51 proxy := TykNewSingleHostReverseProxy(remote, spec, nil) 52 proxyHandler := ProxyHandler(proxy, spec) 53 baseMid := BaseMiddleware{Spec: spec, Proxy: proxy} 54 chain := alice.New(mwList( 55 &IPWhiteListMiddleware{baseMid}, 56 &IPBlackListMiddleware{BaseMiddleware: baseMid}, 57 &AuthKey{baseMid}, 58 &VersionCheck{BaseMiddleware: baseMid}, 59 &KeyExpired{baseMid}, 60 &AccessRightsCheck{baseMid}, 61 &RateLimitForAPI{BaseMiddleware: baseMid}, 62 &RateLimitAndQuotaCheck{baseMid}, 63 )...).Then(proxyHandler) 64 return chain 65 } 66 67 func TestRLOpen(t *testing.T) { 68 spec := LoadSampleAPI(openRLDefSmall) 69 70 req := TestReq(t, "GET", "/rl_test/", nil) 71 72 DRLManager.SetCurrentTokenValue(1) 73 DRLManager.RequestTokenValue = 1 74 75 chain := getRLOpenChain(spec) 76 for a := 0; a <= 10; a++ { 77 recorder := httptest.NewRecorder() 78 chain.ServeHTTP(recorder, req) 79 if a < 3 { 80 if recorder.Code != 200 { 81 t.Fatalf("Rate limit kicked in too early, after only %v requests", a) 82 } 83 } 84 85 if a > 7 { 86 if recorder.Code != 429 { 87 t.Fatalf("Rate limit did not activate, code was: %v", recorder.Code) 88 } 89 } 90 } 91 92 DRLManager.SetCurrentTokenValue(0) 93 DRLManager.RequestTokenValue = 0 94 } 95 96 func requestThrottlingTest(limiter string, testLevel string) func(t *testing.T) { 97 return func(t *testing.T) { 98 defer ResetTestConfig() 99 100 ts := StartTest() 101 defer ts.Close() 102 103 globalCfg := config.Global() 104 105 switch limiter { 106 case "InMemoryRateLimiter": 107 DRLManager.SetCurrentTokenValue(1) 108 DRLManager.RequestTokenValue = 1 109 case "SentinelRateLimiter": 110 globalCfg.EnableSentinelRateLimiter = true 111 case "RedisRollingRateLimiter": 112 globalCfg.EnableRedisRollingLimiter = true 113 default: 114 t.Fatal("There is no such a rate limiter:", limiter) 115 } 116 117 config.SetGlobal(globalCfg) 118 119 var per, rate float64 120 var throttleRetryLimit int 121 122 per = 2 123 rate = 1 124 throttleRetryLimit = 3 125 126 // Toggle request throttling on and off, with different throttle intervals. 127 iterations := map[bool][]float64{ 128 true: {-1, 0, 1}, 129 false: {-1, 0, 1}, 130 } 131 132 for requestThrottlingEnabled, throttleIntervals := range iterations { 133 for _, throttleInterval := range throttleIntervals { 134 spec := BuildAndLoadAPI(func(spec *APISpec) { 135 spec.Name = "test" 136 spec.APIID = "test" 137 spec.OrgID = "default" 138 spec.UseKeylessAccess = false 139 spec.Proxy.ListenPath = "/" 140 })[0] 141 142 policyID := CreatePolicy(func(p *user.Policy) { 143 p.OrgID = "default" 144 145 p.AccessRights = map[string]user.AccessDefinition{ 146 spec.APIID: { 147 APIName: spec.APIDefinition.Name, 148 APIID: spec.APIID, 149 }, 150 } 151 152 if testLevel == "PolicyLevel" { 153 p.Per = per 154 p.Rate = rate 155 156 if requestThrottlingEnabled { 157 p.ThrottleInterval = throttleInterval 158 p.ThrottleRetryLimit = throttleRetryLimit 159 } 160 } else if testLevel == "APILevel" { 161 a := p.AccessRights[spec.APIID] 162 a.Limit = &user.APILimit{ 163 Rate: rate, 164 Per: per, 165 } 166 167 if requestThrottlingEnabled { 168 a.Limit.ThrottleInterval = throttleInterval 169 a.Limit.ThrottleRetryLimit = throttleRetryLimit 170 } 171 172 p.Partitions.PerAPI = true 173 174 p.AccessRights[spec.APIID] = a 175 } else { 176 t.Fatal("There is no such a test level:", testLevel) 177 } 178 }) 179 180 key := CreateSession(func(s *user.SessionState) { 181 s.ApplyPolicies = []string{policyID} 182 s.Mutex = &sync.RWMutex{} 183 }) 184 185 authHeaders := map[string]string{ 186 "authorization": key, 187 } 188 189 if requestThrottlingEnabled && throttleInterval > 0 { 190 ts.Run(t, []test.TestCase{ 191 {Path: "/", Headers: authHeaders, Code: 200, Delay: 100 * time.Millisecond}, 192 {Path: "/", Headers: authHeaders, Code: 200}, 193 }...) 194 } else { 195 ts.Run(t, []test.TestCase{ 196 {Path: "/", Headers: authHeaders, Code: 200, Delay: 100 * time.Millisecond}, 197 {Path: "/", Headers: authHeaders, Code: 429}, 198 }...) 199 } 200 } 201 } 202 } 203 } 204 205 func TestRequestThrottling(t *testing.T) { 206 t.Run("PolicyLevel", func(t *testing.T) { 207 t.Run("InMemoryRateLimiter", requestThrottlingTest("InMemoryRateLimiter", "PolicyLevel")) 208 t.Run("SentinelRateLimiter", requestThrottlingTest("SentinelRateLimiter", "PolicyLevel")) 209 t.Run("RedisRollingRateLimiter", requestThrottlingTest("RedisRollingRateLimiter", "PolicyLevel")) 210 }) 211 212 t.Run("APILevel", func(t *testing.T) { 213 t.Run("InMemoryRateLimiter", requestThrottlingTest("InMemoryRateLimiter", "APILevel")) 214 t.Run("SentinelRateLimiter", requestThrottlingTest("SentinelRateLimiter", "APILevel")) 215 t.Run("RedisRollingRateLimiter", requestThrottlingTest("RedisRollingRateLimiter", "APILevel")) 216 }) 217 } 218 219 func TestRLClosed(t *testing.T) { 220 spec := LoadSampleAPI(closedRLDefSmall) 221 222 req := TestReq(t, "GET", "/rl_closed_test/", nil) 223 224 session := createRLSession() 225 customToken := uuid.NewV4().String() 226 // AuthKey sessions are stored by {token} 227 spec.SessionManager.UpdateSession(customToken, session, 60, false) 228 req.Header.Set("authorization", "Bearer "+customToken) 229 230 DRLManager.SetCurrentTokenValue(1) 231 DRLManager.RequestTokenValue = 1 232 233 chain := getGlobalRLAuthKeyChain(spec) 234 for a := 0; a <= 10; a++ { 235 recorder := httptest.NewRecorder() 236 chain.ServeHTTP(recorder, req) 237 if a < 3 { 238 if recorder.Code != 200 { 239 t.Fatalf("Rate limit kicked in too early, after only %v requests", a) 240 } 241 } 242 243 if a > 7 { 244 if recorder.Code != 429 { 245 t.Fatalf("Rate limit did not activate, code was: %v", recorder.Code) 246 } 247 } 248 } 249 250 DRLManager.SetCurrentTokenValue(0) 251 DRLManager.RequestTokenValue = 0 252 } 253 254 func TestRLOpenWithReload(t *testing.T) { 255 spec := LoadSampleAPI(openRLDefSmall) 256 257 req := TestReq(t, "GET", "/rl_test/", nil) 258 259 DRLManager.SetCurrentTokenValue(1) 260 DRLManager.RequestTokenValue = 1 261 262 chain := getRLOpenChain(spec) 263 for a := 0; a <= 10; a++ { 264 recorder := httptest.NewRecorder() 265 chain.ServeHTTP(recorder, req) 266 if a < 3 { 267 if recorder.Code != 200 { 268 t.Fatalf("Rate limit (pre change) kicked in too early, after only %v requests", a) 269 } 270 } 271 272 if a > 7 { 273 if recorder.Code != 429 { 274 t.Fatalf("Rate limit (pre change) did not activate, code was: %v", recorder.Code) 275 } 276 } 277 } 278 279 // Change rate and emulate a reload 280 spec.GlobalRateLimit.Rate = 20 281 chain = getRLOpenChain(spec) 282 for a := 0; a <= 30; a++ { 283 recorder := httptest.NewRecorder() 284 chain.ServeHTTP(recorder, req) 285 if a < 20 { 286 if recorder.Code != 200 { 287 t.Fatalf("Rate limit (post change) kicked in too early, after only %v requests", a) 288 } 289 } 290 291 if a > 23 { 292 if recorder.Code != 429 { 293 t.Fatalf("Rate limit (post change) did not activate, code was: %v", recorder.Code) 294 } 295 } 296 } 297 298 DRLManager.SetCurrentTokenValue(0) 299 DRLManager.RequestTokenValue = 0 300 } 301 302 const openRLDefSmall = `{ 303 "api_id": "313232", 304 "org_id": "default", 305 "auth": {"auth_header_name": "authorization"}, 306 "use_keyless": true, 307 "version_data": { 308 "not_versioned": true, 309 "versions": { 310 "v1": {"name": "v1"} 311 } 312 }, 313 "proxy": { 314 "listen_path": "/rl_test/", 315 "target_url": "` + TestHttpAny + `" 316 }, 317 "global_rate_limit": { 318 "rate": 3, 319 "per": 1 320 } 321 }` 322 323 const closedRLDefSmall = `{ 324 "api_id": "31445455", 325 "org_id": "default", 326 "auth": {"auth_header_name": "authorization"}, 327 "version_data": { 328 "not_versioned": true, 329 "versions": { 330 "v1": {"name": "v1"} 331 } 332 }, 333 "proxy": { 334 "listen_path": "/rl_closed_test/", 335 "target_url": "` + TestHttpAny + `" 336 }, 337 "global_rate_limit": { 338 "rate": 3, 339 "per": 1 340 } 341 }`