• Stars
    star
    2,580
  • Rank 17,765 (Top 0.4 %)
  • Language
    Go
  • License
    MIT License
  • Created over 9 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple middleware to rate-limit HTTP requests.

GoDoc license

Tollbooth

This is a generic middleware to rate-limit HTTP requests.

NOTE 1: This library is considered finished.

NOTE 2: Major version changes are backward-incompatible. v2.0.0 streamlines the ugliness of the old API.

Versions

v1.0.0: This version maintains the old API but all the thirdparty modules are moved to their own repo.

v2.x.x: Brand-new API for the sake of code cleanup, thread safety, & auto-expiring data structures.

v3.x.x: Apparently we have been using golang.org/x/time/rate incorrectly. See issue #48. It always limits X number per 1 second. The time duration is not changeable, so it does not make sense to pass TTL to tollbooth.

v4.x.x: Float64 for max requests per second

v5.x.x: go.mod and go.sum

v6.x.x: Replaced go-cache with github.com/go-pkgz/expirable-cache because go-cache leaks goroutines.

v7.x.x: Replaced time/rate with embedded time/rate so that we can support more rate limit headers.

Five Minute Tutorial

package main

import (
    "net/http"

    "github.com/didip/tollbooth/v7"
)

func HelloHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("Hello, World!"))
}

func main() {
    // Create a request limiter per handler.
    http.Handle("/", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, nil), HelloHandler))
    http.ListenAndServe(":12345", nil)
}

Features

  1. Rate-limit by request's remote IP, path, methods, custom headers, & basic auth usernames.

    import (
        "time"
    
        "github.com/didip/tollbooth/v7"
        "github.com/didip/tollbooth/v7/limiter"
    )
    
    lmt := tollbooth.NewLimiter(1, nil)
    
    // or create a limiter with expirable token buckets
    // This setting means:
    // create a 1 request/second limiter and
    // every token bucket in it will expire 1 hour after it was initially set.
    lmt = tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour})
    
    // Configure list of places to look for IP address.
    // By default it's: "RemoteAddr", "X-Forwarded-For", "X-Real-IP"
    // If your application is behind a proxy, set "X-Forwarded-For" first.
    lmt.SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"})
    
    // Limit only GET and POST requests.
    lmt.SetMethods([]string{"GET", "POST"})
    
    // Limit based on basic auth usernames.
    // You add them on-load, or later as you handle requests.
    lmt.SetBasicAuthUsers([]string{"bob", "jane", "didip", "vip"})
    // You can remove them later as well.
    lmt.RemoveBasicAuthUsers([]string{"vip"})
    
    // Limit request headers containing certain values.
    // You add them on-load, or later as you handle requests.
    lmt.SetHeader("X-Access-Token", []string{"abc123", "xyz098"})
    // You can remove all entries at once.
    lmt.RemoveHeader("X-Access-Token")
    // Or remove specific ones.
    lmt.RemoveHeaderEntries("X-Access-Token", []string{"limitless-token"})
    
    // By the way, the setters are chainable. Example:
    lmt.SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}).
        SetMethods([]string{"GET", "POST"}).
        SetBasicAuthUsers([]string{"sansa"}).
        SetBasicAuthUsers([]string{"tyrion"})
  2. Compose your own middleware by using LimitByKeys().

  3. Header entries and basic auth users can expire over time (to conserve memory).

    import "time"
    
    lmt := tollbooth.NewLimiter(1, nil)
    
    // Set a custom expiration TTL for token bucket.
    lmt.SetTokenBucketExpirationTTL(time.Hour)
    
    // Set a custom expiration TTL for basic auth users.
    lmt.SetBasicAuthExpirationTTL(time.Hour)
    
    // Set a custom expiration TTL for header entries.
    lmt.SetHeaderEntryExpirationTTL(time.Hour)
  4. Upon rejection, the following HTTP response headers are available to users:

    • 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.

    • X-Rate-Limit-Request-Remote-Addr The rejected request RemoteAddr.

    Upon both success and rejection RateLimit headers are sent:

    • 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.

  5. Customize your own message or function when limit is reached.

    lmt := tollbooth.NewLimiter(1, nil)
    
    // Set a custom message.
    lmt.SetMessage("You have reached maximum request limit.")
    
    // Set a custom content-type.
    lmt.SetMessageContentType("text/plain; charset=utf-8")
    
    // Set a custom function for rejection.
    lmt.SetOnLimitReached(func(w http.ResponseWriter, r *http.Request) { fmt.Println("A request was rejected") })
  6. Tollbooth does not require external storage since it uses an algorithm called Token Bucket (Go library: golang.org/x/time/rate).

Other Web Frameworks

Sometimes, other frameworks require a little bit of shim to use Tollbooth. These shims below are contributed by the community, so I make no promises on how well they work. The one I am familiar with are: Chi, Gin, and Negroni.

My other Go libraries

  • ErrStack: A small library to combine errors and also display filename and line number.

  • Stopwatch: A small library to measure latency of things. Useful if you want to report latency data to Graphite.

  • LaborUnion: A dynamic worker pool library.

  • Gomet: Simple HTTP client & server long poll library for Go. Useful for receiving live updates without needing Websocket.

Contributions

Before sending a PR with code changes, please make sure altered code is covered with tests which are passing, and that golangci-lint shows no errors.

To check the linter output, install it and then run golangci-lint run in the root directory of the repository.

More Repositories

1

shawty

Smallest URL shortener in Go
Go
307
star
2

bayes_on_redis

Bayesian classifier on top of Redis
Python
63
star
3

tollbooth_gin

Tollbooth - Gin integration layer
Go
57
star
4

beaker_extensions

Extending beaker (cache & session module) to use No SQL backend.
Python
34
star
5

stopwatch

A small library to measure latency of things.
Go
31
star
6

laborunion

Dynamic Worker Pool Library
Go
27
star
7

gomet

Simple HTTP client & server long poll library for Go
Go
25
star
8

tollbooth_echo

Tollbooth - Echo integration layer
Go
23
star
9

tornado_api

Collection of web service libraries for Tornado web framework
Python
20
star
10

tollbooth_chi

Tollbooth - Chi integration layer
Go
12
star
11

tornado-stripe

Tornado client library for accessing Stripe API
Python
11
star
12

errstack

A very small library to combine errors
Go
9
star
13

tollbooth_fasthttp

Tollbooth - Fasthttp integration layer
Go
6
star
14

.js

Didip's dotjs files
JavaScript
5
star
15

tollbooth_iris

Tollbooth - Iris integration layer
Go
3
star
16

tcsv

Simpler, streamable, more compact, easier to read, gzip friendlier than JSON. Hopefully...
3
star
17

go-urldownloader

Small HTTP daemon that downloads URL content and reupload to S3
Go
3
star
18

mcrouter-hub

An HTTP companion to Facebook's McRouter
Go
3
star
19

jazz

What if you can handle HTTP requests in Javascript in Go?
Go
3
star
20

tollbooth_negroni

Tollbooth - Negroni integration layer
Go
2
star
21

go-nanomsg-examples

Examples on how to use nanomsg in Go language
Go
2
star
22

gravatar-python

Simple Gravatar client library
Python
2
star
23

jquery.html.encode.decode

JQuery plugin for HTML encoding and decoding
JavaScript
2
star
24

tollbooth_httprouter

Tollbooth - HTTPRouter integration layer
Go
2
star
25

jquery.scroll

Cross Browser scrollX, scrollY, scrollMaxX, scrollMaxY
JavaScript
2
star
26

mcrouter-util

Collections of things to make mcrouter easier.
C++
1
star
27

didip.github.io

Didip Blog
HTML
1
star
28

supervisor_remote_logging

Emits supervisor logs to various remote endpoints.
Python
1
star