http_static: url handler buffer large POST body 46/42846/2
authorMatus Fabian <[email protected]>
Wed, 23 Apr 2025 13:54:45 +0000 (09:54 -0400)
committerMatus Fabian <[email protected]>
Wed, 23 Apr 2025 15:36:28 +0000 (11:36 -0400)
Add support to handle larger POST body in url handler using tx_buff,
member of hss_session_t, with configurable limit.

Add hss_confirm_data_read which notify http transport when body data
is read and transport requested notification (e.g. used by h2 flow
control).

Remove old deprecated api (v2 and v3).

Type: improvement

Change-Id: I62640bb9cb851cb567b8176bf01f02c63257ecb9
Signed-off-by: Matus Fabian <[email protected]>
extras/hs-test/http_test.go
src/plugins/http_static/http_static.api
src/plugins/http_static/http_static.c
src/plugins/http_static/http_static.h
src/plugins/http_static/http_static_test.c
src/plugins/http_static/static_server.c
test/asf/test_http_static.py

index 5fec25a..b9fcd39 100644 (file)
@@ -28,7 +28,7 @@ func init() {
        RegisterNoTopoTests(HeaderServerTest, HttpPersistentConnectionTest, HttpPipeliningTest,
                HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, HttpAbsoluteFormUriTest,
                HttpCliBadRequestTest, HttpStaticBuildInUrlGetIfStatsTest, HttpStaticBuildInUrlPostIfStatsTest,
-               HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest,
+               HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest, HttpStaticPostTest,
                HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest,
                HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest,
                HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathSanitizationTest, HttpUriDecodeTest,
@@ -229,6 +229,27 @@ func HttpPipeliningTest(s *NoTopoSuite) {
        s.AssertMatchError(err, os.ErrDeadlineExceeded, "second request response received")
 }
 
+func HttpStaticPostTest(s *NoTopoSuite) {
+       // testing url handler app do not support multi-thread
+       s.SkipIfMultiWorker()
+       vpp := s.Containers.Vpp.VppInstance
+       serverAddress := s.VppAddr()
+       s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug max-body-size 1m"))
+       s.Log(vpp.Vppctl("test-url-handler enable"))
+
+       body := make([]byte, 131072)
+       _, err := rand.Read(body)
+       client := NewHttpClient(defaultHttpTimeout)
+       req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test3", bytes.NewBuffer(body))
+       s.AssertNil(err, fmt.Sprint(err))
+       resp, err := client.Do(req)
+       s.AssertNil(err, fmt.Sprint(err))
+       defer resp.Body.Close()
+       s.AssertHttpStatus(resp, 200)
+       _, err = io.ReadAll(resp.Body)
+       s.AssertNil(err, fmt.Sprint(err))
+}
+
 func HttpCliTest(s *VethsSuite) {
        s.Containers.ServerVpp.VppInstance.Vppctl("http cli server")
 
index bd0cebc..5c1eaf7 100644 (file)
@@ -3,41 +3,7 @@
     This file defines static http server control-plane API messages
 */
 
-option version = "2.4.0";
-
-/** \brief Configure and enable the static http server
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param fifo_size - size (in bytes) of the session FIFOs
-    @param cache_size_limit - size (in bytes) of the in-memory file data cache
-    @param max_age - how long a response is considered fresh (in seconds)
-    @param prealloc_fifos - number of preallocated fifos (usually 0)
-    @param private_segment_size - fifo segment size (usually 0)
-    @param www_root - html root path
-    @param uri - bind URI, defaults to "tcp://0.0.0.0/80"
-*/
-
-autoreply define http_static_enable_v2 {
-    option deprecated;
-
-    /* Client identifier, set from api_main.my_client_index */
-    u32 client_index;
-
-    /* Arbitrary context, so client can match reply to request */
-    u32 context;
-    /* Typical options */
-    u32 fifo_size;
-    u32 cache_size_limit;
-    u32 max_age [default=600];
-    /* Unusual options */
-    u32 prealloc_fifos;
-    u32 private_segment_size;
-
-    /* Root of the html path */
-    string www_root[256];
-    /* The bind URI */
-    string uri[256];
-};
+option version = "2.5.0";
 
 /** \brief Configure and enable the static http server
     @param client_index - opaque cookie to identify the sender
@@ -45,6 +11,7 @@ autoreply define http_static_enable_v2 {
     @param fifo_size - size (in bytes) of the session FIFOs
     @param cache_size_limit - size (in bytes) of the in-memory file data cache
     @param max_age - how long a response is considered fresh (in seconds)
+    @param max_body_size - maximum size of a request body (in bytes)
     @param keepalive_timeout - timeout during which client connection will stay open (in seconds)
     @param prealloc_fifos - number of preallocated fifos (usually 0)
     @param private_segment_size - fifo segment size (usually 0)
@@ -52,7 +19,7 @@ autoreply define http_static_enable_v2 {
     @param uri - bind URI, defaults to "tcp://0.0.0.0/80"
 */
 
-autoreply define http_static_enable_v3 {
+autoreply define http_static_enable_v4 {
     option deprecated;
 
     /* Client identifier, set from api_main.my_client_index */
@@ -65,6 +32,7 @@ autoreply define http_static_enable_v3 {
     u32 cache_size_limit;
     u32 max_age [default=600];
     u32 keepalive_timeout [default=60];
+    u64 max_body_size [default=8000];
     /* Unusual options */
     u32 prealloc_fifos;
     u32 private_segment_size;
@@ -76,12 +44,14 @@ autoreply define http_static_enable_v3 {
 };
 
 /** \brief Configure and enable the static http server
+
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param fifo_size - size (in bytes) of the session FIFOs
     @param cache_size_limit - size (in bytes) of the in-memory file data cache
     @param max_age - how long a response is considered fresh (in seconds)
     @param max_body_size - maximum size of a request body (in bytes)
+    @param rx_buff_thresh - maximum size of a large memory allocation (in bytes)
     @param keepalive_timeout - timeout during which client connection will stay open (in seconds)
     @param prealloc_fifos - number of preallocated fifos (usually 0)
     @param private_segment_size - fifo segment size (usually 0)
@@ -89,7 +59,7 @@ autoreply define http_static_enable_v3 {
     @param uri - bind URI, defaults to "tcp://0.0.0.0/80"
 */
 
-autoreply define http_static_enable_v4 {
+autoreply define http_static_enable_v5 {
     /* Client identifier, set from api_main.my_client_index */
     u32 client_index;
 
@@ -100,7 +70,8 @@ autoreply define http_static_enable_v4 {
     u32 cache_size_limit;
     u32 max_age [default=600];
     u32 keepalive_timeout [default=60];
-    u64 max_body_size [default=8000];
+    u64 max_body_size [default=8192];
+    u32 rx_buff_thresh [default=1048576];
     /* Unusual options */
     u32 prealloc_fifos;
     u32 private_segment_size;
index 2574f65..85b044f 100644 (file)
@@ -67,7 +67,7 @@ hss_register_url_handler (hss_url_handler_fn fp, const char *url,
 static int
 hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
                u32 private_segment_size, u8 *www_root, u8 *uri, u32 max_age,
-               u32 keepalive_timeout, u64 max_body_size)
+               u32 keepalive_timeout, u64 max_body_size, u32 rx_buff_thresh)
 {
   hss_main_t *hsm = &hss_main;
   int rv;
@@ -81,6 +81,7 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
   hsm->default_listener.cache_size = cache_limit;
   hsm->default_listener.max_age = max_age;
   hsm->default_listener.max_body_size = max_body_size;
+  hsm->default_listener.rx_buff_thresh = rx_buff_thresh;
   hsm->default_listener.keepalive_timeout = keepalive_timeout;
   hsm->have_default_listener = 1;
 
@@ -109,49 +110,29 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
 
 /* API message handler */
 static void
-vl_api_http_static_enable_v2_t_handler (vl_api_http_static_enable_v2_t *mp)
-{
-  vl_api_http_static_enable_v2_reply_t *rmp;
-  hss_main_t *hsm = &hss_main;
-  int rv;
-
-  mp->uri[ARRAY_LEN (mp->uri) - 1] = 0;
-  mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0;
-
-  rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
-                      ntohl (mp->prealloc_fifos),
-                      ntohl (mp->private_segment_size), mp->www_root, mp->uri,
-                      ntohl (mp->max_age), HSS_DEFAULT_KEEPALIVE_TIMEOUT,
-                      HSS_DEFAULT_MAX_BODY_SIZE);
-
-  REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V2_REPLY);
-}
-
-/* API message handler */
-static void
-vl_api_http_static_enable_v3_t_handler (vl_api_http_static_enable_v3_t *mp)
+vl_api_http_static_enable_v4_t_handler (vl_api_http_static_enable_v4_t *mp)
 {
-  vl_api_http_static_enable_v3_reply_t *rmp;
+  vl_api_http_static_enable_v4_reply_t *rmp;
   hss_main_t *hsm = &hss_main;
   int rv;
 
   mp->uri[ARRAY_LEN (mp->uri) - 1] = 0;
   mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0;
 
-  rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
-                      ntohl (mp->prealloc_fifos),
-                      ntohl (mp->private_segment_size), mp->www_root, mp->uri,
-                      ntohl (mp->max_age), ntohl (mp->keepalive_timeout),
-                      HSS_DEFAULT_MAX_BODY_SIZE);
+  rv = hss_enable_api (
+    ntohl (mp->fifo_size), ntohl (mp->cache_size_limit),
+    ntohl (mp->prealloc_fifos), ntohl (mp->private_segment_size), mp->www_root,
+    mp->uri, ntohl (mp->max_age), ntohl (mp->keepalive_timeout),
+    ntohl (mp->max_body_size), HSS_DEFAULT_RX_BUFFER_THRESH);
 
-  REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V3_REPLY);
+  REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V4_REPLY);
 }
 
 /* API message handler */
 static void
-vl_api_http_static_enable_v4_t_handler (vl_api_http_static_enable_v4_t *mp)
+vl_api_http_static_enable_v5_t_handler (vl_api_http_static_enable_v5_t *mp)
 {
-  vl_api_http_static_enable_v4_reply_t *rmp;
+  vl_api_http_static_enable_v5_reply_t *rmp;
   hss_main_t *hsm = &hss_main;
   int rv;
 
@@ -162,9 +143,9 @@ vl_api_http_static_enable_v4_t_handler (vl_api_http_static_enable_v4_t *mp)
                       ntohl (mp->prealloc_fifos),
                       ntohl (mp->private_segment_size), mp->www_root, mp->uri,
                       ntohl (mp->max_age), ntohl (mp->keepalive_timeout),
-                      ntohl (mp->max_body_size));
+                      ntohl (mp->max_body_size), ntohl (mp->rx_buff_thresh));
 
-  REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V4_REPLY);
+  REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V5_REPLY);
 }
 
 #include <http_static/http_static.api.c>
index 25ee195..678fb63 100644 (file)
@@ -25,6 +25,7 @@
 
 #define HSS_DEFAULT_MAX_AGE 600
 #define HSS_DEFAULT_MAX_BODY_SIZE     8192
+#define HSS_DEFAULT_RX_BUFFER_THRESH  1 << 20
 #define HSS_DEFAULT_KEEPALIVE_TIMEOUT 60
 
 /** @file http_static.h
@@ -63,6 +64,10 @@ typedef struct hss_session_
   http_headers_ctx_t resp_headers;
   /** Response header buffer */
   u8 *headers_buf;
+  /** RX buffer (POST body) */
+  u8 *rx_buff;
+  /** Current RX buffer offset */
+  u64 rx_buff_offset;
   /** POST body left to receive */
   u64 left_recv;
   /** threshold for switching to pointers */
@@ -137,6 +142,8 @@ typedef struct hss_listener_
   u64 cache_size;
   /** Maximum size of a request body (in bytes) **/
   u64 max_body_size;
+  /** Maximum size of a large memory allocation */
+  u32 rx_buff_thresh;
   /** Timeout during which client connection will stay open */
   u32 keepalive_timeout;
   /** How long a response is considered fresh (in seconds) */
index 5648789..aba7bc4 100644 (file)
@@ -39,100 +39,10 @@ http_static_test_main_t http_static_test_main;
 #include <vlibapi/vat_helper_macros.h>
 
 static int
-api_http_static_enable_v2 (vat_main_t *vam)
-{
-  unformat_input_t *line_input = vam->input;
-  vl_api_http_static_enable_v2_t *mp;
-  u64 tmp;
-  u8 *www_root = 0;
-  u8 *uri = 0;
-  u32 prealloc_fifos = 0;
-  u32 private_segment_size = 0;
-  u32 fifo_size = 8 << 10;
-  u32 cache_size_limit = 1 << 20;
-  u32 max_age = HSS_DEFAULT_MAX_AGE;
-  int ret;
-
-  /* Parse args required to build the message */
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (line_input, "www-root %s", &www_root))
-       ;
-      else if (unformat (line_input, "prealloc-fifos %d", &prealloc_fifos))
-       ;
-      else if (unformat (line_input, "private-segment-size %U",
-                        unformat_memory_size, &tmp))
-       {
-         if (tmp >= 0x100000000ULL)
-           {
-             errmsg ("private segment size %llu, too large", tmp);
-             return -99;
-           }
-         private_segment_size = (u32) tmp;
-       }
-      else if (unformat (line_input, "fifo-size %U", unformat_memory_size,
-                        &tmp))
-       {
-         if (tmp >= 0x100000000ULL)
-           {
-             errmsg ("fifo-size %llu, too large", tmp);
-             return -99;
-           }
-         fifo_size = (u32) tmp;
-       }
-      else if (unformat (line_input, "cache-size %U", unformat_memory_size,
-                        &tmp))
-       {
-         if (tmp < (128ULL << 10))
-           {
-             errmsg ("cache-size must be at least 128kb");
-             return -99;
-           }
-         cache_size_limit = (u32) tmp;
-       }
-      else if (unformat (line_input, "max-age %d", &max_age))
-       ;
-      else if (unformat (line_input, "uri %s", &uri))
-       ;
-      else
-       {
-         errmsg ("unknown input `%U'", format_unformat_error, line_input);
-         return -99;
-       }
-    }
-
-  if (www_root == 0)
-    {
-      errmsg ("Must specify www-root");
-      return -99;
-    }
-
-  if (uri == 0)
-    uri = format (0, "tcp://0.0.0.0/80%c", 0);
-
-  /* Construct the API message */
-  M (HTTP_STATIC_ENABLE_V2, mp);
-  strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256);
-  strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256);
-  mp->fifo_size = ntohl (fifo_size);
-  mp->cache_size_limit = ntohl (cache_size_limit);
-  mp->prealloc_fifos = ntohl (prealloc_fifos);
-  mp->private_segment_size = ntohl (private_segment_size);
-  mp->max_age = ntohl (max_age);
-
-  /* send it... */
-  S (mp);
-
-  /* Wait for a reply... */
-  W (ret);
-  return ret;
-}
-
-static int
-api_http_static_enable_v3 (vat_main_t *vam)
+api_http_static_enable_v4 (vat_main_t *vam)
 {
   unformat_input_t *line_input = vam->input;
-  vl_api_http_static_enable_v3_t *mp;
+  vl_api_http_static_enable_v4_t *mp;
   u64 tmp;
   u8 *www_root = 0;
   u8 *uri = 0;
@@ -142,6 +52,7 @@ api_http_static_enable_v3 (vat_main_t *vam)
   u32 cache_size_limit = 1 << 20;
   u32 max_age = HSS_DEFAULT_MAX_AGE;
   u32 keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
+  u64 max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
   int ret;
 
   /* Parse args required to build the message */
@@ -188,6 +99,8 @@ api_http_static_enable_v3 (vat_main_t *vam)
        ;
       else if (unformat (line_input, "uri %s", &uri))
        ;
+      else if (unformat (line_input, "max-body-size %llu", &max_body_size))
+       ;
       else
        {
          errmsg ("unknown input `%U'", format_unformat_error, line_input);
@@ -205,7 +118,7 @@ api_http_static_enable_v3 (vat_main_t *vam)
     uri = format (0, "tcp://0.0.0.0/80%c", 0);
 
   /* Construct the API message */
-  M (HTTP_STATIC_ENABLE_V3, mp);
+  M (HTTP_STATIC_ENABLE_V4, mp);
   strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256);
   strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256);
   mp->fifo_size = ntohl (fifo_size);
@@ -214,6 +127,8 @@ api_http_static_enable_v3 (vat_main_t *vam)
   mp->private_segment_size = ntohl (private_segment_size);
   mp->max_age = ntohl (max_age);
   mp->keepalive_timeout = ntohl (keepalive_timeout);
+  mp->max_body_size = ntohl (max_body_size);
+
   /* send it... */
   S (mp);
 
@@ -223,10 +138,10 @@ api_http_static_enable_v3 (vat_main_t *vam)
 }
 
 static int
-api_http_static_enable_v4 (vat_main_t *vam)
+api_http_static_enable_v5 (vat_main_t *vam)
 {
   unformat_input_t *line_input = vam->input;
-  vl_api_http_static_enable_v4_t *mp;
+  vl_api_http_static_enable_v5_t *mp;
   u64 tmp;
   u8 *www_root = 0;
   u8 *uri = 0;
@@ -237,6 +152,7 @@ api_http_static_enable_v4 (vat_main_t *vam)
   u32 max_age = HSS_DEFAULT_MAX_AGE;
   u32 keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
   u64 max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+  u32 rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
   int ret;
 
   /* Parse args required to build the message */
@@ -283,7 +199,11 @@ api_http_static_enable_v4 (vat_main_t *vam)
        ;
       else if (unformat (line_input, "uri %s", &uri))
        ;
-      else if (unformat (line_input, "max-body-size %llu", &max_body_size))
+      else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
+                        &max_body_size))
+       ;
+      else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+                        &rx_buff_thresh))
        ;
       else
        {
index 9914bec..9c5dfba 100644 (file)
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include <http/http_content_types.h>
+#include <http/http_status_codes.h>
 
 /** @file static_server.c
  *  Static http server, sufficient to serve .html / .css / .js content.
@@ -32,7 +33,7 @@
 hss_main_t hss_main;
 
 static int file_handler_discard_body (hss_session_t *hs, session_t *ts);
-static int url_handler_wait_body (hss_session_t *hs, session_t *ts);
+static int url_handler_read_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,
@@ -66,6 +67,19 @@ hss_add_header (hss_session_t *hs, http_header_name_t name, const char *value,
   return 0;
 }
 
+static_always_inline void
+hss_confirm_data_read (hss_session_t *hs, u32 n_last_deq)
+{
+  session_t *ts;
+
+  ts = session_get (hs->vpp_session_index, hs->thread_index);
+  if (svm_fifo_needs_deq_ntf (ts->rx_fifo, n_last_deq))
+    {
+      svm_fifo_clear_deq_ntf (ts->rx_fifo);
+      session_program_transport_io_evt (ts->handle, SESSION_IO_EVT_RX);
+    }
+}
+
 static hss_session_t *
 hss_session_alloc (u32 thread_index)
 {
@@ -121,6 +135,7 @@ hss_session_disconnect_transport (hss_session_t *hs)
 static void
 start_send_data (hss_session_t *hs, http_status_code_t status)
 {
+  hss_main_t *hsm = &hss_main;
   http_msg_t msg;
   session_t *ts;
   u32 n_enq;
@@ -129,6 +144,9 @@ start_send_data (hss_session_t *hs, http_status_code_t status)
 
   ts = session_get (hs->vpp_session_index, hs->thread_index);
 
+  if (hsm->debug_level > 0)
+    clib_warning ("status code: %U", format_http_status_code, status);
+
   msg.type = HTTP_MSG_REPLY;
   msg.code = status;
   msg.data.body_len = hs->data_len;
@@ -291,7 +309,8 @@ try_url_handler (hss_session_t *hs)
   hss_url_handler_args_t args = {};
   uword *p, *url_table;
   session_t *ts;
-  u8 *data = 0, *target_path;
+  u32 max_deq;
+  u8 *target_path;
   int rv;
 
   target_path = hs->target_path;
@@ -313,18 +332,41 @@ try_url_handler (hss_session_t *hs)
   if (!p)
     return -1;
 
+  hs->rx_buff = 0;
+
   /* Read request body */
   if (hs->left_recv)
     {
+      hss_listener_t *l = hss_listener_get (hs->listener_index);
+      if (hs->left_recv > l->rx_buff_thresh)
+       {
+         /* TODO: large body (not buffered in memory) */
+         clib_warning ("data length %u above threshold %u", hs->left_recv,
+                       l->rx_buff_thresh);
+         hs->left_recv = 0;
+         start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
+         hss_session_disconnect_transport (hs);
+       }
+      hs->rx_buff_offset = 0;
+      vec_validate (hs->rx_buff, hs->left_recv - 1);
       ts = session_get (hs->vpp_session_index, hs->thread_index);
-      if (svm_fifo_max_dequeue (ts->rx_fifo) < hs->left_recv)
+      max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+      if (max_deq < hs->left_recv)
        {
-         hs->read_body_handler = url_handler_wait_body;
+         hs->read_body_handler = url_handler_read_body;
+         if (max_deq == 0)
+           return 0;
+         rv = svm_fifo_dequeue (ts->rx_fifo, max_deq, hs->rx_buff);
+         ASSERT (rv == max_deq);
+         hs->rx_buff_offset = max_deq;
+         hs->left_recv -= max_deq;
+         hss_confirm_data_read (hs, max_deq);
          return 0;
        }
-      vec_validate (data, hs->left_recv - 1);
-      rv = svm_fifo_dequeue (ts->rx_fifo, hs->left_recv, data);
+      rv = svm_fifo_dequeue (ts->rx_fifo, hs->left_recv,
+                            hs->rx_buff + hs->rx_buff_offset);
       ASSERT (rv == hs->left_recv);
+      hss_confirm_data_read (hs, hs->left_recv);
       hs->left_recv = 0;
     }
 
@@ -338,13 +380,13 @@ try_url_handler (hss_session_t *hs)
 
   args.req_type = hs->rt;
   args.query = hs->target_query;
-  args.req_data = data;
+  args.req_data = hs->rx_buff;
   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);
+  vec_free (hs->rx_buff);
 
   /* Wait for data from handler */
   if (rv == HSS_URL_HANDLER_ASYNC)
@@ -482,9 +524,11 @@ try_file_handler (hss_session_t *hs)
          svm_fifo_dequeue_drop (ts->rx_fifo, max_dequeue);
          hs->left_recv -= max_dequeue;
          hs->read_body_handler = file_handler_discard_body;
+         hss_confirm_data_read (hs, max_dequeue);
          return 0;
        }
       svm_fifo_dequeue_drop (ts->rx_fifo, hs->left_recv);
+      hss_confirm_data_read (hs, hs->left_recv);
       hs->left_recv = 0;
     }
 
@@ -599,26 +643,29 @@ file_handler_discard_body (hss_session_t *hs, session_t *ts)
   to_discard = clib_min (max_dequeue, hs->left_recv);
   svm_fifo_dequeue_drop (ts->rx_fifo, to_discard);
   hs->left_recv -= to_discard;
+  hss_confirm_data_read (hs, to_discard);
   if (hs->left_recv == 0)
     return try_file_handler (hs);
   return 0;
 }
 
 static int
-url_handler_wait_body (hss_session_t *hs, session_t *ts)
+url_handler_read_body (hss_session_t *hs, session_t *ts)
 {
-  /* TODO: add support for large content (buffer or stream data) */
-  if (svm_fifo_max_dequeue (ts->rx_fifo) < hs->left_recv)
-    {
-      clib_warning ("not all data in fifo, max deq %u, left recv %u",
-                   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;
-    }
-  hs->left_recv = 0;
-  return try_url_handler (hs);
+  u32 max_dequeue, to_read;
+  int rv;
+
+  max_dequeue = svm_fifo_max_dequeue (ts->rx_fifo);
+  to_read = clib_min (max_dequeue, hs->left_recv);
+  rv =
+    svm_fifo_dequeue (ts->rx_fifo, to_read, hs->rx_buff + hs->rx_buff_offset);
+  ASSERT (rv == to_read);
+  hs->rx_buff_offset += to_read;
+  hs->left_recv -= to_read;
+  hss_confirm_data_read (hs, to_read);
+  if (hs->left_recv == 0)
+    return try_url_handler (hs);
+  return 0;
 }
 
 static int
@@ -1082,6 +1129,7 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
   l->cache_size = 10 << 20;
   l->max_age = HSS_DEFAULT_MAX_AGE;
   l->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+  l->rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
   l->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
 
   /* Get a line of input. */
@@ -1119,6 +1167,9 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
       else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
                         &l->max_body_size))
        ;
+      else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+                        &l->rx_buff_thresh))
+       ;
       else if (unformat (line_input, "keepalive-timeout %d",
                         &l->keepalive_timeout))
        ;
@@ -1214,6 +1265,12 @@ hss_add_del_listener_command_fn (vlib_main_t *vm, unformat_input_t *input,
   if (!unformat_user (input, unformat_line_input, line_input))
     return clib_error_return (0, "No input provided");
 
+  l->cache_size = 10 << 20;
+  l->max_age = HSS_DEFAULT_MAX_AGE;
+  l->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+  l->rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
+  l->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
+
   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (line_input, "add"))
@@ -1240,6 +1297,9 @@ hss_add_del_listener_command_fn (vlib_main_t *vm, unformat_input_t *input,
       else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
                         &l->max_body_size))
        ;
+      else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+                        &l->rx_buff_thresh))
+       ;
       else
        {
          error = clib_error_return (0, "unknown input `%U'",
index 8d488cb..f643480 100644 (file)
@@ -64,7 +64,7 @@ class TestHttpStaticVapi(VppAsfTestCase):
         super(TestHttpStaticVapi, cls).tearDownClass()
 
     def test_http_static_vapi(self):
-        self.vapi.http_static_enable_v3(
+        self.vapi.http_static_enable_v5(
             www_root="/tmp",
             uri="tcp://0.0.0.0/80",
         )