limitter

package module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2023 License: MIT Imports: 11 Imported by: 0

README

gin-request-limitter

Request limitter as middleware for gin-gonic framework: limit requests can be accepted of an user.

Features:

  • Limit request interval: user can not send request too fast
  • Limit request freequently:
    • User can not send too many requests in a time window.
    • Implement Fixed windows algorithm using Redis or Google Firestore as persistence.

Usage

  • Install
go get github.com/zeroboo/gin-request-limitter
  • Run

ctx := context.Background()
dsClient, _ := datastore.NewClient(ctx, datastoreProjectId, option.WithCredentialsFile(serviceAccount))
  
/*
Create a limitter middleware per user:
  persists data to kind 'tracker' in datastore
  collect userId from gin context by field 'userId'
  restricts request only send to 200 milis after the last one
  restricts maximum 100 requests per 60 seconds
*/
handler := CreateDatastoreBackedLimitter(dsClient,
  "tracker",
  GetUserIdFromContextByField("userId"), 
  200, 
  60000, 
  100),
  
//use handler ...
  • Test
go test -timeout 60s github.com/zeroboo/gin-request-limitter -v

Documentation

Index

Constants

View Source
const DefaultRequestTrackingWindowMilis int64 = 60000
View Source
const DefaultSessionExpirationSeconds int64 = 3600
View Source
const MIN_REQUEST_INTERVAL_MILIS int64 = 200
View Source
const VALIDATE_RESULT_FAILED int = -3
View Source
const VALIDATE_RESULT_TOO_FAST int = -1
View Source
const VALIDATE_RESULT_TOO_FREQUENTLY int = -2
View Source
const VALIDATE_RESULT_VALID int = 1

Variables

View Source
var ErrorRequestTooFast = fmt.Errorf("request is too fast")
View Source
var ErrorRequestTooFreequently = fmt.Errorf("request is too freequently")

Functions

func CreateDatastoreBackedLimitter

func CreateDatastoreBackedLimitter(pClient *datastore.Client, pTrackerKind string,
	pUserIdExtractor func(c *gin.Context) string,
	pConfig *LimitterConfig,
	pIsMiddleware bool) func(c *gin.Context)

func CreateDatastoreBackedLimitterHandler added in v0.2.3

func CreateDatastoreBackedLimitterHandler(client *datastore.Client,
	trackerKind string,
	getUserIdFromContext func(c *gin.Context) string,
	minRequestIntervalMilis int64,
	windowFrameMilis int64,
	maxRequestInWindow int,
	sessionExpirationSeconds int64) func(c *gin.Context)

CreateDatastoreBackedLimitter returns a limitter with given config as middleware.

Limitter aborts gin context if validating failed. If limitter has internal error, it will leaves the context run by calling c.Next() Params:

  • trackerKind: Kind of tracker in datastore

  • GetUserIdFromContext: Function to extract userid from a gin context

  • minRequestIntervalMilis: Minimum time between 2 requests, 0 means no limit

  • windowFrameMilis: Window frame in miliseconds, 0 means no limit

  • maxRequestInWindow: Max request in a window frame

func CreateDatastoreBackedLimitterMiddleware added in v0.2.3

func CreateDatastoreBackedLimitterMiddleware(client *datastore.Client,
	trackerKind string,
	getUserIdFromContext func(c *gin.Context) string,
	minRequestIntervalMilis int64,
	windowFrameMilis int64,
	maxRequestInWindow int,
	sessionExpirationSeconds int64) func(c *gin.Context)

CreateDatastoreBackedLimitter returns a limitter with given config as middleware.

Limitter aborts gin context if validating failed. If limitter has internal error, it will leaves the context run by calling c.Next() Params:

  • trackerKind: Kind of tracker in datastore

  • GetUserIdFromContext: Function to extract userid from a gin context

  • minRequestIntervalMilis: Minimum time between 2 requests, 0 means no limit

  • windowFrameMilis: Window frame in miliseconds, 0 means no limit

  • maxRequestInWindow: Max request in a window frame

func CreateRedisBackedLimitter added in v0.2.4

func CreateRedisBackedLimitter(pUserIdExtractor func(c *gin.Context) string,
	pConfig *LimitterConfig, pIsMiddleware bool) func(c *gin.Context)

func CreateRedisBackedLimitterHandler added in v0.2.4

func CreateRedisBackedLimitterHandler(getUserIdFromContext func(c *gin.Context) string,
	minRequestIntervalMilis int64,
	windowFrameMilis int64,
	maxRequestInWindow int,
	sessionExpirationSeconds int64) func(c *gin.Context)

func CreateRedisBackedLimitterMiddleware added in v0.2.4

func CreateRedisBackedLimitterMiddleware(getUserIdFromContext func(c *gin.Context) string,
	minRequestIntervalMilis int64,
	windowFrameMilis int64,
	maxRequestInWindow int,
	sessionExpirationSeconds int64) func(c *gin.Context)

func CreateRedisTrackerKey added in v0.2.0

func CreateRedisTrackerKey(pUserId string, pUrl string) string

func CreateTrackerName added in v0.0.4

func CreateTrackerName(userId string, url string) string

CreateTrackerName returns key of tracker based on userId and request URL. Key is a hash string to prevent invalid key in datastore

func GetUserIdFromContextByField

func GetUserIdFromContextByField(userIdField string) func(c *gin.Context) string

GetUserIdFromContextByField extracts userId from a gin context by property name

func InitRedis added in v0.2.0

func InitRedis(pKeyPrefix string, pEnvironment string, pRedisServerAddress string, pRedisPassword string, pRedisDatabase int)

func ProcessValidateResult

func ProcessValidateResult(validateError error, c *gin.Context, isMiddleware bool)

ProcessValidateResult aborts gin context if there is an error, let gin context run otherwise

func SaveRedisRequestTracker added in v0.2.0

func SaveRedisRequestTracker(ctx context.Context, rClient *redis.Client, tracker *RequestTracker, expireSecond int64) error

func ValidateRequest

func ValidateRequest(tracker *RequestTracker,
	currentTime time.Time,
	requestURL string,
	requestClientIP string,
	limitterConfig *LimitterConfig) error

ValidateRequest returns nil if request is valid, an error means invalid request

Types

type LimitterConfig added in v0.0.3

type LimitterConfig struct {
	//Time between 2 requests in milisecs. 0 means no limit
	MinRequestInterval int64

	//Window frame in milisec. Value 0 means no limit
	WindowSize int64

	//Max requests per window
	MaxRequestPerWindow int64

	//If true, error when save/load tracker will abort request
	//If false, request will be served even if save/load tracker error
	AbortOnFail bool

	//ExpSec is sesion expiration in seconds
	ExpSec int64
}

func (*LimitterConfig) CreateExpiration added in v0.1.2

func (config *LimitterConfig) CreateExpiration(Now time.Time) time.Time

type RequestTracker

type RequestTracker struct {
	UID string `redis:"uid" datastore:"uid"`
	URL string `redis:"url" datastore:"url"`

	//WindowNum is index of current window
	WindowNum int64 `redis:"winNum" datastore:"winNum"`

	//WindowRequest is calls of request in current window
	WindowRequest int64 `redis:"winReq" datastore:"winReq"`
	//Last time request in millisec
	LastCall int64 `redis:"last" datastore:"last"`

	//Exp is expiration of this tracker as unix millisecond
	Exp int64 `redis:"exp" datastore:"exp"`
}

----------------------------------------------------------------------------------------------------

func LoadRedisRequestTracker added in v0.2.0

func LoadRedisRequestTracker(ctx context.Context, rClient *redis.Client, userId string, url string) (*RequestTracker, error)

func LoadUserTracker added in v0.0.5

func LoadUserTracker(Client *datastore.Client, TrackerKind string, URL string, UserId string) (*datastore.Key, *RequestTracker, error)

LoadUserTracker returns a tracker

func NewRequestTracker added in v0.2.0

func NewRequestTracker(uid string, url string) *RequestTracker

func NewRequestTrackerWithExpiration added in v0.2.0

func NewRequestTrackerWithExpiration(uid string, url string, expiration time.Time) *RequestTracker

func (*RequestTracker) IsRequestTooFast

func (tracker *RequestTracker) IsRequestTooFast(currentTime time.Time, requestMinIntervalMilis int64) bool

func (*RequestTracker) IsRequestTooFrequently

func (tracker *RequestTracker) IsRequestTooFrequently(currentTime time.Time, maxRequestPerWindow int64) bool

func (*RequestTracker) String added in v0.1.3

func (tracker *RequestTracker) String() string

func (*RequestTracker) UpdateRequest

func (tracker *RequestTracker) UpdateRequest(currentTime time.Time, config *LimitterConfig)

func (*RequestTracker) UpdateWindow

func (tracker *RequestTracker) UpdateWindow(currentTime time.Time, windowMilis int64)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL