mirror of
https://github.com/btcsuite/btcd.git
synced 2025-01-19 05:33:36 +01:00
Add --rpcmaxclients option with default of 10.
This commit adds a new configuration option, --rpcmaxclients, to limit the number of max standard RPC clients that are served concurrently. Note that this value does not apply to websocket connections. A future commit will add support for limiting those separately. Closes #68.
This commit is contained in:
parent
66e93f5163
commit
a293212581
21
config.go
21
config.go
@ -33,6 +33,7 @@ const (
|
||||
defaultBtcnet = btcwire.MainNet
|
||||
defaultMaxPeers = 125
|
||||
defaultBanDuration = time.Hour * 24
|
||||
defaultMaxRPCClients = 10
|
||||
defaultVerifyEnabled = false
|
||||
defaultDbType = "leveldb"
|
||||
)
|
||||
@ -71,6 +72,7 @@ type config struct {
|
||||
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 8334, testnet: 18334)"`
|
||||
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
|
||||
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
||||
RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"`
|
||||
DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified"`
|
||||
DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"`
|
||||
ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"`
|
||||
@ -278,15 +280,16 @@ func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *fl
|
||||
func loadConfig() (*config, []string, error) {
|
||||
// Default config.
|
||||
cfg := config{
|
||||
DebugLevel: defaultLogLevel,
|
||||
MaxPeers: defaultMaxPeers,
|
||||
BanDuration: defaultBanDuration,
|
||||
ConfigFile: defaultConfigFile,
|
||||
DataDir: defaultDataDir,
|
||||
LogDir: defaultLogDir,
|
||||
DbType: defaultDbType,
|
||||
RPCKey: defaultRPCKeyFile,
|
||||
RPCCert: defaultRPCCertFile,
|
||||
ConfigFile: defaultConfigFile,
|
||||
DebugLevel: defaultLogLevel,
|
||||
MaxPeers: defaultMaxPeers,
|
||||
BanDuration: defaultBanDuration,
|
||||
RPCMaxClients: defaultMaxRPCClients,
|
||||
DataDir: defaultDataDir,
|
||||
LogDir: defaultLogDir,
|
||||
DbType: defaultDbType,
|
||||
RPCKey: defaultRPCKeyFile,
|
||||
RPCCert: defaultRPCCertFile,
|
||||
}
|
||||
|
||||
// Service options which are only added on Windows.
|
||||
|
1
doc.go
1
doc.go
@ -41,6 +41,7 @@ Application Options:
|
||||
(default port: 8334, testnet: 18334)
|
||||
--rpccert= File containing the certificate file
|
||||
--rpckey= File containing the certificate key
|
||||
--rpcmaxclients= Max number of RPC clients for standard connections (10)
|
||||
--norpc Disable built-in RPC server -- NOTE: The RPC server is
|
||||
disabled by default if no rpcuser/rpcpass is specified
|
||||
--nodnsseed Disable DNS seeding for peers
|
||||
|
73
rpcserver.go
73
rpcserver.go
@ -140,14 +140,16 @@ var rpcUnimplemented = map[string]bool{
|
||||
// rpcServer holds the items the rpc server may need to access (config,
|
||||
// shutdown, main server, etc.)
|
||||
type rpcServer struct {
|
||||
started int32
|
||||
shutdown int32
|
||||
server *server
|
||||
authsha [sha256.Size]byte
|
||||
ws *wsContext
|
||||
wg sync.WaitGroup
|
||||
listeners []net.Listener
|
||||
quit chan int
|
||||
started int32
|
||||
shutdown int32
|
||||
server *server
|
||||
authsha [sha256.Size]byte
|
||||
ws *wsContext
|
||||
numClients int
|
||||
numClientsMutex sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
listeners []net.Listener
|
||||
quit chan int
|
||||
}
|
||||
|
||||
// Start is used by server.go to start the rpc listener.
|
||||
@ -167,11 +169,22 @@ func (s *rpcServer) Start() {
|
||||
}
|
||||
rpcServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Connection", "close")
|
||||
r.Close = true
|
||||
|
||||
// Limit the number of connections to max allowed.
|
||||
if s.limitConnections(w, r.RemoteAddr) {
|
||||
return
|
||||
}
|
||||
|
||||
// Keep track of the number of connected clients.
|
||||
s.incrementClients()
|
||||
defer s.decrementClients()
|
||||
if _, err := s.checkAuth(r, true); err != nil {
|
||||
jsonAuthFail(w, r, s)
|
||||
return
|
||||
}
|
||||
jsonRPCRead(w, r, s)
|
||||
|
||||
})
|
||||
|
||||
rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
@ -199,6 +212,49 @@ func (s *rpcServer) Start() {
|
||||
}
|
||||
}
|
||||
|
||||
// limitConnections responds with a 503 service unavailable and returns true if
|
||||
// adding another client would exceed the maximum allow RPC clients.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *rpcServer) limitConnections(w http.ResponseWriter, remoteAddr string) bool {
|
||||
s.numClientsMutex.Lock()
|
||||
defer s.numClientsMutex.Unlock()
|
||||
|
||||
if s.numClients+1 > cfg.RPCMaxClients {
|
||||
rpcsLog.Infof("Max RPC clients exceeded [%d] - "+
|
||||
"disconnecting client %s", cfg.RPCMaxClients,
|
||||
remoteAddr)
|
||||
http.Error(w, "503 Too busy. Try again later.",
|
||||
http.StatusServiceUnavailable)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// incrementClients adds one to the number of connected RPC clients. Note
|
||||
// this only applies to standard clients. Websocket clients have their own
|
||||
// limits and are tracked separately.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *rpcServer) incrementClients() {
|
||||
s.numClientsMutex.Lock()
|
||||
defer s.numClientsMutex.Unlock()
|
||||
|
||||
s.numClients++
|
||||
}
|
||||
|
||||
// decrementClients subtracts one from the number of connected RPC clients.
|
||||
// Note this only applies to standard clients. Websocket clients have their own
|
||||
// limits and are tracked separately.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *rpcServer) decrementClients() {
|
||||
s.numClientsMutex.Lock()
|
||||
defer s.numClientsMutex.Unlock()
|
||||
|
||||
s.numClients--
|
||||
}
|
||||
|
||||
// checkAuth checks the HTTP Basic authentication supplied by a wallet
|
||||
// or RPC client in the HTTP request r. If the supplied authentication
|
||||
// does not match the username and password expected, a non-nil error is
|
||||
@ -344,7 +400,6 @@ func jsonAuthFail(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
||||
// jsonRPCRead is the RPC wrapper around the jsonRead function to handle reading
|
||||
// and responding to RPC messages.
|
||||
func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
||||
r.Close = true
|
||||
if atomic.LoadInt32(&s.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
@ -143,6 +143,9 @@
|
||||
; rpclisten=0.0.0.0:8337 ; all ipv4 interfaces on non-standard port 8337
|
||||
; rpclisten=[::]:8337 ; all ipv6 interfaces on non-standard port 8337
|
||||
|
||||
; Specify the maximum number of concurrent RPC clients for standard connections.
|
||||
; rpcmaxclients=10
|
||||
|
||||
; Use the following setting to disable the RPC server even if the rpcuser and
|
||||
; rpcpass are specified above. This allows one to quickly disable the RPC
|
||||
; server without having to remove credentials from the config file.
|
||||
|
Loading…
Reference in New Issue
Block a user