http: add half-close support for h2 tcp tunnels 73/43673/5
authorMatus Fabian <[email protected]>
Mon, 8 Sep 2025 17:08:51 +0000 (13:08 -0400)
committerFlorin Coras <[email protected]>
Mon, 15 Sep 2025 20:44:10 +0000 (20:44 +0000)
Proxy app can now handle TCP connection close better.

Type: improvement

Change-Id: I8d0cbf0c8dbc97aac4b8d860e67c4a325f316213
Signed-off-by: Matus Fabian <[email protected]>
src/plugins/http/http.c
src/plugins/http/http1.c
src/plugins/http/http2/http2.c
src/plugins/http/http_private.h

index f7071bf..71baccf 100644 (file)
@@ -1171,9 +1171,8 @@ http_stop_listen (u32 listener_index)
 
   return 0;
 }
-
-static void
-http_transport_close (u32 rh, clib_thread_index_t thread_index)
+static_always_inline void
+http_app_close (u32 rh, clib_thread_index_t thread_index, u8 is_shutdown)
 {
   http_conn_t *hc;
   u32 hc_index;
@@ -1183,7 +1182,8 @@ http_transport_close (u32 rh, clib_thread_index_t thread_index)
 
   hc_index = http_vfts[hr_handle.version].hc_index_get_by_req_index (
     hr_handle.req_index, thread_index);
-  HTTP_DBG (1, "App disconnecting [%u]%x", thread_index, hc_index);
+  HTTP_DBG (1, "App disconnecting [%u]%x is_shutdown=%u", thread_index,
+           hc_index, is_shutdown);
 
   hc = http_conn_get_w_thread (hc_index, thread_index);
   if (hc->state == HTTP_CONN_STATE_CONNECTING)
@@ -1200,7 +1200,19 @@ http_transport_close (u32 rh, clib_thread_index_t thread_index)
     }
 
   http_vfts[hc->version].app_close_callback (hc, hr_handle.req_index,
-                                            thread_index);
+                                            thread_index, is_shutdown);
+}
+
+static void
+http_transport_shutdown (u32 rh, clib_thread_index_t thread_index)
+{
+  http_app_close (rh, thread_index, 1);
+}
+
+static void
+http_transport_close (u32 rh, clib_thread_index_t thread_index)
+{
+  http_app_close (rh, thread_index, 0);
 }
 
 static void
@@ -1272,7 +1284,7 @@ http_app_tx_callback (void *session, transport_send_params_t *sp)
 
   if (hc->state == HTTP_CONN_STATE_APP_CLOSED)
     http_vfts[hc->version].app_close_callback (hc, hr_handle.req_index,
-                                              as->thread_index);
+                                              as->thread_index, 0);
 
   sent = max_burst_sz - sp->max_burst_size;
 
@@ -1420,6 +1432,7 @@ static const transport_proto_vft_t http_proto = {
   .connect = http_transport_connect,
   .start_listen = http_start_listen,
   .stop_listen = http_stop_listen,
+  .half_close = http_transport_shutdown,
   .close = http_transport_close,
   .reset = http_transport_reset,
   .cleanup_ho = http_transport_cleanup_ho,
index 5547d30..19cc187 100644 (file)
@@ -1922,7 +1922,7 @@ http1_app_rx_evt_callback (http_conn_t *hc, u32 req_index,
 
 static void
 http1_app_close_callback (http_conn_t *hc, u32 req_index,
-                         clib_thread_index_t thread_index)
+                         clib_thread_index_t thread_index, u8 is_shutdown)
 {
   http_req_t *req;
 
index adc99b1..a98969b 100644 (file)
@@ -31,6 +31,7 @@ typedef enum http2_stream_state_
 
 #define foreach_http2_req_flags                                               \
   _ (APP_CLOSED, "app-closed")                                                \
+  _ (SHUTDOWN_TUNNEL, "shutdown-tunnel")                                      \
   _ (NEED_WINDOW_UPDATE, "need-window-update")                                \
   _ (IS_PARENT, "is-parent")
 
@@ -268,6 +269,8 @@ http2_conn_alloc_req (http_conn_t *hc, u8 is_parent)
   h2c->req_num++;
   if (is_parent)
     {
+      HTTP_DBG (1, "is parent");
+      ASSERT (h2c->parent_req_index == SESSION_INVALID_INDEX);
       req->flags |= HTTP2_REQ_F_IS_PARENT;
       h2c->parent_req_index = req_index;
     }
@@ -1717,7 +1720,8 @@ http2_req_state_tunnel_rx (http_conn_t *hc, http2_req_t *req,
   u32 max_enq;
 
   HTTP_DBG (1, "tunnel received data from peer %lu", req->payload_len);
-  if (req->flags & HTTP2_REQ_F_APP_CLOSED)
+  if (req->flags & HTTP2_REQ_F_APP_CLOSED &&
+      !(req->flags & HTTP2_REQ_F_SHUTDOWN_TUNNEL))
     {
       HTTP_DBG (1, "proxy app closed, going to reset stream");
       http2_stream_error (hc, req, HTTP2_ERROR_CONNECT_ERROR, sp);
@@ -2820,7 +2824,7 @@ http2_app_rx_evt_callback (http_conn_t *hc, u32 req_index,
 
 static void
 http2_app_close_callback (http_conn_t *hc, u32 req_index,
-                         clib_thread_index_t thread_index)
+                         clib_thread_index_t thread_index, u8 is_shutdown)
 {
   http2_req_t *req;
 
@@ -2839,9 +2843,10 @@ http2_app_close_callback (http_conn_t *hc, u32 req_index,
       req->stream_state == HTTP2_STREAM_STATE_IDLE ||
       hc->state == HTTP_CONN_STATE_CLOSED)
     {
+      u8 is_parent = req->flags & HTTP2_REQ_F_IS_PARENT;
       HTTP_DBG (1, "nothing more to send, confirm close");
       http2_stream_close (req, hc);
-      if (req->flags & HTTP2_REQ_F_IS_PARENT)
+      if (is_parent)
        {
          HTTP_DBG (1, "client app closed parent, closing connection");
          ASSERT (!(hc->flags & HTTP_CONN_F_IS_SERVER));
@@ -2850,6 +2855,7 @@ http2_app_close_callback (http_conn_t *hc, u32 req_index,
     }
   else if (req->base.is_tunnel)
     {
+      req->flags |= is_shutdown ? HTTP2_REQ_F_SHUTDOWN_TUNNEL : 0;
       switch (req->stream_state)
        {
        case HTTP2_STREAM_STATE_OPEN:
@@ -2860,7 +2866,7 @@ http2_app_close_callback (http_conn_t *hc, u32 req_index,
              HTTP_DBG (1, "wait for all data to be written to ts");
              return;
            }
-         if (req->our_window == 0)
+         if (req->our_window == 0 && !is_shutdown)
            {
              HTTP_DBG (1, "app has unread data, going to reset stream");
              http2_stream_error (hc, req, HTTP2_ERROR_CONNECT_ERROR, 0);
index 0ca599a..49d4196 100644 (file)
@@ -286,7 +286,8 @@ typedef struct http_engine_vft_
   void (*app_rx_evt_callback) (http_conn_t *hc, u32 req_index,
                               clib_thread_index_t thread_index);
   void (*app_close_callback) (http_conn_t *hc, u32 req_index,
-                             clib_thread_index_t thread_index);
+                             clib_thread_index_t thread_index,
+                             u8 is_shutdown);
   void (*app_reset_callback) (http_conn_t *hc, u32 req_index,
                              clib_thread_index_t thread_index);
   int (*transport_connected_callback) (http_conn_t *hc);