// The middleware handler func (l *Limiter) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) { apiKey := req.Header.Get("Api-Key") // Here, in real life, we would typically create a limiter // based on information from the given API key hasher := speedbump.PerMinuteHasher{} limiter := speedbump.NewLimiter(l.redisConnexion, hasher, 5) canAccess, err := limiter.Attempt(apiKey) // Trouble with Redis? if err != nil { respondRedisError(w) return } // Over the rate limit? if !canAccess { responses.WriteMessage(429, "rate_exceeded", "API rate exceeded. Too many requests.", w) return } requestsLeft, err := limiter.Left(apiKey) // Trouble with Redis? if err != nil { respondRedisError(w) return } // Add the number of remaining request as a header w.Header().Set("Api-Remaining", strconv.Itoa(int(requestsLeft))) // Call the next middleware handler next(w, req) }
// RateLimitLB is very similar to RateLimit but it takes the X-Forwarded-For // header in cosideration when trying to figure the IP address of the client. // This is useful for when running a server behind a load balancer or proxy. // // However, this header can be spoofed by the client, so in some cases it could // provide a way of getting around the rate limiter. // // When using this middleware, make sure the load balancer will strip any // X-Forwarded-For headers set by the client, and that the server will not be // publicly accessible by the public, just the load balancer. func RateLimitLB(client *redis.Client, hasher speedbump.RateHasher, max int64) gin.HandlerFunc { limiter := speedbump.NewLimiter(client, hasher, max) return func(c *gin.Context) { // Attempt to perform the request ip := GetRequesterAddress(c.Request) ok, err := limiter.Attempt(ip) if err != nil { panic(err) } if !ok { nextTime := time.Now().Add(hasher.Duration()) c.JSON(429, gin.H{ "status": "error", "messages": []string{"Rate limit exceeded. Try again in " + humanize.Time(nextTime)}, }) c.Abort() } c.Next() // After the request // log.Print(ip + " was limited because it exceeded the max rate") } }
func RateLimit(client *redis.Client, hasher speedbump.RateHasher, max int64) negroni.HandlerFunc { limiter := speedbump.NewLimiter(client, hasher, max) rnd := render.New() return func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { ip, _, _ := net.SplitHostPort(r.RemoteAddr) ok, err := limiter.Attempt(ip) if err != nil { panic(err) } if !ok { nextTime := time.Now().Add(hasher.Duration()) rnd.JSON(rw, 429, map[string]string{"error": "Rate limit exceeded. Try again in " + humanize.Time(nextTime)}) } else { next(rw, r) } } }