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  }