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  }`