Skip to content

Commit

Permalink
feat: Add rate limit (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajnavarro authored Apr 5, 2024
1 parent 8d7f735 commit 0aeed85
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Starts the indexer service, which includes the fetcher and JSON-RPC server

FLAGS
-db-path indexer-db the absolute path for the indexer DB (embedded)
-http-rate-limit 0 the maximum HTTP requests allowed per minute per IP, unlimited by default
-listen-address 0.0.0.0:8546 the IP:PORT URL for the indexer JSON-RPC server
-log-level info the log level for the CLI output
-max-chunk-size 100 the range for fetching blockchain data by a single worker
Expand Down
30 changes: 30 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"errors"
"flag"
"fmt"
"net/http"
"time"

"github.com/go-chi/chi/v5"
"github.com/go-chi/httprate"
"github.com/peterbourgon/ff/v3/ffcli"
"go.uber.org/zap"

Expand All @@ -31,6 +34,8 @@ type startCfg struct {

maxSlots int
maxChunkSize int64

rateLimit int
}

// newStartCmd creates the indexer start command
Expand Down Expand Up @@ -95,6 +100,13 @@ func (c *startCfg) registerFlags(fs *flag.FlagSet) {
fetch.DefaultMaxChunkSize,
"the range for fetching blockchain data by a single worker",
)

fs.IntVar(
&c.rateLimit,
"http-rate-limit",
0,
"the maximum HTTP requests allowed per minute per IP, unlimited by default",
)
}

// exec executes the indexer start command
Expand Down Expand Up @@ -149,6 +161,24 @@ func (c *startCfg) exec(ctx context.Context) error {
)

mux := chi.NewMux()

if c.rateLimit != 0 {
logger.Info("rate-limit set", zap.Int("rate-limit", c.rateLimit))
mux.Use(httprate.Limit(
c.rateLimit,
1*time.Minute,
httprate.WithKeyFuncs(httprate.KeyByRealIP),
httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
//nolint:errcheck // no need to handle error here, it had been checked before
ip, _ := httprate.KeyByRealIP(r)
logger.Debug("too many requests", zap.String("from", ip))

// send a json response to give more info when using the graphQL explorer
http.Error(w, `{"error": "too many requests"}`, http.StatusTooManyRequests)
}),
))
}

mux = j.SetupRoutes(mux)
mux = graph.Setup(db, em, mux)

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/gnolang/gno v0.0.0-20231215125729-9262c1a8f949
github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect
github.com/go-chi/httprate v0.9.0
golang.org/x/crypto v0.21.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1G
github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/httprate v0.9.0 h1:21A+4WDMDA5FyWcg7mNrhj63aNT8CGh+Z1alOE/piU8=
github.com/go-chi/httprate v0.9.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
Expand Down

0 comments on commit 0aeed85

Please sign in to comment.