The client sends the SETTINGS acknowledgement that RFC 9113 Section
6.5.3 requires. It went to mhd_h2_proc_new_settings(), a TODO that
fails for every SETTINGS frame after the first. The frame was never
consumed, so no later frame of the connection was processed: a curl
download stalled after the 10 MiB stream window that curl announces.

The acknowledgement has no payload, so it is now consumed, and the
count of unacknowledged SETTINGS frames goes down. A later SETTINGS
frame that is not an acknowledgement still goes to the TODO.

test_h2_raw now acknowledges the server's settings.
---
 src/mhd2/h2/h2_proc_in.c       |  5 +++++
 src/mhd2/h2/h2_proc_settings.c |  9 +++++++++
 src/mhd2/h2/h2_proc_settings.h |  8 ++++++++
 src/tests/raw/test_h2_raw.c    | 32 +++++++++++++++++++++++++++++++-
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/src/mhd2/h2/h2_proc_in.c b/src/mhd2/h2/h2_proc_in.c
index 9336049..602df14 100644
--- a/src/mhd2/h2/h2_proc_in.c
+++ b/src/mhd2/h2/h2_proc_in.c
@@ -111,6 +111,11 @@ process_inc_frame (struct MHD_Connection *restrict c,
                                                h2frame->rst_stream.stream_id,
                                                h2frame->rst_stream.error_code);
   case mhd_H2_FRAME_IDS_SETTINGS_ID:
+    if (h2frame->settings.ack)
+    {
+      mhd_h2_proc_settings_ack (c);
+      return true;
+    }
     return (mhd_H2_STNGS_PROC_OK == mhd_h2_proc_new_settings (c,
                                                               payload));
   case mhd_H2_FRAME_IDS_PUSH_PROMISE_ID:
diff --git a/src/mhd2/h2/h2_proc_settings.c b/src/mhd2/h2/h2_proc_settings.c
index ebcd976..9459a01 100644
--- a/src/mhd2/h2/h2_proc_settings.c
+++ b/src/mhd2/h2/h2_proc_settings.c
@@ -196,6 +196,15 @@ mhd_h2_proc_new_settings (struct MHD_Connection
*restrict c,
 }


+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_h2_proc_settings_ack (struct MHD_Connection *restrict c)
+{
+  /* An unsolicited acknowledgement is not an error */
+  if (0u != c->h2.state.sent_setns_noakc)
+    --c->h2.state.sent_setns_noakc;
+}
+
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
 mhd_h2_q_settings_first_fr (struct MHD_Connection *restrict c)
 {
diff --git a/src/mhd2/h2/h2_proc_settings.h b/src/mhd2/h2/h2_proc_settings.h
index 49aaceb..753f658 100644
--- a/src/mhd2/h2/h2_proc_settings.h
+++ b/src/mhd2/h2/h2_proc_settings.h
@@ -111,6 +111,14 @@ mhd_h2_proc_new_settings (struct MHD_Connection
*restrict c,
                           const struct mhd_Buffer *restrict stngs_payload)
 MHD_FN_PAR_NONNULL_ALL_;

+/**
+ * Process the acknowledgement of the settings sent to the peer
+ * @param c the connection to use
+ */
+MHD_INTERNAL void
+mhd_h2_proc_settings_ack (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+

 MHD_INTERNAL bool
 mhd_h2_q_settings_first_fr (struct MHD_Connection *restrict c)
diff --git a/src/tests/raw/test_h2_raw.c b/src/tests/raw/test_h2_raw.c
index 4ad0372..038582e 100644
--- a/src/tests/raw/test_h2_raw.c
+++ b/src/tests/raw/test_h2_raw.c
@@ -112,6 +112,11 @@
  */
 #define H2_FLAG_END_HEADERS 0x4

+/**
+ * The ACK flag of the SETTINGS frame, RFC 9113, Section 6.5.
+ */
+#define H2_FLAG_ACK 0x1
+
 /**
  * The SETTINGS_INITIAL_WINDOW_SIZE identifier, RFC 9113, Section 6.5.2.
  */
@@ -597,6 +602,27 @@ send_preface (int s,
 }


+/**
+ * Acknowledge the settings of the server, RFC 9113, Section 6.5.3.
+ *
+ * @param s the socket to send to
+ */
+static void
+ack_settings (int s)
+{
+  unsigned char buf[H2_FRAME_HDR_SIZE];
+
+  put_frame_header (buf,
+                    H2_FRAME_SETTINGS,
+                    H2_FLAG_ACK,
+                    0,
+                    0);
+  write_all (s,
+             buf,
+             sizeof (buf));
+}
+
+
 /**
  * A connection to our server and the data read from it.
  */
@@ -688,7 +714,8 @@ connect_to_server (struct Conn *c)

 /**
  * Read the next complete frame. Its payload follows the frame header
- * and stays in the buffer until the next call.
+ * and stays in the buffer until the next call. A SETTINGS frame without
+ * the ACK flag is acknowledged, RFC 9113, Section 6.5.3.
  *
  * @param c the connection to read from
  * @return the frame header, NULL if the server closed the connection
@@ -726,6 +753,9 @@ read_frame (struct Conn *c)
     c->have += (size_t)ret;
   }
   c->pos = H2_FRAME_HDR_SIZE + get_u24 (c->buf);
+  if ((H2_FRAME_SETTINGS == c->buf[3])
+      && (0 == (c->buf[4] & H2_FLAG_ACK)))
+    ack_settings (c->s);
   return c->buf;
 }

-- 
2.43.0

Reply via email to