decred.org/dcrdex@v1.0.5/server/comms/middleware.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package comms 5 6 import ( 7 "fmt" 8 "net/http" 9 "sync/atomic" 10 11 "decred.org/dcrdex/dex" 12 ) 13 14 type contextKey int 15 16 // These are the keys for different types of values stored in a request context. 17 const ( 18 CtxThing contextKey = iota 19 ctxListener 20 ) 21 22 // LimitRate is rate-limiting middleware that checks whether a request can be 23 // fulfilled. This is intended for the /api HTTP endpoints. 24 func (s *Server) LimitRate(next http.Handler) http.Handler { 25 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 26 code, err := s.meterIP(dex.NewIPKey(r.RemoteAddr)) 27 if err != nil { 28 http.Error(w, err.Error(), code) 29 return 30 } 31 next.ServeHTTP(w, r) 32 }) 33 } 34 35 // meterIP applies the dataEnabled flag, the global HTTP rate limiter, and the 36 // more restrictive IP-based rate limiter. This is only intended for the data 37 // API. The other websocket route handlers have different limiters that are 38 // shared between connections from the same IP, but not globally. 39 func (s *Server) meterIP(ip dex.IPKey) (int, error) { 40 if atomic.LoadUint32(&s.dataEnabled) != 1 { 41 return http.StatusServiceUnavailable, fmt.Errorf("data API is disabled") 42 } 43 if !globalHTTPRateLimiter.Allow() { 44 return http.StatusTooManyRequests, fmt.Errorf("too many global requests") 45 } 46 ipLimiter := getIPLimiter(ip) 47 if !ipLimiter.Allow() { 48 return http.StatusTooManyRequests, fmt.Errorf("too many requests") 49 } 50 return 0, nil 51 }