ulule / limiter

Dead simple rate limit middleware for Go.
MIT License
2.09k stars 150 forks source link

Should it be more convenient to use multiple Rates at the same time? #32

Open Hubro opened 6 years ago

Hubro commented 6 years ago

I want to rate limit my API's login endpoint thusly: max 1 request per second, max 5 requests per hour, max 10 requests per day.

I have implemented it like this using your middleware:

func loginLimiterHandler() http.Handler {
    secondRate := limiter.Rate{Period: 1 * time.Second, Limit: 1}
    hourRate := limiter.Rate{Period: 1 * time.Hour, Limit: 5}
    dayRate := limiter.Rate{Period: 24 * time.Hour, Limit: 10}

    secondLimit := limiter.New(memory.NewStore(), secondRate)
    hourLimit := limiter.New(memory.NewStore(), hourRate)
    dayLimit := limiter.New(memory.NewStore(), dayRate)

    var handler http.Handler = http.HandlerFunc(LoginHandler)

    handler = stdlib.NewMiddleware(dayLimit).Handler(handler)
    handler = stdlib.NewMiddleware(hourLimit).Handler(handler)
    handler = stdlib.NewMiddleware(secondLimit).Handler(handler)

    return handler
}

I think it would be practical to add the function limiter.NewMultipleRates (or similar) to allow this:

func loginLimiterHandler() http.Handler {
    secondRate := limiter.Rate{Period: 1 * time.Second, Limit: 1}
    hourRate := limiter.Rate{Period: 1 * time.Hour, Limit: 5}
    dayRate := limiter.Rate{Period: 24 * time.Hour, Limit: 10}

    limit := limiter.NewMultipleRates(memory.NewStore(), secondRate, hourRate, dayRate)

    return stdlib.NewMiddleware(limit).Handler(http.HandlerFunc(LoginHandler))
}

That way you could also properly display all the right HTTP headers. With my solution, when the second-limit blocks the request, the response doesn't include headers about the hourly or daily limits.

My apologies in advance if I've massively over-complicated my solution.

novln commented 6 years ago

Hello,

Thank you for the idea. We'll gladly review your pull request if you want to contribute to the project :slightly_smiling_face: