Skip to content

Instantly share code, notes, and snippets.

@dvdscripter
Created October 3, 2018 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvdscripter/e6b662d705cbed6620fdf0f0c30488e6 to your computer and use it in GitHub Desktop.
Save dvdscripter/e6b662d705cbed6620fdf0f0c30488e6 to your computer and use it in GitHub Desktop.
API rate-limiting in go
package main
import (
"fmt"
"log"
"net/http"
"sync"
"time"
"github.com/gorilla/mux"
)
type limit struct {
max int
interval time.Duration
counter int
mux sync.Mutex
}
func main() {
r := mux.NewRouter()
limits := &limit{
max: 100,
interval: 10 * time.Second,
}
// set max to 3 and keep hitting localhost:8000
// ab from apache package can also used to test
go limits.start()
r.Use(limits.toLimit)
r.HandleFunc("/", hello)
srv := &http.Server{
Addr: "127.0.0.1:8000",
Handler: r,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello %s!", r.RemoteAddr)
}
func (l *limit) pass() bool {
l.mux.Lock()
defer l.mux.Unlock()
if l.counter >= l.max {
return false
}
l.counter++
return true
}
func (l *limit) start() {
ticker := time.NewTicker(l.interval)
defer ticker.Stop()
// precious resources must not be leaked
for {
select {
case <-ticker.C:
l.mux.Lock()
l.counter = 0
l.mux.Unlock()
}
}
}
func (l *limit) toLimit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !l.pass() {
http.Error(w, "Rate limiting", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment