tollbooth

repository·master·Indexed 25 days ago

https://github.com/didip/tollbooth

A generic middleware for rate-limiting HTTP requests in Go using the Token Bucket algorithm. It supports limiting by IP, path, methods, custom headers, and basic auth usernames. Version 8 introduces HTTPMiddleware for standard router compatibility and requires explicit IP lookup configuration via SetIPLookup.

Tokens
2.3K
Snippets
6
Records
14
Agent score
84%

What's inside tollbooth

  1. Quickstart: Implement rate-limiting with Tollbooth v8

    master

    To use Tollbooth as middleware in a standard Go HTTP server, create a new limiter using tollbooth.NewLimiter, configure your IP lookup strategy, and wrap your handler with tollbooth.HTTPMiddleware.

    Note: In version 8 and above, you must explicitly define how to pick the IP address using SetIPLookup. If an IP address cannot be found, the rate limiter will not be activated.

    package main
    
    import (
    	"net/http"
    
    	"github.com/didip/tollbooth/v8"
    	"github.com/didip/tollbooth/v8/limiter"
    )
    
    func HelloHandler(w http.ResponseWriter, req *http.Request) {
    	w.Write([]byte("Hello, World!"))
    }
    
    func main() {
    	// Create a request limiter per handler.
    	lmt := tollbooth.NewLimiter(1, nil)
    
    	// New in version >= 8, you must explicitly define how to pick the IP address.
    	lmt.SetIPLookup(limiter.IPLookup{
    		Name:           "X-Real-IP",
    		IndexFromRight: 0,
    	})
    
    	// New in version >= 8, HTTPMiddleware is a standard router compatible alternative to the previously used LimitFuncHandler.
    	http.Handle("/", tollbooth.HTTPMiddleware(lmt)(http.HandlerFunc(HelloHandler)))
    
    	http.ListenAndServe(":12345", nil)
    }
  2. Configure Limiter options and expiration

    master

    You can initialize a limiter with ExpirableOptions to ensure token buckets expire after a certain duration, conserving memory.

    Initialization:

    lmt := tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})

    Individual TTL settings:

    • SetTokenBucketExpirationTTL(time.Duration): Custom expiration for token buckets.
    • SetBasicAuthExpirationTTL(time.Duration): Custom expiration for basic auth users.
    • SetHeaderEntryExpirationTTL(time.Duration): Custom expiration for header entries.
    import (
        "time"
        "github.com/didip/tollbooth/v8"
        "github.com/didip/tollbooth/v8/limiter"
    )
    
    lmt := tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})
    
    lmt.SetTokenBucketExpirationTTL(time.Hour)
    lmt.SetBasicAuthExpirationTTL(time.Hour)
    lmt.SetHeaderEntryExpirationTTL(time.Hour)
  3. Customize rejection messages and behavior

    master

    When a request exceeds the limit, you can customize the response sent to the client.

    • SetMessage(string): Set a custom error message.
    • SetMessageContentType(string): Set a custom Content-Type for the error response.
    • SetOnLimitReached(func(http.ResponseWriter, *http.Request)): Provide a custom function to execute when a limit is reached.
    lmt.SetMessage("You have reached maximum request limit.")
    lmt.SetMessageContentType("text/plain; charset=utf-8")
    lmt.SetOnLimitReached(func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("A request was rejected")
    })
  4. Configure IP Lookup strategy

    master

    Use SetIPLookup to define how the rate limiter identifies the client's IP address. This is required in version 8+.

    limiter.IPLookup fields:

    • Name: The name of the lookup method. Supported values are RemoteAddr, X-Forwarded-For, X-Real-IP, and CF-Connecting-IP. Other headers are ignored.
    • IndexFromRight: The index position to pick the IP address from a comma-separated list (counting from right to left).
    lmt.SetIPLookup(limiter.IPLookup{
        Name:           "X-Real-IP",
        IndexFromRight: 0,
    })
  5. Limit by Methods, Basic Auth, and Headers

    master

    Tollbooth allows fine-grained control over which requests are subject to rate limiting.

    • Methods: Restrict limiting to specific HTTP methods using SetMethods([]string).
    • Basic Auth: Limit based on usernames using SetBasicAuthUsers([]string) and RemoveBasicAuthUsers([]string).
    • Headers: Limit based on specific header values using SetHeader(key, values).

    Setters are chainable.

    // Limit only GET and POST requests.
    lmt.SetMethods([]string{"GET", "POST"})
    
    // Limit based on basic auth usernames.
    lmt.SetBasicAuthUsers([]string{"bob", "jane"})
    lmt.RemoveBasicAuthUsers([]string{"vip"})
    
    // Limit request headers containing certain values.
    lmt.SetHeader("X-Access-Token", []string{"abc123", "xyz098"})
    lmt.RemoveHeader("X-Access-Token")
    lmt.RemoveHeaderEntries("X-Access-Token", []string{"limitless-token"})
    
    // Chainable example
    lmt.SetMethods([]string{"GET", "POST"}).
        SetBasicAuthUsers([]string{"sansa"}).
        SetBasicAuthUsers([]string{"tyrion"})
  6. Reference: Rate Limit HTTP Response Headers

    master

    Tollbooth provides several headers in the HTTP response to communicate rate limit status.

    Upon Rejection:

    • X-Rate-Limit-Limit: The maximum request limit.
    • X-Rate-Limit-Duration: The rate-limiter duration.
    • X-Rate-Limit-Request-Forwarded-For: The rejected request X-Forwarded-For value.
    • X-Rate-Limit-Request-Remote-Addr: The rejected request RemoteAddr value.

    Upon both Success and Rejection (Standard Headers):

    • RateLimit-Limit: The maximum request limit within the time window (1s).
    • RateLimit-Reset: The rate-limiter time window duration in seconds (always 1s).
    • RateLimit-Remaining: The remaining tokens.
  7. Perform rate-limit checks for a single request

    master

    LimitByRequest is a low-level function that performs a full rate-limit check for a specific http.Request. It automatically:

    1. Sets rate-limit response headers (X-Rate-Limit-*).
    2. Determines if the request should be skipped based on limiter configuration (IP, methods, headers, context, etc.).
    3. Builds composite keys based on the request properties.
    4. Checks the limit and returns an *errors.HTTPError if exceeded.
  8. Use LimitHandler as HTTP middleware

    master

    LimitHandler is a middleware designed to wrap an http.Handler. It automatically performs rate-limiting checks for every incoming request using the provided limiter. If the limit is reached, it executes the limiter's limit-reached logic and returns the appropriate error response.

    Arguments:

    • lmt (*limiter.Limiter): The configured limiter.
    • next (http.Handler): The next handler in the chain.
  9. Create a new limiter with NewLimiter

    master

    Use NewLimiter as a convenience function to initialize a new limiter.Limiter. It sets the maximum number of requests allowed and automatically sets the burst capacity to match the maximum value (or 1 if the maximum is less than 1).

    Arguments:

    • max (float64): The maximum number of requests allowed.
    • tbOptions (*limiter.ExpirableOptions): Configuration options for the limiter.