MEDIUM: quic: trigger fast connection closing on process stopping

With previous commit, quic-conn are now handled as jobs to prevent the
termination of haproxy process. This ensures that QUIC connections are
closed when all data are acknowledged by the client and there is no more
active streams.

The quic-conn layer emits a CONNECTION_CLOSE once the MUX has been
released and all streams are acknowledged. Then, the timer is scheduled
to definitely free the connection after the idle timeout period. This
allows to treat late-arriving packets.

Adjust this procedure to deactivate this timer when process stopping is
in progress. In this case, quic-conn timer is set to expire immediately
to free the quic-conn instance as soon as possible. This allows to
quickly close haproxy process.

This should be backported up to 2.7.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index 79f806e..ebe3dfd 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -623,9 +623,9 @@
 #define QUIC_FL_CONN_FINALIZED                   (1U << 26) /* QUIC connection finalized (functional, ready to send/receive) */
 #define QUIC_FL_CONN_NOTIFY_CLOSE                (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */
 #define QUIC_FL_CONN_EXP_TIMER                   (1U << 28) /* timer has expired, quic-conn can be freed */
-#define QUIC_FL_CONN_CLOSING                     (1U << 29)
-#define QUIC_FL_CONN_DRAINING                    (1U << 30)
-#define QUIC_FL_CONN_IMMEDIATE_CLOSE             (1U << 31)
+#define QUIC_FL_CONN_CLOSING                     (1U << 29) /* closing state, entered on CONNECTION_CLOSE emission */
+#define QUIC_FL_CONN_DRAINING                    (1U << 30) /* draining state, entered on CONNECTION_CLOSE reception */
+#define QUIC_FL_CONN_IMMEDIATE_CLOSE             (1U << 31) /* A CONNECTION_CLOSE must be sent */
 
 struct quic_conn {
 	const struct quic_version *original_version;
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 8dd2b48..f40ae25 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -3032,8 +3032,8 @@
 				 * Rearm the idle timeout only one time when entering draining
 				 * state.
 				 */
-				qc_idle_timer_do_rearm(qc);
 				qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE;
+				qc_idle_timer_do_rearm(qc);
 				qc_notify_close(qc);
 			}
 			break;
@@ -5280,9 +5280,15 @@
 {
 	unsigned int expire;
 
-	expire = QUIC_MAX(3 * quic_pto(qc), qc->max_idle_timeout);
-	qc->idle_timer_task->expire = tick_add(now_ms, MS_TO_TICKS(expire));
-	task_queue(qc->idle_timer_task);
+	if (stopping && qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_DRAINING)) {
+		TRACE_STATE("executing idle timer immediately on stopping", QUIC_EV_CONN_IDLE_TIMER, qc);
+		task_wakeup(qc->idle_timer_task, TASK_WOKEN_MSG);
+	}
+	else {
+		expire = QUIC_MAX(3 * quic_pto(qc), qc->max_idle_timeout);
+		qc->idle_timer_task->expire = tick_add(now_ms, MS_TO_TICKS(expire));
+		task_queue(qc->idle_timer_task);
+	}
 }
 
 /* Rearm the idle timer for <qc> QUIC connection depending on <read> boolean
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index 9e9258f..2a4d01e 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -34,6 +34,10 @@
 		goto leave;
 	}
 
+	/* Schedule a CONNECTION_CLOSE emission. If process stopping is in
+	 * progress, quic-conn idle-timer will be scheduled immediately after
+	 * its emission to ensure an immediate connection closing.
+	 */
 	qc_check_close_on_released_mux(qc);
  leave:
 	TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);