Skip to content

Commit

Permalink
Add Caddyfile support
Browse files Browse the repository at this point in the history
Still needs matchers but that can come later
  • Loading branch information
mholt committed Apr 1, 2021
1 parent d00cbd5 commit f96ccfc
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 12 deletions.
171 changes: 162 additions & 9 deletions caddyfile.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package caddyrl

import (
"fmt"
"strconv"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
Expand All @@ -12,17 +14,168 @@ func init() {
httpcaddyfile.RegisterHandlerDirective("rate_limit", parseCaddyfile)
}

// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
return fmt.Errorf("TODO: not yet implemented")
}
return nil
}

// parseCaddyfile unmarshals tokens from h into a new Middleware.
func parseCaddyfile(helper httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var h Handler
err := h.UnmarshalCaddyfile(helper.Dispenser)
return h, err
}

// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
//
// rate_limit {
// zone <name> {
// key <string>
// window <duration>
// events <max_events>
// }
// distributed {
// read_interval <duration>
// write_interval <duration>
// }
// storage <module...>
// jitter <percent>
// sweep_interval <duration>
// }
//
func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if d.NextArg() {
return d.ArgErr()
}

for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "zone":
if !d.NextArg() {
return d.ArgErr()
}
zoneName := d.Val()

var zone RateLimit
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "key":
if !d.NextArg() {
return d.ArgErr()
}
if zone.Key != "" {
return d.Errf("zone key already specified: %s", zone.Key)
}
zone.Key = d.Val()

case "window":
if !d.NextArg() {
return d.ArgErr()
}
if zone.Window != 0 {
return d.Errf("zone window already specified: %s", zone.Window)
}
window, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid window duration '%s': %v", d.Val(), err)
}
zone.Window = caddy.Duration(window)

case "events":
if !d.NextArg() {
return d.ArgErr()
}
if zone.MaxEvents != 0 {
return d.Errf("zone max events already specified: %s", zone.MaxEvents)
}
maxEvents, err := strconv.Atoi(d.Val())
if err != nil {
return d.Errf("invalid max events integer '%s': %v", d.Val(), err)
}
zone.MaxEvents = maxEvents
}
}
if zone.Window == 0 || zone.MaxEvents == 0 {
return d.Err("a rate limit zone requires both a window and maximum events")
}

if h.RateLimits == nil {
h.RateLimits = make(map[string]*RateLimit)
}
h.RateLimits[zoneName] = &zone

case "distributed":
h.Distributed = new(DistributedRateLimiting)

for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "read_interval":
if !d.NextArg() {
return d.ArgErr()
}
if h.Distributed.ReadInterval != 0 {
return d.Errf("read interval already specified: %s", h.Distributed.ReadInterval)
}
interval, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid read interval '%s': %v", d.Val(), err)
}
h.Distributed.ReadInterval = caddy.Duration(interval)

case "write_interval":
if !d.NextArg() {
return d.ArgErr()
}
if h.Distributed.WriteInterval != 0 {
return d.Errf("write interval already specified: %s", h.Distributed.WriteInterval)
}
interval, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid write interval '%s': %v", d.Val(), err)
}
h.Distributed.WriteInterval = caddy.Duration(interval)
}
}

case "storage":
if !d.NextArg() {
return d.ArgErr()
}
modID := "caddy.storage." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return err
}
storage, ok := unm.(caddy.StorageConverter)
if !ok {
return d.Errf("module %s is not a caddy.StorageConverter", modID)
}
h.StorageRaw = caddyconfig.JSONModuleObject(storage, "module", storage.(caddy.Module).CaddyModule().ID.Name(), nil)

case "jitter":
if !d.NextArg() {
return d.ArgErr()
}
if h.Jitter != 0 {
return d.Errf("jitter already specified: %s", h.Jitter)
}
jitter, err := strconv.ParseFloat(d.Val(), 64)
if err != nil {
return d.Errf("invalid jitter percentage '%s': %v", d.Val(), err)
}
h.Jitter = jitter

case "sweep_interval":
if !d.NextArg() {
return d.ArgErr()
}
if h.SweepInterval != 0 {
return d.Errf("sweep interval already specified: %s", h.SweepInterval)
}
interval, err := caddy.ParseDuration(d.Val())
if err != nil {
return d.Errf("invalid sweep interval '%s': %v", d.Val(), err)
}
h.SweepInterval = caddy.Duration(interval)
}
}
}

return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/mholt/caddy-ratelimit
go 1.16

require (
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210330201520-aac1ccf12d00
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210401181228-7da9241fd7d1
github.com/caddyserver/certmagic v0.12.1-0.20210224184602-7550222c4a6a
go.uber.org/zap v1.16.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTK
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210330201520-aac1ccf12d00 h1:myf2INadYSYDQis6BrAcAqUIjCoxidatsVdx7oQJAg8=
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210330201520-aac1ccf12d00/go.mod h1:YBJFK0UJhl86DT7EgeZrmZ7R923h6CHQ/3U8tHB0Av0=
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210401181228-7da9241fd7d1 h1:pAZEcBgskvHSUmp1ajKq99pcKrlpMIhe9jarStTmh00=
github.com/caddyserver/caddy/v2 v2.4.0-beta.1.0.20210401181228-7da9241fd7d1/go.mod h1:YBJFK0UJhl86DT7EgeZrmZ7R923h6CHQ/3U8tHB0Av0=
github.com/caddyserver/certmagic v0.12.1-0.20210224184602-7550222c4a6a h1:gcqCMCPuHlQPY+XZzOilNouNeRpH40UhMfSp0tmulxI=
github.com/caddyserver/certmagic v0.12.1-0.20210224184602-7550222c4a6a/go.mod h1:dNOzF4iOB7H9E51xTooMB90vs+2XNVtpnx0liQNsQY4=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
Expand Down

0 comments on commit f96ccfc

Please sign in to comment.