A very basic and naive in-memory rate limiter middleware. Compatible with standard library (i think?) as it uses http.Handler interface
THIS IS NAIVE & NOT RECOMMENDED 😁
go get github.com/Noblefel/perisai
Example #1 - limit by user id:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ping"))
})
http.ListenAndServe("localhost:8080", auth(mux))
}
// example as outer variable to be grouped inside other middleware,
// OR you could just set it locally and manually assign this to each route
var limiter = perisai.New(10, 8*time.Second, perisai.FuncUserId)
// example a typical authentication middleware
func auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
userId, err := verifyToken(tokenString)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user_id", userId) // <--
limiter.Handle(next).ServeHTTP(w, r.WithContext(ctx)) // <--
})
}
func verifyToken(string) (int, error) { return 1, nil }Example #2 - limit by ip:
var throttle = perisai.New(40, 5*time.Second, perisai.FuncIP)Example #3 - custom value func
scenario: limit post request once every 10s. since method "post" will be too common to be incremented, we'll concat it with user id so it wont affect others.
var postLimiter = perisai.New(1, 10*time.Second, func(r *http.Request) any {
if r.Method != "POST" {
return nil // ignore other methods
}
if id := r.Context().Value("user_id"); id != nil {
return fmt.Sprintf("%d:post", id)
}
return nil
})