github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_rate_limiting.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "net/http" 6 "time" 7 8 "github.com/sirupsen/logrus" 9 10 "github.com/TykTechnologies/tyk/request" 11 ) 12 13 var sessionLimiter = SessionLimiter{} 14 var sessionMonitor = Monitor{} 15 16 // RateLimitAndQuotaCheck will check the incomming 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 RateLimitAndQuotaCheck struct { 19 BaseMiddleware 20 } 21 22 func (k *RateLimitAndQuotaCheck) Name() string { 23 return "RateLimitAndQuotaCheck" 24 } 25 26 func (k *RateLimitAndQuotaCheck) EnabledForSpec() bool { 27 return !k.Spec.DisableRateLimit || !k.Spec.DisableQuota 28 } 29 30 func (k *RateLimitAndQuotaCheck) handleRateLimitFailure(r *http.Request, token string) (error, int) { 31 k.Logger().WithField("key", obfuscateKey(token)).Info("Key rate limit exceeded.") 32 33 // Fire a rate limit exceeded event 34 k.FireEvent(EventRateLimitExceeded, EventKeyFailureMeta{ 35 EventMetaDefault: EventMetaDefault{Message: "Key Rate Limit Exceeded", OriginatingRequest: EncodeRequestToEvent(r)}, 36 Path: r.URL.Path, 37 Origin: request.RealIP(r), 38 Key: token, 39 }) 40 41 // Report in health check 42 reportHealthValue(k.Spec, Throttle, "-1") 43 44 return errors.New("Rate limit exceeded"), http.StatusTooManyRequests 45 } 46 47 func (k *RateLimitAndQuotaCheck) handleQuotaFailure(r *http.Request, token string) (error, int) { 48 k.Logger().WithField("key", obfuscateKey(token)).Info("Key quota limit exceeded.") 49 50 // Fire a quota exceeded event 51 k.FireEvent(EventQuotaExceeded, EventKeyFailureMeta{ 52 EventMetaDefault: EventMetaDefault{Message: "Key Quota Limit Exceeded", OriginatingRequest: EncodeRequestToEvent(r)}, 53 Path: r.URL.Path, 54 Origin: request.RealIP(r), 55 Key: token, 56 }) 57 58 // Report in health check 59 reportHealthValue(k.Spec, QuotaViolation, "-1") 60 61 return errors.New("Quota exceeded"), http.StatusForbidden 62 } 63 64 // ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail 65 func (k *RateLimitAndQuotaCheck) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { 66 if ctxGetRequestStatus(r) == StatusOkAndIgnore { 67 return nil, http.StatusOK 68 } 69 70 // Skip rate limiting and quotas for looping 71 if !ctxCheckLimits(r) { 72 return nil, http.StatusOK 73 } 74 75 session := ctxGetSession(r) 76 token := ctxGetAuthToken(r) 77 78 storeRef := k.Spec.SessionManager.Store() 79 reason := sessionLimiter.ForwardMessage( 80 r, 81 session, 82 token, 83 storeRef, 84 !k.Spec.DisableRateLimit, 85 !k.Spec.DisableQuota, 86 &k.Spec.GlobalConfig, 87 k.Spec.APIID, 88 false, 89 ) 90 91 throttleRetryLimit := session.ThrottleRetryLimit 92 throttleInterval := session.ThrottleInterval 93 94 if len(session.AccessRights) > 0 { 95 if rights, ok := session.GetAccessRightByAPIID(k.Spec.APIID); ok { 96 if rights.Limit != nil { 97 throttleInterval = rights.Limit.ThrottleInterval 98 throttleRetryLimit = rights.Limit.ThrottleRetryLimit 99 } 100 } 101 } 102 103 switch reason { 104 case sessionFailNone: 105 case sessionFailRateLimit: 106 err, errCode := k.handleRateLimitFailure(r, token) 107 if throttleRetryLimit > 0 { 108 for { 109 ctxIncThrottleLevel(r, throttleRetryLimit) 110 time.Sleep(time.Duration(throttleInterval * float64(time.Second))) 111 112 reason = sessionLimiter.ForwardMessage( 113 r, 114 session, 115 token, 116 storeRef, 117 !k.Spec.DisableRateLimit, 118 !k.Spec.DisableQuota, 119 &k.Spec.GlobalConfig, 120 k.Spec.APIID, 121 true, 122 ) 123 124 log.WithFields(logrus.Fields{ 125 "middleware": "RateLimitAndQuotaCheck", 126 "func": "ProcessRequest", 127 }).Debugf("after dry-run (reason: '%s')", reason) 128 129 if ctxThrottleLevel(r) > throttleRetryLimit { 130 break 131 } 132 133 if reason == sessionFailNone { 134 return k.ProcessRequest(w, r, nil) 135 } 136 } 137 } 138 return err, errCode 139 140 case sessionFailQuota: 141 return k.handleQuotaFailure(r, token) 142 default: 143 // Other reason? Still not allowed 144 return errors.New("Access denied"), http.StatusForbidden 145 } 146 // Run the trigger monitor 147 if k.Spec.GlobalConfig.Monitor.MonitorUserKeys { 148 sessionMonitor.Check(session, token) 149 } 150 151 // Request is valid, carry on 152 return nil, http.StatusOK 153 }