@@ -15,8 +15,8 @@ import (
1515 "sync"
1616 "time"
1717
18- "github.com/didip/tollbooth/v7 "
19- "github.com/didip/tollbooth_chi "
18+ "github.com/didip/tollbooth/v8 "
19+ "github.com/didip/tollbooth/v8/limiter "
2020 "github.com/go-chi/chi/v5"
2121 "github.com/go-chi/chi/v5/middleware"
2222 "github.com/go-chi/cors"
@@ -234,14 +234,14 @@ func (s *Rest) routes() chi.Router {
234234
235235 router .Group (func (r chi.Router ) {
236236 r .Use (middleware .Timeout (5 * time .Second ))
237- r .Use (logInfoWithBody , tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 2 , nil ) ), middleware .NoCache )
237+ r .Use (logInfoWithBody , rateLimiter ( 2 ), middleware .NoCache )
238238 r .Use (validEmailAuth ()) // reject suspicious email logins
239239 r .Mount ("/auth" , authHandler )
240240 })
241241
242242 router .Group (func (r chi.Router ) {
243243 r .Use (middleware .Timeout (5 * time .Second ))
244- r .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 100 , nil ) ))
244+ r .Use (rateLimiter ( 100 ))
245245 r .Mount ("/avatar" , avatarHandler )
246246 })
247247
@@ -251,14 +251,14 @@ func (s *Rest) routes() chi.Router {
251251 router .Route ("/api/v1" , func (rapi chi.Router ) {
252252 rapi .Group (func (rava chi.Router ) {
253253 rava .Use (middleware .Timeout (5 * time .Second ))
254- rava .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 100 , nil ) ))
254+ rava .Use (rateLimiter ( 100 ))
255255 rava .Mount ("/avatar" , avatarHandler )
256256 })
257257
258258 // open routes
259259 rapi .Group (func (ropen chi.Router ) {
260260 ropen .Use (middleware .Timeout (30 * time .Second ))
261- ropen .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( s .openRouteLimiter , nil ) ))
261+ ropen .Use (rateLimiter ( s .openRouteLimiter ))
262262 ropen .Use (authMiddleware .Trace , middleware .NoCache , logInfoWithBody )
263263 ropen .Get ("/config" , s .configCtrl )
264264 ropen .Get ("/find" , s .pubRest .findCommentsCtrl )
@@ -281,7 +281,7 @@ func (s *Rest) routes() chi.Router {
281281 // open routes, cached
282282 rapi .Group (func (ropen chi.Router ) {
283283 ropen .Use (middleware .Timeout (30 * time .Second ))
284- ropen .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 10 , nil ) ))
284+ ropen .Use (rateLimiter ( 10 ))
285285 ropen .Use (authMiddleware .Trace , logInfoWithBody )
286286 ropen .Get ("/picture/{user}/{id}" , s .pubRest .loadPictureCtrl )
287287 ropen .Get ("/qr/telegram" , s .pubRest .telegramQrCtrl )
@@ -290,7 +290,7 @@ func (s *Rest) routes() chi.Router {
290290 // protected routes, require auth
291291 rapi .Group (func (rauth chi.Router ) {
292292 rauth .Use (middleware .Timeout (30 * time .Second ))
293- rauth .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 10 , nil ) ))
293+ rauth .Use (rateLimiter ( 10 ))
294294 rauth .Use (authMiddleware .Auth , matchSiteID , middleware .NoCache , logInfoWithBody )
295295 rauth .Get ("/user" , s .privRest .userInfoCtrl )
296296 rauth .Get ("/userdata" , s .privRest .userAllDataCtrl )
@@ -299,7 +299,7 @@ func (s *Rest) routes() chi.Router {
299299 // admin routes, require auth and admin users only
300300 rapi .Route ("/admin" , func (radmin chi.Router ) {
301301 radmin .Use (middleware .Timeout (30 * time .Second ))
302- radmin .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 10 , nil ) ))
302+ radmin .Use (rateLimiter ( 10 ))
303303 radmin .Use (authMiddleware .Auth , authMiddleware .AdminOnly , matchSiteID )
304304 radmin .Use (middleware .NoCache , logInfoWithBody )
305305
@@ -325,7 +325,7 @@ func (s *Rest) routes() chi.Router {
325325 // protected routes, throttled to 10/s by default, controlled by external UpdateLimiter param
326326 rapi .Group (func (rauth chi.Router ) {
327327 rauth .Use (middleware .Timeout (10 * time .Second ))
328- rauth .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( s .updateLimiter (), nil )))
328+ rauth .Use (rateLimiter ( s .updateLimiter ()))
329329 rauth .Use (authMiddleware .Auth , matchSiteID , subscribersOnly (s .SubscribersOnly ))
330330 rauth .Use (middleware .NoCache , logInfoWithBody )
331331
@@ -345,7 +345,7 @@ func (s *Rest) routes() chi.Router {
345345 // protected routes, anonymous rejected
346346 rapi .Group (func (rauth chi.Router ) {
347347 rauth .Use (middleware .Timeout (10 * time .Second ))
348- rauth .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( s .updateLimiter (), nil )))
348+ rauth .Use (rateLimiter ( s .updateLimiter ()))
349349 rauth .Use (authMiddleware .Auth , rejectAnonUser , matchSiteID )
350350 rauth .Use (logger .New (logger .Log (log .Default ()), logger .Prefix ("[DEBUG]" ), logger .IPfn (ipFn )).Handler )
351351 rauth .Post ("/picture" , s .privRest .savePictureCtrl )
@@ -355,7 +355,7 @@ func (s *Rest) routes() chi.Router {
355355 // open routes on root level
356356 router .Group (func (rroot chi.Router ) {
357357 rroot .Use (middleware .Timeout (10 * time .Second ))
358- rroot .Use (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 50 , nil ) ))
358+ rroot .Use (rateLimiter ( 50 ))
359359 rroot .Get ("/robots.txt" , s .pubRest .robotsCtrl )
360360 rroot .Get ("/email/unsubscribe.html" , s .privRest .emailUnsubscribeCtrl )
361361 rroot .Post ("/email/unsubscribe.html" , s .privRest .emailUnsubscribeCtrl )
@@ -491,7 +491,7 @@ func addFileServer(r chi.Router, embedFS embed.FS, webRoot, version string) {
491491 webFS = http .StripPrefix ("/web" , webFS )
492492 r .Get ("/web" , http .RedirectHandler ("/web/" , http .StatusMovedPermanently ).ServeHTTP )
493493
494- r .With (tollbooth_chi . LimitHandler ( tollbooth . NewLimiter ( 20 , nil ) ),
494+ r .With (rateLimiter ( 20 ),
495495 middleware .Timeout (10 * time .Second ),
496496 cacheControl (time .Hour , version ),
497497 ).Get ("/web/*" , func (w http.ResponseWriter , r * http.Request ) {
@@ -728,3 +728,16 @@ func parseError(err error, defaultCode int) (code int) {
728728
729729 return code
730730}
731+
732+ // rateLimiter creates a rate limiting middleware with proper IP lookup configuration.
733+ // tollbooth v8 requires explicit IP lookup method to be set.
734+ // uses RemoteAddr which is set by chi's middleware.RealIP to the real client IP
735+ // from X-Forwarded-For, X-Real-IP, or True-Client-IP headers.
736+ func rateLimiter (maxReq float64 ) func (http.Handler ) http.Handler {
737+ lmt := tollbooth .NewLimiter (maxReq , nil )
738+ lmt .SetIPLookup (limiter.IPLookup {
739+ Name : "RemoteAddr" ,
740+ IndexFromRight : 0 ,
741+ })
742+ return tollbooth .HTTPMiddleware (lmt )
743+ }
0 commit comments