// 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)
}
Example #2
0
// 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")
	}
}
Example #3
0
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)
		}
	}
}