Add global_max_connections option.

pull/2386/head
Roger A. Light 4 years ago
parent 02685d49b6
commit f552ec48b1

@ -58,6 +58,8 @@ Broker:
- Add `mosquitto_client_port()` function for plugins. - Add `mosquitto_client_port()` function for plugins.
- Add `global_max_clients` option to allow limiting client sessions globally - Add `global_max_clients` option to allow limiting client sessions globally
on the broker. on the broker.
- Add `global_max_connections` option to allow limiting client onnections globally
on the broker.
Client library: Client library:
- Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair - Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair

@ -416,7 +416,7 @@
</para> </para>
<para> <para>
See also the max_connections setting, which applies to See also the <option>max_connections</option> setting, which applies to
listeners. If you set <option>global_max_clients</option> listeners. If you set <option>global_max_clients</option>
to 1000 and <option>max_connections</option> on a to 1000 and <option>max_connections</option> on a
listener to 10, then that means only 10 simultaneous listener to 10, then that means only 10 simultaneous
@ -429,6 +429,24 @@
<para>Reloaded on reload signal.</para> <para>Reloaded on reload signal.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>global_max_connections</option> <replaceable>count</replaceable></term>
<listitem>
<para>
The maximum number of currently connected clients to
allow across the whole broker.
</para>
<para>
See also the <option>global_max_clients</option> and
<option>max_connections</option> settings.
</para>
<para>This option applies globally.</para>
<para>Defaults to -1 (unlimited)</para>
<para>Reloaded on reload signal.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>global_plugin</option> <replaceable>file path</replaceable></term> <term><option>global_plugin</option> <replaceable>file path</replaceable></term>
<listitem> <listitem>

@ -65,13 +65,19 @@
# disconnected, or has disconnected and still hasn't exceeded its session # disconnected, or has disconnected and still hasn't exceeded its session
# expiry interval (MQTT v5). # expiry interval (MQTT v5).
# #
# See also the max_connections setting, which applies to listeners. If you set # See also the global_max_connections setting, and the max_connections setting,
# global_max_clients to 1000 and max_connections on a listener to 10, then that # which applies to listeners. If you set global_max_clients to 1000 and
# means only 10 simultaneous connections will be allowed at once, with an # max_connections on a listener to 10, then that means only 10 simultaneous
# overall maximum of 1000 client sessions. # connections will be allowed at once, with an overall maximum of 1000 client
# sessions.
# #
#global_max_clients -1 #global_max_clients -1
# The maximum number of currently connected clients to allow across the whole
# broker.
# See also the global_max_clients and max_connections settings.
#global_max_connections -1
# QoS 1 and 2 messages will be allowed inflight per client until this limit # QoS 1 and 2 messages will be allowed inflight per client until this limit
# is exceeded. Defaults to 0. (No maximum) # is exceeded. Defaults to 0. (No maximum)
# See also max_inflight_messages # See also max_inflight_messages

@ -193,6 +193,7 @@ static void config__init_reload(struct mosquitto__config *config)
mosquitto__free(config->log_timestamp_format); mosquitto__free(config->log_timestamp_format);
config->log_timestamp_format = NULL; config->log_timestamp_format = NULL;
config->global_max_clients = -1; config->global_max_clients = -1;
config->global_max_connections = -1;
config->max_keepalive = 65535; config->max_keepalive = 65535;
config->max_packet_size = 0; config->max_packet_size = 0;
config->max_inflight_messages = 20; config->max_inflight_messages = 20;
@ -1474,6 +1475,8 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload,
#endif #endif
}else if(!strcmp(token, "global_max_clients")){ }else if(!strcmp(token, "global_max_clients")){
if(conf__parse_int(&token, "global_max_clients", &config->global_max_clients, &saveptr)) return MOSQ_ERR_INVAL; if(conf__parse_int(&token, "global_max_clients", &config->global_max_clients, &saveptr)) return MOSQ_ERR_INVAL;
}else if(!strcmp(token, "global_max_connections")){
if(conf__parse_int(&token, "global_max_connections", &config->global_max_connections, &saveptr)) return MOSQ_ERR_INVAL;
}else if(!strcmp(token, "http_dir")){ }else if(!strcmp(token, "http_dir")){
#ifdef WITH_WEBSOCKETS #ifdef WITH_WEBSOCKETS
if(reload) continue; /* Listeners not valid for reloading. */ if(reload) continue; /* Listeners not valid for reloading. */

@ -274,6 +274,7 @@ struct mosquitto__config {
bool daemon; bool daemon;
bool enable_control_api; bool enable_control_api;
int global_max_clients; int global_max_clients;
int global_max_connections;
struct mosquitto__listener default_listener; struct mosquitto__listener default_listener;
struct mosquitto__listener *listeners; struct mosquitto__listener *listeners;
int listener_count; int listener_count;

@ -197,7 +197,9 @@ struct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock
} }
new_context->listener->client_count++; new_context->listener->client_count++;
if(new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections){ if((new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections)
|| (db.config->global_max_connections > 0 && HASH_CNT(hh_sock, db.contexts_by_sock) > (unsigned int)db.config->global_max_connections)){
if(db.config->connection_messages == true){ if(db.config->connection_messages == true){
log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address); log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address);
} }

@ -164,7 +164,9 @@ static int callback_mqtt(
u->mosq = NULL; u->mosq = NULL;
return -1; return -1;
} }
if(mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections){ if((mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections)
|| (db.config->global_max_connections > 0 && HASH_CNT(hh_sock, db.contexts_by_sock) > (unsigned int)db.config->global_max_connections)){
if(db.config->connection_messages == true){ if(db.config->connection_messages == true){
log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address); log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address);
} }

@ -0,0 +1,79 @@
#!/usr/bin/env python3
# Test whether global_max_connections works
from mosq_test_helper import *
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("global_max_connections 10\n")
def do_test():
rc = 1
connect_packets_ok = []
connack_packets_ok = []
for i in range(0, 10):
connect_packets_ok.append(mosq_test.gen_connect("max-conn-%d"%i, proto_ver=5))
connack_packets_ok.append(mosq_test.gen_connack(rc=0, proto_ver=5))
connect_packet_bad = mosq_test.gen_connect("max-conn-bad", proto_ver=5)
connack_packet_bad = b""
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
socks = []
try:
# Open all allowed connections, a limit of 10
for i in range(0, 10):
socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))
# Try to open an 11th connection
try:
sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)
except ConnectionResetError:
# Expected behaviour
pass
# Close all allowed connections
for i in range(0, 10):
socks[i].close()
## Now repeat - check it works as before
# Open all allowed connections, a limit of 10
for i in range(0, 10):
socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port))
# Try to open an 11th connection
try:
sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port)
except ConnectionResetError:
# Expected behaviour
pass
# Close all allowed connections
for i in range(0, 10):
socks[i].close()
rc = 0
except mosq_test.TestError:
pass
except Exception as err:
print(err)
finally:
os.remove(conf_file)
broker.terminate()
broker.wait()
(stdo, stde) = broker.communicate()
if rc:
print(stde.decode('utf-8'))
exit(rc)
do_test()
exit(0)

@ -27,6 +27,7 @@ msg_sequence_test:
./01-connect-allow-anonymous.py ./01-connect-allow-anonymous.py
./01-connect-disconnect-v5.py ./01-connect-disconnect-v5.py
./01-connect-global-max-clients.py ./01-connect-global-max-clients.py
./01-connect-global-max-connections.py
./01-connect-max-connections.py ./01-connect-max-connections.py
./01-connect-max-keepalive.py ./01-connect-max-keepalive.py
./01-connect-uname-no-password-denied.py ./01-connect-uname-no-password-denied.py

@ -9,6 +9,7 @@ tests = [
(1, './01-connect-allow-anonymous.py'), (1, './01-connect-allow-anonymous.py'),
(1, './01-connect-disconnect-v5.py'), (1, './01-connect-disconnect-v5.py'),
(1, './01-connect-global-max-clients.py'), (1, './01-connect-global-max-clients.py'),
(1, './01-connect-global-max-connections.py'),
(1, './01-connect-max-connections.py'), (1, './01-connect-max-connections.py'),
(1, './01-connect-max-keepalive.py'), (1, './01-connect-max-keepalive.py'),
(1, './01-connect-uname-no-password-denied.py'), (1, './01-connect-uname-no-password-denied.py'),

Loading…
Cancel
Save