github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_api_rate_limit.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "net/http" 6 "sync" 7 8 "strconv" 9 "time" 10 11 "github.com/TykTechnologies/tyk/request" 12 "github.com/TykTechnologies/tyk/storage" 13 "github.com/TykTechnologies/tyk/user" 14 ) 15 16 // RateLimitAndQuotaCheck will check the incoming request and key whether it is within it's quota and 17 // within it's rate limit, it makes use of the SessionLimiter object to do this 18 type RateLimitForAPI struct { 19 BaseMiddleware 20 keyName string 21 apiSess *user.SessionState 22 } 23 24 func (k *RateLimitForAPI) Name() string { 25 return "RateLimitForAPI" 26 } 27 28 func (k *RateLimitForAPI) EnabledForSpec() bool { 29 if k.Spec.DisableRateLimit || k.Spec.GlobalRateLimit.Rate == 0 { 30 return false 31 } 32 33 // We'll init here 34 k.keyName = "apilimiter-" + k.Spec.OrgID + k.Spec.APIID 35 36 // Set last updated on each load to ensure we always use a new rate limit bucket 37 k.apiSess = &user.SessionState{ 38 Rate: k.Spec.GlobalRateLimit.Rate, 39 Per: k.Spec.GlobalRateLimit.Per, 40 LastUpdated: strconv.Itoa(int(time.Now().UnixNano())), 41 Mutex: &sync.RWMutex{}, 42 } 43 k.apiSess.SetKeyHash(storage.HashKey(k.keyName)) 44 45 return true 46 } 47 48 func (k *RateLimitForAPI) handleRateLimitFailure(r *http.Request, token string) (error, int) { 49 k.Logger().WithField("key", obfuscateKey(token)).Info("API rate limit exceeded.") 50 51 // Fire a rate limit exceeded event 52 k.FireEvent(EventRateLimitExceeded, EventKeyFailureMeta{ 53 EventMetaDefault: EventMetaDefault{Message: "API Rate Limit Exceeded", OriginatingRequest: EncodeRequestToEvent(r)}, 54 Path: r.URL.Path, 55 Origin: request.RealIP(r), 56 Key: token, 57 }) 58 59 // Report in health check 60 reportHealthValue(k.Spec, Throttle, "-1") 61 62 return errors.New("API Rate limit exceeded"), http.StatusTooManyRequests 63 } 64 65 // ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail 66 func (k *RateLimitForAPI) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { 67 // Skip rate limiting and quotas for looping 68 if !ctxCheckLimits(r) { 69 return nil, http.StatusOK 70 } 71 72 storeRef := k.Spec.SessionManager.Store() 73 reason := sessionLimiter.ForwardMessage(r, k.apiSess, 74 k.keyName, 75 storeRef, 76 true, 77 false, 78 &k.Spec.GlobalConfig, 79 k.Spec.APIID, 80 false, 81 ) 82 83 if reason == sessionFailRateLimit { 84 return k.handleRateLimitFailure(r, k.keyName) 85 } 86 87 // Request is valid, carry on 88 return nil, http.StatusOK 89 }