Fix will messages being incorrectly delayed.

This occured if a client set session-expiry-interval=0 when using
will-delay-interval>0.

Closes #3505. Thanks to Julian Graf.
This commit is contained in:
Roger A. Light
2026-02-16 00:11:20 +00:00
committed by Roger Light
parent 3a0f46aa20
commit d0152406e3
8 changed files with 87 additions and 9 deletions

View File

@@ -7,6 +7,8 @@
- Fix password length not being passed to MOSQ_EVT_BASIC_AUTH events. - Fix password length not being passed to MOSQ_EVT_BASIC_AUTH events.
Closes #3490. Closes #3490.
- Fix incorrect maximum-packet-size restriction. Closes #3503. - Fix incorrect maximum-packet-size restriction. Closes #3503.
- Fix will messages being incorrectly delayed if a client set
session-expiry-interval=0 when using will-delay-interval>0. Closes #3505.
# Common lib: # Common lib:
- Fix potential crash if reading a file in restricted mode and the group id - Fix potential crash if reading a file in restricted mode and the group id

View File

@@ -216,7 +216,7 @@ void context__cleanup(struct mosquitto *context, bool force_free)
void context__send_will(struct mosquitto *ctxt) void context__send_will(struct mosquitto *ctxt)
{ {
if(ctxt->state != mosq_cs_disconnecting && ctxt->will){ if(ctxt->state != mosq_cs_disconnecting && ctxt->will){
if(ctxt->will_delay_interval > 0){ if(ctxt->session_expiry_interval > 0 && ctxt->will_delay_interval > 0){
will_delay__add(ctxt); will_delay__add(ctxt);
return; return;
} }

View File

@@ -14,12 +14,13 @@ def do_test(start_broker):
connect1_packet = mosq_test.gen_connect("will-delay-reconnect-test", proto_ver=5) connect1_packet = mosq_test.gen_connect("will-delay-reconnect-test", proto_ver=5)
connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3) props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)
connect2a_packet = mosq_test.gen_connect("will-delay-reconnect-helper", proto_ver=5, will_topic="will/delay/reconnect/test", will_payload=b"will delay", will_properties=props, clean_session=False) will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)
connect2a_packet = mosq_test.gen_connect("will-delay-reconnect-helper", proto_ver=5, will_topic="will/delay/reconnect/test", will_payload=b"will delay", will_properties=will_props, clean_session=False, properties=props)
connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
connect2b_packet = mosq_test.gen_connect("will-delay-reconnect-helper", proto_ver=5, clean_session=True) connect2b_packet = mosq_test.gen_connect("will-delay-reconnect-helper", proto_ver=5, clean_session=False)
connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2b_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5)
subscribe_packet = mosq_test.gen_subscribe(mid, "will/delay/reconnect/test", 0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/delay/reconnect/test", 0, proto_ver=5)
suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)

View File

@@ -27,6 +27,8 @@ def do_test(start_broker, clean_session):
connect2_packet_clear = mosq_test.gen_connect("will-delay-recovery-helper", proto_ver=5) connect2_packet_clear = mosq_test.gen_connect("will-delay-recovery-helper", proto_ver=5)
will_packet = mosq_test.gen_publish(topic="will/delay/recovery/test", payload="will delay", qos=0, proto_ver=5)
port = mosq_test.get_port() port = mosq_test.get_port()
if start_broker: if start_broker:
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)
@@ -43,8 +45,13 @@ def do_test(start_broker, clean_session):
time.sleep(3) time.sleep(3)
# The client2 has reconnected within the will delay interval, which has now # The client2 has reconnected within the will delay interval, which has now
# passed. We should not have received the will at this point. # passed.
mosq_test.do_ping(sock1) if clean_session:
# The old session has ended, so we should receive the will
mosq_test.expect_packet(sock1, "will", will_packet)
else:
# We should not have received the will at this point.
mosq_test.do_ping(sock1)
rc = 0 rc = 0
sock1.close() sock1.close()

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
# Test whether a client that connects with a will delay that is longer than
# their session expiry interval has their will published.
# MQTT 5
# https://github.com/eclipse/mosquitto/issues/1401
from mosq_test_helper import *
def do_test(start_broker):
rc = 1
mid = 1
connect1_packet = mosq_test.gen_connect("will-session-exp", proto_ver=5)
connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 60)
connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 0)
connect2_packet = mosq_test.gen_connect("will-session-exp-helper", proto_ver=5, properties=connect_props, will_topic="will/session-expiry/test", will_payload=b"will delay", will_qos=2, will_properties=will_props)
connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
subscribe_packet = mosq_test.gen_subscribe(mid, "will/session-expiry/test", 0, proto_ver=5)
suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)
publish_packet = mosq_test.gen_publish("will/session-expiry/test", qos=0, payload="will delay", proto_ver=5)
port = mosq_test.get_port()
if start_broker:
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)
try:
sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port, connack_error="connack1")
mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback")
sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port, connack_error="connack2")
time.sleep(1)
sock2.close()
# Will should be sent immediately due to session-expiry-interval=0. If not, the read will timeout
mosq_test.expect_packet(sock1, "publish", publish_packet)
rc = 0
sock1.close()
except mosq_test.TestError:
pass
finally:
if start_broker:
broker.terminate()
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
(stdo, stde) = broker.communicate()
if rc:
print(stde.decode('utf-8'))
exit(rc)
else:
return rc
def all_tests(start_broker=False):
return do_test(start_broker)
if __name__ == '__main__':
all_tests(True)

View File

@@ -12,8 +12,9 @@ def do_test(start_broker, clean_session):
connect1_packet = mosq_test.gen_connect("will-delay-test", proto_ver=5) connect1_packet = mosq_test.gen_connect("will-delay-test", proto_ver=5)
connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3) props = mqtt5_props.gen_uint32_prop(mqtt5_props.SESSION_EXPIRY_INTERVAL, 60)
connect2_packet = mosq_test.gen_connect("will-delay-helper", proto_ver=5, will_topic="will/delay/test", will_payload=b"will delay", will_qos=2, will_properties=props, clean_session=clean_session) will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.WILL_DELAY_INTERVAL, 3)
connect2_packet = mosq_test.gen_connect("will-delay-helper", proto_ver=5, properties=props, will_topic="will/delay/test", will_payload=b"will delay", will_qos=2, will_properties=will_props, clean_session=clean_session)
connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
subscribe_packet = mosq_test.gen_subscribe(mid, "will/delay/test", 0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/delay/test", 0, proto_ver=5)

View File

@@ -148,6 +148,7 @@ msg_sequence_test:
./07-will-delay-invalid-573191.py ./07-will-delay-invalid-573191.py
./07-will-delay-reconnect.py ./07-will-delay-reconnect.py
./07-will-delay-recover.py ./07-will-delay-recover.py
./07-will-delay-session-expiry-0.py
./07-will-delay-session-expiry.py ./07-will-delay-session-expiry.py
./07-will-delay-session-expiry2.py ./07-will-delay-session-expiry2.py
./07-will-delay.py ./07-will-delay.py

View File

@@ -125,6 +125,7 @@ tests = [
(1, './07-will-delay-invalid-573191.py'), (1, './07-will-delay-invalid-573191.py'),
(1, './07-will-delay-reconnect.py'), (1, './07-will-delay-reconnect.py'),
(1, './07-will-delay-recover.py'), (1, './07-will-delay-recover.py'),
(1, './07-will-delay-session-expiry-0.py'),
(1, './07-will-delay-session-expiry.py'), (1, './07-will-delay-session-expiry.py'),
(1, './07-will-delay-session-expiry2.py'), (1, './07-will-delay-session-expiry2.py'),
(1, './07-will-delay.py'), (1, './07-will-delay.py'),