@@ -32,6 +32,7 @@ import (
3232 "github.com/textileio/textile/v2/api/common"
3333 mdb "github.com/textileio/textile/v2/mongodb"
3434 "go.mongodb.org/mongo-driver/mongo"
35+ "golang.org/x/time/rate"
3536 "google.golang.org/grpc"
3637)
3738
@@ -68,6 +69,9 @@ type Gateway struct {
6869 ipfs iface.CoreAPI
6970
7071 emailSessionBus * broadcast.Broadcaster
72+
73+ maxEventsPerSec int
74+ maxBurstSize int
7175}
7276
7377// Config defines the gateway configuration.
@@ -81,6 +85,8 @@ type Config struct {
8185 Collections * mdb.Collections
8286 IPFSClient iface.CoreAPI
8387 EmailSessionBus * broadcast.Broadcaster
88+ MaxEventsPerSec int
89+ MaxBurstSize int
8490 Hub bool
8591 Debug bool
8692}
@@ -111,6 +117,14 @@ func NewGateway(conf Config) (*Gateway, error) {
111117 if err != nil {
112118 return nil , err
113119 }
120+
121+ if conf .MaxEventsPerSec < 1 {
122+ conf .MaxEventsPerSec = 1000
123+ }
124+ if conf .MaxBurstSize < 1 {
125+ conf .MaxBurstSize = 20
126+ }
127+
114128 return & Gateway {
115129 addr : conf .Addr ,
116130 url : conf .URL ,
@@ -123,6 +137,8 @@ func NewGateway(conf Config) (*Gateway, error) {
123137 hub : conf .Hub ,
124138 ipfs : conf .IPFSClient ,
125139 emailSessionBus : conf .EmailSessionBus ,
140+ maxEventsPerSec : conf .MaxEventsPerSec ,
141+ maxBurstSize : conf .MaxBurstSize ,
126142 }, nil
127143}
128144
@@ -140,6 +156,7 @@ func (g *Gateway) Start() {
140156 }
141157 router .SetHTMLTemplate (temp )
142158
159+ router .Use (throttle (g .maxEventsPerSec , g .maxBurstSize ))
143160 router .Use (location .Default ())
144161 router .Use (static .Serve ("" , & fileSystem {Assets }))
145162 router .Use (serveBucket (& bucketFS {
@@ -499,3 +516,17 @@ func (g *Gateway) toSubdomainURL(r *http.Request) (redirURL string, ok bool) {
499516 host := urlparts [1 ]
500517 return safeRedirectURL (fmt .Sprintf ("%s://%s.%s.%s/%s%s" , scheme , rootID , ns , host , rest , query ))
501518}
519+
520+ func throttle (maxEventsPerSec int , maxBurstSize int ) gin.HandlerFunc {
521+ limiter := rate .NewLimiter (rate .Limit (maxEventsPerSec ), maxBurstSize )
522+
523+ return func (context * gin.Context ) {
524+ if limiter .Allow () {
525+ context .Next ()
526+ return
527+ }
528+
529+ context .Error (errors .New ("limit exceeded" ))
530+ context .AbortWithStatus (http .StatusTooManyRequests )
531+ }
532+ }
0 commit comments