Files
junk2jive-server/internal/limiter/limiter.go
rogueking 84e150cb11
Some checks failed
Build and Push Docker Image / Build and push image (push) Failing after 43s
Run Go Tests / build (push) Failing after 0s
golangci-lint / lint (push) Failing after 2m0s
added workflows and fixed router
2025-05-06 14:49:14 -07:00

65 lines
1.4 KiB
Go

package limiter
import (
"fmt"
"net"
"net/http"
"sync"
"golang.org/x/time/rate"
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response"
)
type RateLimiter struct {
limiters map[string]map[string]*rate.Limiter // map[path][ip] => limiter
mu sync.RWMutex
}
func New() *RateLimiter {
return &RateLimiter{
limiters: make(map[string]map[string]*rate.Limiter),
}
}
func GetIP(r *http.Request) string {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
return ip
}
func (rl *RateLimiter) Get(path string, ip string, r rate.Limit, burst int) *rate.Limiter {
rl.mu.Lock()
defer rl.mu.Unlock()
if _, exists := rl.limiters[path]; !exists {
rl.limiters[path] = make(map[string]*rate.Limiter)
}
if limiter, exists := rl.limiters[path][ip]; exists {
return limiter
}
limiter := rate.NewLimiter(r, burst)
rl.limiters[path][ip] = limiter
return limiter
}
func (rl *RateLimiter) Middleware(rateLimit rate.Limit, burst int) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := GetIP(r)
limiter := rl.Get(r.URL.Path, ip, rateLimit, burst)
if !limiter.Allow() {
response.RespondWithError(w, r, http.StatusTooManyRequests, http.StatusText(http.StatusTooManyRequests), fmt.Errorf("too many requests"))
return
}
next.ServeHTTP(w, r)
})
}
}