4

I already created a function for limiting to 50 requests for API logins in one day.

var limit = 50

package middleware

import (
    "log"
    "net"
    "net/http"
    "sync"
    "time"

    "golang.org/x/time/rate"
)

// Create a custom request struct which holds the rate limiter for each
// visitor and the last time that the request was seen.
type request struct {
    limiter  *rate.Limiter
    lastSeen time.Time
}

// Change the the map to hold values of the type request.
// defaultTime using 3 minutes
var requests = make(map[string]*request)
var mu sync.Mutex

func getRequest(ip string, limit int) *rate.Limiter {
    mu.Lock()
    defer mu.Unlock()

    v, exists := requests[ip]
    if !exists {
        limiter := rate.NewLimiter(1, limit)
        requests[ip] = &request{limiter, time.Now()}
        return limiter
    }
    // Update the last seen time for the visitor.
    v.lastSeen = time.Now()
    return v.limiter
}

func throttle(next http.Handler, limit int) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip, _, err := net.SplitHostPort(r.RemoteAddr)
        if err != nil {
            log.Println(err.Error())
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            return
        }
        limiter := getRequest(ip, limit)
        fmt.Println(limiter.Allow())
        if limiter.Allow() == false {
            http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Is it correct?

Because when I try it, it still passes. The function limit is not working.

I doubt with NewLimiter()

 limiter := rate.NewLimiter(1, limit)

Does it mean one user only can request login 50 requests per day? (I already read the docs, but I do not understand.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
agny reza
  • 137
  • 2
  • 7

2 Answers2

3

From the rate docs:

func NewLimiter(r Limit, b int) *Limiter

NewLimiter returns a new Limiter that allows events up to rate r and permits bursts of at most b tokens.


So the first parameter is the rate-limit, not the second. Burst is the number of requests you want to allow that occur faster than the rate-limit - typically one uses a value of 1 to disallow bursting, anything higher will let this number of requests in before the regular rate-limit kicks in. Anyway...

To create the rate.Limit for your needs, you can use the helper function rate.Every():

rt := rate.Every(24*time.Hour / 50)

limiter := rate.NewLimiter(rt, 1)
colm.anseo
  • 19,337
  • 4
  • 43
  • 52
  • i already test with rt := rate.Every(5*time.Minute / 5) limiter := rate.NewLimiter(rt, 1) means 5req/5 minute so if i request 6 times under 5 minute will give me error (to many request) ?? but when i test its not should be – agny reza Mar 23 '20 at 16:20
  • 1
    are you sure your test was valid? You are storing each limiter under an `ip` key - are you sure that key is the same each time? Maybe remove the individual limiters first - and use a single one - to prove the limiter is working first - before switching to individual `ip` limiters. – colm.anseo Mar 23 '20 at 16:27
  • @agnyreza Here is a working example of rate-limiter given an acceptable rate of requests; then an exceeded level - and then switching back: https://play.golang.org/p/dWH6ZQY8tD1 – colm.anseo Mar 23 '20 at 17:38
  • @colm.anseo I modified your code code a little bit, just using 0.0 as Limit.. and something weird occurs: all the call to Allow() returns true.. Using limit 0.0 should not allow any request.. that right? https://play.golang.org/p/hVPT-KUPMAN – Fco Mar 11 '21 at 13:07
  • @colm.anseo Sorry this one: https://play.golang.org/p/_vqaCPq58SZ Because the first one uses Every (and Every return Inf in case of 0) – Fco Mar 11 '21 at 13:26
1

NewLimited(1, 50) means 1 request/second with a burst of up to 50 requests. It's a token bucket, which means that there are 50 tokens, each accepted API call uses up one token, and the tokens are regenerated at the given rate, up to burst. Your code is creating a limiter per IP address, so that's a limit per IP address (which I guess you are approximating as one IP address is one user).

If you're running on a single persistent server, and the server and code never restarts, then you may be able to get something like 50 requests/day per user by specifying a rate of 50 / (3600*24) and a burst of 50. (Note: 3600*24 is the number of seconds in a day). But the rate limiting package you're using is not designed for such coarse rate-limiting (on the order of requests per day) -- it's designed to prevent server overload under heavy traffic in the short term (on the order of requests per second).

You probably want a rate-limiter that works with a database or similar (perhaps using a token bucket scheme, since that can be implemented efficiently). Probably there's a package somewhere for that, but I don't know of one of the top of my head.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118