added media fixes, search, push updates, pwa, metrics. preparing release 1.0
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// Package ratelimit provides a simple in-memory fixed-window limiter
|
||||
// keyed by client IP, used to protect authentication endpoints.
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Limiter is a fixed-window, per-IP request limiter.
|
||||
type Limiter struct {
|
||||
mu sync.Mutex
|
||||
max int
|
||||
window time.Duration
|
||||
hits map[string]*hits
|
||||
}
|
||||
|
||||
type hits struct {
|
||||
windowStart time.Time
|
||||
count int
|
||||
}
|
||||
|
||||
// New returns a limiter allowing max requests per window per IP.
|
||||
func New(max int, window time.Duration) *Limiter {
|
||||
return &Limiter{max: max, window: window, hits: make(map[string]*hits)}
|
||||
}
|
||||
|
||||
// Allow reports whether the request passes the limit, consuming one slot.
|
||||
func (l *Limiter) Allow(r *http.Request) bool {
|
||||
ip := ClientIP(r)
|
||||
now := time.Now()
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
h := l.hits[ip]
|
||||
if h == nil || now.Sub(h.windowStart) >= l.window {
|
||||
h = &hits{windowStart: now}
|
||||
l.hits[ip] = h
|
||||
}
|
||||
h.count++
|
||||
return h.count <= l.max
|
||||
}
|
||||
|
||||
// ClientIP extracts the remote address, preferring X-Forwarded-For when
|
||||
// running behind a reverse proxy.
|
||||
func ClientIP(r *http.Request) string {
|
||||
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
|
||||
if i := strings.IndexByte(fwd, ','); i > 0 {
|
||||
return strings.TrimSpace(fwd[:i])
|
||||
}
|
||||
return strings.TrimSpace(fwd)
|
||||
}
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return host
|
||||
}
|
||||
Reference in New Issue
Block a user