rate limiter added
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
const (
|
||||
rateLimit = 20 // times per rateLimitInterval
|
||||
rateLimitInterval = 1 * time.Hour
|
||||
)
|
||||
|
||||
var accesses = &sync.Map{}
|
||||
|
||||
type access struct {
|
||||
count int
|
||||
timestamp time.Time
|
||||
}
|
||||
|
||||
func legitAccess(c echo.Context) bool {
|
||||
ip := c.Request().RemoteAddr
|
||||
aRaw, found := accesses.Load(ip)
|
||||
var a *access
|
||||
if found {
|
||||
a, _ = aRaw.(*access)
|
||||
} else {
|
||||
a = &access{}
|
||||
}
|
||||
a.count++
|
||||
a.timestamp = time.Now()
|
||||
accesses.Store(ip, a)
|
||||
return a.count < rateLimit
|
||||
}
|
||||
|
||||
func cleanAccessRegistry(logger echo.Logger) {
|
||||
for {
|
||||
time.Sleep(rateLimitInterval)
|
||||
t, e := 0, 0
|
||||
accesses.Range(func(ip, aRaw interface{}) bool {
|
||||
t++
|
||||
a, _ := aRaw.(*access)
|
||||
if a.timestamp.Add(rateLimitInterval).Before(time.Now()) {
|
||||
accesses.Delete(ip)
|
||||
e++
|
||||
}
|
||||
return true
|
||||
})
|
||||
if e > 0 {
|
||||
logger.Infof("cleaned up %d/%d outdated accesses", e, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ var (
|
||||
401: "Unauthorized",
|
||||
404: "Not found",
|
||||
412: "Precondition failed",
|
||||
429: "Too many requests",
|
||||
503: "Service unavailable",
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
go persistStats(e.Logger, db, stats)
|
||||
go cleanAccessRegistry(e.Logger)
|
||||
|
||||
e.Renderer = &Template{templates: template.Must(template.ParseGlob("assets/templates/*.html"))}
|
||||
|
||||
e.File("/favicon.ico", "assets/public/favicon.ico")
|
||||
@@ -57,8 +60,6 @@ func main() {
|
||||
e.File("/index.html", "assets/public/index.html")
|
||||
e.File("/", "assets/public/index.html")
|
||||
|
||||
go persistStats(e.Logger, db, stats)
|
||||
|
||||
e.GET("/TOS.md", func(c echo.Context) error {
|
||||
n, code := md2html(c, "TOS")
|
||||
return c.Render(code, "Page", n)
|
||||
@@ -111,6 +112,11 @@ func main() {
|
||||
|
||||
e.POST("/note", func(c echo.Context) error {
|
||||
c.Logger().Debug("POST /note requested")
|
||||
if !legitAccess(c) {
|
||||
code := http.StatusTooManyRequests
|
||||
c.Logger().Errorf("rate limit exceeded for %s", c.Request().RemoteAddr)
|
||||
return c.Render(code, "Note", errPage(code))
|
||||
}
|
||||
vals, err := c.FormParams()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user