http_static: POST to static files improvement 88/42588/2
authorMatus Fabian <matfabia@cisco.com>
Fri, 28 Mar 2025 16:04:50 +0000 (12:04 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Fri, 28 Mar 2025 20:28:15 +0000 (20:28 +0000)
Type: improvement

Change-Id: I6a6794dee3c9e948227e6c08769d45aa595bfe02
Signed-off-by: Matus Fabian <matfabia@cisco.com>
extras/scripts/host-stack/http-speed-test/speed_test_startup_conf [new file with mode: 0644]
extras/scripts/host-stack/http-speed-test/speedtest.cli [new file with mode: 0644]
src/plugins/http/http.c
src/plugins/http/http1.c
src/plugins/http/http_private.h
src/plugins/http_static/http_static.h
src/plugins/http_static/static_server.c

diff --git a/extras/scripts/host-stack/http-speed-test/speed_test_startup_conf b/extras/scripts/host-stack/http-speed-test/speed_test_startup_conf
new file mode 100644 (file)
index 0000000..ec92301
--- /dev/null
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2025 Cisco Systems, Inc.
+unix {
+  interactive log /tmp/vpp.log
+  full-coredump
+  exec /scratch/matfabia/vpp/extras/scripts/host-stack/http-speed-test/speedtest.cli
+  cli-listen /run/vpp/cli.sock
+  poll-sleep-usec 0
+}
+heapsize 4g
+api-trace { on }
+api-segment { global-size 2000M api-size 1G gid vpp }
+dpdk {
+  dev 0000:17:00.0 {
+    num-tx-desc 256
+    num-rx-desc 512
+    num-rx-queues 1
+    tso on
+  }
+  enable-tcp-udp-checksum
+}
+cpu {
+  skip-cores 0
+  main-core 1
+  corelist-workers 2-10
+}
+buffers { buffers-per-numa 16536 }
+session { event-queue-length 100000 use-app-socket-api }
+tcp { max-rx-fifo 128m tso }
+socksvr { socket-name /run/vpp-api.sock }
+
diff --git a/extras/scripts/host-stack/http-speed-test/speedtest.cli b/extras/scripts/host-stack/http-speed-test/speedtest.cli
new file mode 100644 (file)
index 0000000..555aa17
--- /dev/null
@@ -0,0 +1,5 @@
+comment {SPDX-License-Identifier: Apache-2.0}
+comment {Copyright (c) 2025 Cisco Systems, Inc.}
+set int ip address HundredGigabitEthernet17/0/0 6.0.1.1/24
+set int state HundredGigabitEthernet17/0/0 up
+http static server www-root /scratch/matfabia/Speed-Test max-body-size 40m max-age 0 keepalive-timeout 120 cache-size 100m fifo-size 512k
index 5998a9e..43b0993 100644 (file)
@@ -525,7 +525,7 @@ http_ts_disconnect_callback (session_t *ts)
 
   hc_handle.as_u32 = ts->opaque;
 
-  HTTP_DBG (1, "hc [%u]%x", ts->thread_index, hc_index);
+  HTTP_DBG (1, "hc [%u]%x", ts->thread_index, hc_handle.conn_index);
 
   hc = http_conn_get_w_thread (hc_handle.conn_index, ts->thread_index);
 
@@ -543,7 +543,7 @@ http_ts_reset_callback (session_t *ts)
 
   hc_handle.as_u32 = ts->opaque;
 
-  HTTP_DBG (1, "hc [%u]%x", ts->thread_index, hc_index);
+  HTTP_DBG (1, "hc [%u]%x", ts->thread_index, hc_handle.conn_index);
 
   hc = http_conn_get_w_thread (hc_handle.conn_index, ts->thread_index);
 
@@ -607,7 +607,8 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
   hc_handle.as_u32 = ts->opaque;
   hc = http_conn_get_w_thread (hc_handle.conn_index, ts->thread_index);
 
-  HTTP_DBG (1, "going to free hc [%u]%x", ts->thread_index, hc_index);
+  HTTP_DBG (1, "going to free hc [%u]%x", ts->thread_index,
+           hc_handle.conn_index);
 
   if (!(hc->flags & HTTP_CONN_F_PENDING_TIMER))
     http_conn_timer_stop (hc);
index ec118aa..258330e 100644 (file)
@@ -1859,10 +1859,12 @@ http1_transport_rx_callback (http_conn_t *hc)
 
   if (!http1_req_state_is_rx_valid (req))
     {
-      clib_warning ("hc [%u]%x invalid rx state: http req state "
-                   "'%U', session state '%U'",
-                   hc->c_thread_index, hc->hc_hc_index, format_http_req_state,
-                   req->state, format_http_conn_state, hc);
+      if (http_io_ts_max_read (hc))
+       clib_warning ("hc [%u]%x invalid rx state: http req state "
+                     "'%U', session state '%U'",
+                     hc->c_thread_index, hc->hc_hc_index,
+                     format_http_req_state, req->state,
+                     format_http_conn_state, hc);
       http_io_ts_drain_all (hc);
       return;
     }
index ebec59a..8354bf2 100644 (file)
@@ -691,7 +691,7 @@ http_conn_accept_request (http_conn_t *hc, http_req_t *req)
   int rv;
 
   HTTP_DBG (1, "hc [%u]%x req %x", hc->hc_hc_index, hc->c_thread_index,
-           req->hr_req_index);
+           req->hr_req_handle);
 
   /* allocate app session and initialize */
   as = session_alloc (hc->c_thread_index);
index e158a32..2b6de03 100644 (file)
@@ -33,7 +33,7 @@
 
 /** \brief Application session
  */
-typedef struct
+typedef struct hss_session_
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
   u32 session_index;
@@ -42,6 +42,9 @@ typedef struct
   /** vpp session index, handle */
   u32 vpp_session_index;
   session_handle_t vpp_session_handle;
+  u8 *target_path;
+  u8 *target_query;
+  http_req_method_t rt;
   /** Fully-resolved file path */
   u8 *path;
   /** Data to send */
@@ -58,6 +61,9 @@ typedef struct
   http_headers_ctx_t resp_headers;
   /** Response header buffer */
   u8 *headers_buf;
+  /** POST body left to receive */
+  u64 left_recv;
+  int (*read_body_handler) (struct hss_session_ *hs, session_t *ts);
 } hss_session_t;
 
 typedef struct hss_session_handle_
index d7958fd..75481f8 100644 (file)
@@ -30,6 +30,8 @@
 #define HSS_HEADER_BUF_MAX_SIZE 16192
 hss_main_t hss_main;
 
+static int file_handler_discard_body (hss_session_t *hs, session_t *ts);
+
 static int
 hss_add_header (hss_session_t *hs, http_header_name_t name, const char *value,
                uword value_len)
@@ -280,14 +282,18 @@ content_type_from_request (u8 *request)
 }
 
 static int
-try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
-                u8 *target_path, u8 *target_query, u8 *data)
+try_url_handler (hss_session_t *hs)
 {
+  hss_main_t *hsm = &hss_main;
   http_status_code_t sc = HTTP_STATUS_OK;
   hss_url_handler_args_t args = {};
   uword *p, *url_table;
+  session_t *ts;
+  u8 *data = 0, *target_path;
   int rv;
 
+  target_path = hs->target_path;
+
   if (!hsm->enable_url_handlers || !target_path)
     return -1;
 
@@ -299,28 +305,48 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
 
   /* Look for built-in GET / POST handlers */
   url_table =
-    (rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers;
+    (hs->rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers;
 
   p = hash_get_mem (url_table, target_path);
   if (!p)
     return -1;
 
+  /* Read request body */
+  if (hs->left_recv)
+    {
+      ts = session_get (hs->vpp_session_index, hs->thread_index);
+      /* TODO: add support for large content (use hs->read_body_handler) */
+      if (svm_fifo_max_dequeue (ts->rx_fifo) < hs->left_recv)
+       {
+         hs->left_recv = 0;
+         start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
+         hss_session_disconnect_transport (hs);
+         return 0;
+       }
+      vec_validate (data, hs->left_recv - 1);
+      rv = svm_fifo_dequeue (ts->rx_fifo, hs->left_recv, data);
+      ASSERT (rv == hs->left_recv);
+      hs->left_recv = 0;
+    }
+
   hs->path = 0;
   hs->data_offset = 0;
   hs->cache_pool_index = ~0;
 
   if (hsm->debug_level > 0)
-    clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST",
+    clib_warning ("%s '%s'", (hs->rt == HTTP_REQ_GET) ? "GET" : "POST",
                  target_path);
 
-  args.req_type = rt;
-  args.query = target_query;
+  args.req_type = hs->rt;
+  args.query = hs->target_query;
   args.req_data = data;
   args.sh.thread_index = hs->thread_index;
   args.sh.session_index = hs->session_index;
 
   rv = ((hss_url_handler_fn) p[0]) (&args);
 
+  vec_free (data);
+
   /* Wait for data from handler */
   if (rv == HSS_URL_HANDLER_ASYNC)
     return 0;
@@ -328,7 +354,7 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
   if (rv == HSS_URL_HANDLER_ERROR)
     {
       clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0],
-                   (rt == HTTP_REQ_GET) ? "GET" : "POST", target_path);
+                   (hs->rt == HTTP_REQ_GET) ? "GET" : "POST", target_path);
       sc = HTTP_STATUS_BAD_GATEWAY;
     }
 
@@ -429,32 +455,49 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
 }
 
 static int
-try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
-                 u8 *target)
+try_file_handler (hss_session_t *hs)
 {
+  hss_main_t *hsm = &hss_main;
   http_status_code_t sc = HTTP_STATUS_OK;
   u8 *path, *sanitized_path;
-  u32 ce_index;
+  u32 ce_index, max_dequeue;
   http_content_type_t type;
   u8 *last_modified;
+  session_t *ts;
 
   /* Feature not enabled */
   if (!hsm->www_root)
     return -1;
 
+  /* Discard request body */
+  if (hs->left_recv)
+    {
+      ts = session_get (hs->vpp_session_index, hs->thread_index);
+      max_dequeue = svm_fifo_max_dequeue (ts->rx_fifo);
+      if (max_dequeue < hs->left_recv)
+       {
+         svm_fifo_dequeue_drop (ts->rx_fifo, max_dequeue);
+         hs->left_recv -= max_dequeue;
+         hs->read_body_handler = file_handler_discard_body;
+         return 0;
+       }
+      svm_fifo_dequeue_drop (ts->rx_fifo, hs->left_recv);
+      hs->left_recv = 0;
+    }
+
   /* Sanitize received path */
-  sanitized_path = http_path_sanitize (target);
+  sanitized_path = http_path_sanitize (hs->target_path);
 
   /*
    * Construct the file to open
    */
-  if (!target)
+  if (!sanitized_path)
     path = format (0, "%s%c", hsm->www_root, 0);
   else
     path = format (0, "%s/%s%c", hsm->www_root, sanitized_path, 0);
 
   if (hsm->debug_level > 0)
-    clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path);
+    clib_warning ("%s '%s'", (hs->rt == HTTP_REQ_GET) ? "GET" : "POST", path);
 
   if (hs->data && hs->free_data)
     vec_free (hs->data);
@@ -498,7 +541,7 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
    * Cache-Control max-age
    * Last-Modified
    */
-  type = content_type_from_request (target);
+  type = content_type_from_request (sanitized_path);
   if (hss_add_header (hs, HTTP_HEADER_CONTENT_TYPE,
                      http_content_type_token (type)) ||
       hss_add_header (hs, HTTP_HEADER_CACHE_CONTROL,
@@ -520,15 +563,12 @@ done:
 }
 
 static void
-handle_request (hss_session_t *hs, http_req_method_t rt, u8 *target_path,
-               u8 *target_query, u8 *data)
+handle_request (hss_session_t *hs)
 {
-  hss_main_t *hsm = &hss_main;
-
-  if (!try_url_handler (hsm, hs, rt, target_path, target_query, data))
+  if (!try_url_handler (hs))
     return;
 
-  if (!try_file_handler (hsm, hs, rt, target_path))
+  if (!try_file_handler (hs))
     return;
 
   /* Handler did not find anything return 404 */
@@ -536,20 +576,42 @@ handle_request (hss_session_t *hs, http_req_method_t rt, u8 *target_path,
   hss_session_disconnect_transport (hs);
 }
 
+static int
+file_handler_discard_body (hss_session_t *hs, session_t *ts)
+{
+  u32 max_dequeue, to_discard;
+
+  max_dequeue = svm_fifo_max_dequeue (ts->rx_fifo);
+  to_discard = clib_min (max_dequeue, hs->left_recv);
+  svm_fifo_dequeue_drop (ts->rx_fifo, to_discard);
+  hs->left_recv -= to_discard;
+  if (hs->left_recv == 0)
+    return try_file_handler (hs);
+  return 0;
+}
+
 static int
 hss_ts_rx_callback (session_t *ts)
 {
   hss_main_t *hsm = &hss_main;
   hss_session_t *hs;
-  u8 *target_path = 0, *target_query = 0, *data = 0;
   http_msg_t msg;
   int rv;
 
   hs = hss_session_get (ts->thread_index, ts->opaque);
+  if (hs->left_recv != 0)
+    {
+      ASSERT (hs->read_body_handler);
+      return hs->read_body_handler (hs, ts);
+    }
+
   if (hs->free_data)
     vec_free (hs->data);
+
   hs->data = 0;
   hs->data_len = 0;
+  vec_free (hs->target_path);
+  vec_free (hs->target_query);
   http_init_headers_ctx (&hs->resp_headers, hs->headers_buf,
                         vec_len (hs->headers_buf));
 
@@ -567,37 +629,38 @@ hss_ts_rx_callback (session_t *ts)
       goto err_done;
     }
 
+  hs->rt = msg.method_type;
+
   /* Read target path */
   if (msg.data.target_path_len)
     {
-      vec_validate (target_path, msg.data.target_path_len - 1);
+      vec_validate (hs->target_path, msg.data.target_path_len - 1);
       rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
-                         msg.data.target_path_len, target_path);
+                         msg.data.target_path_len, hs->target_path);
       ASSERT (rv == msg.data.target_path_len);
-      if (http_validate_abs_path_syntax (target_path, 0))
+      if (http_validate_abs_path_syntax (hs->target_path, 0))
        {
          start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
          goto err_done;
        }
       /* Target path must be a proper C-string in addition to a vector */
-      vec_add1 (target_path, 0);
+      vec_add1 (hs->target_path, 0);
     }
 
   /* Read target query */
   if (msg.data.target_query_len)
     {
-      vec_validate (target_query, msg.data.target_query_len - 1);
+      vec_validate (hs->target_query, msg.data.target_query_len - 1);
       rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset,
-                         msg.data.target_query_len, target_query);
+                         msg.data.target_query_len, hs->target_query);
       ASSERT (rv == msg.data.target_query_len);
-      if (http_validate_query_syntax (target_query, 0))
+      if (http_validate_query_syntax (hs->target_query, 0))
        {
          start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
          goto err_done;
        }
     }
 
-  /* Read request body for POST requests */
   if (msg.data.body_len && msg.method_type == HTTP_REQ_POST)
     {
       if (msg.data.body_len > hsm->max_body_size)
@@ -605,28 +668,19 @@ hss_ts_rx_callback (session_t *ts)
          start_send_data (hs, HTTP_STATUS_CONTENT_TOO_LARGE);
          goto err_done;
        }
-      if (svm_fifo_max_dequeue (ts->rx_fifo) - msg.data.body_offset <
-         msg.data.body_len)
-       {
-         start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
-         goto err_done;
-       }
-      vec_validate (data, msg.data.body_len - 1);
-      rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len,
-                         data);
-      ASSERT (rv == msg.data.body_len);
+
+      hs->left_recv = msg.data.body_len;
+      /* drop everything up to body */
+      svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
     }
 
   /* Find and send data */
-  handle_request (hs, msg.method_type, target_path, target_query, data);
+  handle_request (hs);
   goto done;
 
 err_done:
   hss_session_disconnect_transport (hs);
 done:
-  vec_free (target_path);
-  vec_free (target_query);
-  vec_free (data);
   svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
   return 0;
 }
@@ -757,6 +811,8 @@ hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf)
   hs->free_data = 0;
   vec_free (hs->headers_buf);
   vec_free (hs->path);
+  vec_free (hs->target_path);
+  vec_free (hs->target_query);
 
   hss_session_free (hs);
 }
@@ -952,8 +1008,9 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
       else if (unformat (line_input, "private-segment-size %U",
                         unformat_memory_size, &seg_size))
        hsm->private_segment_size = seg_size;
-      else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
-       hsm->fifo_size <<= 10;
+      else if (unformat (line_input, "fifo-size %U", unformat_memory_size,
+                        &hsm->fifo_size))
+       ;
       else if (unformat (line_input, "cache-size %U", unformat_memory_size,
                         &hsm->cache_size))
        ;