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  }