瀏覽代碼

POST parser: implemented incremental processing when system provides data larger than "large buffer"

Evgeny Grin (Karlson2k) 1 年之前
父節點
當前提交
0a5775ca8a
共有 5 個文件被更改,包括 320 次插入99 次删除
  1. 80 0
      src/mhd2/daemon_funcs.c
  2. 16 0
      src/mhd2/daemon_funcs.h
  3. 7 2
      src/mhd2/mhd_post_parser.h
  4. 210 94
      src/mhd2/post_parser_funcs.c
  5. 7 3
      src/mhd2/stream_process_request.c

+ 80 - 0
src/mhd2/daemon_funcs.c

@@ -99,6 +99,33 @@ mhd_daemon_claim_lbuf (struct MHD_Daemon *d,
 }
 
 
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_MUST_CHECK_RESULT_ size_t
+mhd_daemon_claim_lbuf_up_to (struct MHD_Daemon *d,
+                             size_t requested_size)
+{
+  size_t ret;
+  struct MHD_Daemon *const masterd = mhd_daemon_get_master_daemon (d);
+  mhd_assert (0 != requested_size);
+  if (0 == masterd->req_cfg.large_buf.space_left)
+    return false; /* Shortcut for typical use without large buffer */
+
+  mhd_mutex_lock_chk (&(masterd->req_cfg.large_buf.lock));
+  if (masterd->req_cfg.large_buf.space_left >= requested_size)
+  {
+    ret = requested_size;
+    masterd->req_cfg.large_buf.space_left -= requested_size;
+  }
+  else
+  {
+    ret = masterd->req_cfg.large_buf.space_left;
+    masterd->req_cfg.large_buf.space_left = 0;
+  }
+  mhd_mutex_unlock_chk (&(masterd->req_cfg.large_buf.lock));
+  return ret;
+}
+
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
 mhd_daemon_reclaim_lbuf (struct MHD_Daemon *d,
                          size_t reclaimed_size)
@@ -165,6 +192,59 @@ mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
 }
 
 
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+MHD_FN_PAR_INOUT_ (3) size_t
+mhd_daemon_extend_lbuf_up_to (struct MHD_Daemon *restrict d,
+                              size_t desired_grow_size,
+                              struct mhd_Buffer *restrict buf)
+{
+  void *new_alloc;
+  size_t grow_size;
+  mhd_assert (NULL != buf->data || 0 == buf->size);
+  mhd_assert (0 != buf->size || NULL == buf->data);
+
+  grow_size = mhd_daemon_claim_lbuf_up_to (d, desired_grow_size);
+
+  new_alloc = NULL;
+  if (NULL == buf->data)
+  {
+    while ((0 != grow_size) &&
+           (NULL == (new_alloc = malloc (grow_size))))
+    {
+      size_t reduce = grow_size / 2;
+      if (sizeof(void *) >= (grow_size - reduce))
+        reduce = grow_size;
+      mhd_daemon_reclaim_lbuf (d,
+                               reduce);
+      grow_size -= reduce;
+    }
+  }
+  else
+  {
+    while ((0 != grow_size) &&
+           (NULL == (new_alloc = realloc (buf->data,
+                                          buf->size + grow_size))))
+    {
+      size_t reduce = grow_size / 2;
+      if (sizeof(void *) >= (grow_size - reduce))
+        reduce = grow_size;
+      mhd_daemon_reclaim_lbuf (d,
+                               reduce);
+      grow_size -= reduce;
+    }
+  }
+
+  if (NULL != new_alloc)
+  {
+    mhd_assert (0 != grow_size);
+    buf->data = new_alloc;
+    buf->size += grow_size;
+  }
+
+  return grow_size;
+}
+
+
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
 MHD_FN_PAR_INOUT_ (2) void
 mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,

+ 16 - 0
src/mhd2/daemon_funcs.h

@@ -123,6 +123,22 @@ mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
 MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
   MHD_FN_PAR_INOUT_ (3);
 
+/**
+ * Grow or allocate the large buffer up to specified grow size.
+ * If the requested grow size is not possible, grow up to max possible size.
+ * @param d the daemon to use
+ * @param desired_grow_size the desired size of grow
+ * @param[in,out] buf the buffer to grow, must be allocated previously or
+ *                    zero-initialised
+ * @return the resulting grow size
+ */
+MHD_INTERNAL size_t
+mhd_daemon_extend_lbuf_up_to (struct MHD_Daemon *restrict d,
+                              size_t desired_grow_size,
+                              struct mhd_Buffer *restrict buf)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_
+  MHD_FN_PAR_INOUT_ (3);
+
 
 /**
  * Free large buffer.

+ 7 - 2
src/mhd2/mhd_post_parser.h

@@ -242,7 +242,7 @@ enum MHD_FIXED_ENUM_ mhd_PostMPartState
    * Check for delimiter failed, continuing processing of the "value"
    * This is an intermediate state, should be processed and switched to the next
    * state immediately.
-   * Should not be used outside processing loop.
+   * Can be used outside processing loop if streaming partial value.
    */
   mhd_POST_MPART_ST_BACK_TO_VALUE
   ,
@@ -623,7 +623,12 @@ struct mhd_PostParserData
   size_t field_start;
 
   /**
-   * The offset in the value data.
+   * 'true' if current filed 'value' must be "streamed".
+   */
+  bool force_streamed;
+
+  /**
+   * The offset in the current value data.
    * Used when value is processed incrementally otherwise it is zero.
    */
   size_t value_off;

+ 210 - 94
src/mhd2/post_parser_funcs.c

@@ -446,6 +446,8 @@ mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
 }
 
 
+#if 0 /* Disable unused functions */
+
 /**
  * Allocate memory from "large shared buffer" for POST parsing
  * @param c the stream to use
@@ -501,6 +503,54 @@ grow_lbuf_fixed_size (struct MHD_Connection *restrict c,
 }
 
 
+#endif /* Disable unused functions */
+
+/**
+ * Grow or allocate the new memory from "large shared buffer" for POST parsing
+ * If the requested grow size is not possible, grow up to max possible size.
+ * @param c the stream to use
+ * @param desired_grow_size the desired size to grow
+ * @param[in,out] buf the buffer to grow
+ * @return the resulting grow size
+ */
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_INOUT_ (3) size_t
+extend_lbuf_up_to (struct MHD_Connection *restrict c,
+                   size_t desired_grow_size,
+                   struct mhd_Buffer *restrict buf)
+{
+  mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
+  mhd_assert (c->rq.u_proc.post.lbuf_limit >= buf->size);
+
+  if (buf->size + desired_grow_size > c->rq.u_proc.post.lbuf_limit)
+    desired_grow_size = c->rq.u_proc.post.lbuf_limit - buf->size;
+
+  return mhd_daemon_extend_lbuf_up_to (c->daemon,
+                                       desired_grow_size,
+                                       buf);
+}
+
+
+static MHD_FN_PAR_NONNULL_ALL_ bool
+report_low_lbuf_mem (struct MHD_Connection *restrict c)
+{
+  mhd_LOG_MSG (c->daemon, \
+               MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM, \
+               "Not enough large shared buffer memory to " \
+               "parse POST request.");
+  c->rq.u_proc.post.parse_result =
+    MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM;
+  if (c->state < MHD_CONNECTION_FULL_REQ_RECEIVED)
+  {
+    c->discard_request = true;
+    c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+
+    return true;
+  }
+  return false;
+}
+
+
 /**
  * Test whether current incomplete value must be provided to the "stream"
  * reader.
@@ -520,13 +570,16 @@ is_value_streaming_needed (struct MHD_Connection *restrict c,
   if (NULL == p_par->stream_reader)
   {
     mhd_assert (0 == p_data->value_off);
+    mhd_assert (! p_data->force_streamed);
     return false; /* No value streaming possible */
   }
 
-  if (0 != p_data->value_off)
-    return true; /* Part of the value has been already provided to "stream"
-                    reader, the rest of the value should be provided
-                    in the same way */
+  /* If part of the value has been already provided to "stream"
+     reader, the rest of the value should be provided
+     in the same way */
+  mhd_assert ((0 == p_data->value_off) || p_data->force_streamed);
+  if (p_data->force_streamed)
+    return true;
 
   return (p_par->max_nonstream_size < field_cur_size);
 }
@@ -721,6 +774,7 @@ process_complete_field_all (struct MHD_Connection *restrict c,
   mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
   mhd_assert ((0 != enc_start) || (0 == enc_len));
   mhd_assert ((0 != value_start) || (0 == value_len));
+  mhd_assert (0 == buf [name_start + name_len]);
 
   p_data->some_data_provided = true;
 
@@ -762,6 +816,7 @@ process_complete_field_all (struct MHD_Connection *restrict c,
     res = mhd_stream_process_upload_action (c, act, false);
     if (c->suspended)
       return true;
+    p_data->force_streamed = false;
     p_data->value_off = 0;
     if (*pdata_size > *pfield_next_pos)
     {
@@ -781,6 +836,10 @@ process_complete_field_all (struct MHD_Connection *restrict c,
     struct mhd_PositionAndLength encoding_i;
     struct mhd_PositionAndLength value_i;
 
+    mhd_assert (! p_data->force_streamed);
+    mhd_assert (0 == p_data->value_off);
+    mhd_assert ((0 == value_start) || (0 == buf [value_start + value_len]));
+
     name_i.pos = name_start;
     name_i.len = name_len;
     filename_i.pos = filename_start;
@@ -971,6 +1030,7 @@ process_partial_value_all (struct MHD_Connection *restrict c,
                               MHD_NO);
 
   p_data->some_data_provided = true;
+  p_data->force_streamed = true;
 
   res = mhd_stream_process_upload_action (c, act, false);
   if (c->suspended)
@@ -1745,9 +1805,9 @@ parse_post_mpart (struct MHD_Connection *restrict c,
       mhd_assert (0 != mf->f.name_len);
       mhd_assert (i > mf->f.name_idx);
       mf->f.value_idx = i;
-      mf->line_start = mhd_POST_INVALID_POS;
     /* Intentional fall-through */
     case mhd_POST_MPART_ST_BACK_TO_VALUE:
+      mf->line_start = mhd_POST_INVALID_POS;
       mf->delim_check_start = mhd_POST_INVALID_POS;
       mf->st = mhd_POST_MPART_ST_VALUE;
     /* Intentional fall-through */
@@ -2001,9 +2061,7 @@ parse_post_mpart (struct MHD_Connection *restrict c,
     if (mhd_POST_MPART_ST_VALUE != mf->st)
     {
       i = mf->delim_check_start; /* Reset position */
-      mf->delim_check_start = mhd_POST_INVALID_POS;
-      mf->line_start = mhd_POST_INVALID_POS;
-      mf->st = mhd_POST_MPART_ST_VALUE;
+      mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
     }
 
     proc_res =
@@ -2257,7 +2315,7 @@ mhd_stream_post_parse (struct MHD_Connection *restrict c,
                        char *restrict buf)
 {
   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
-  bool alloc_failed;
+  size_t lbuf_left;
 
   mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != p_data->enc);
   mhd_assert (c->rq.cntn.lbuf.size <= p_data->lbuf_limit);
@@ -2266,75 +2324,74 @@ mhd_stream_post_parse (struct MHD_Connection *restrict c,
       (mhd_POST_MPART_ST_EPILOGUE == p_data->e_d.m_form.st))
   {
     /* No need to process the data */
-    *pdata_size = 0; /* All data has been moved */
+    *pdata_size = 0; /* All data has been "processed" */
     return false; /* Continue normal processing */
   }
 
   // TODO: support process in the connection buffer
-  if (NULL == c->rq.cntn.lbuf.data)
-  {
-    alloc_failed = ! get_lbuf_fixed_size (c,
-                                          *pdata_size + 1,
-                                          &(c->rq.cntn.lbuf));
-  }
-  else
-  {
-    size_t space_left;
 
-    mhd_assert (c->rq.cntn.lbuf.size >= p_data->lbuf_used);
-    space_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
+  mhd_assert (c->rq.cntn.lbuf.size >= p_data->lbuf_used);
+  lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
 
-    if (*pdata_size > space_left)
-      alloc_failed = ! grow_lbuf_fixed_size (c,
-                                             *pdata_size - space_left + 1,
-                                             &(c->rq.cntn.lbuf));
-    else
-      alloc_failed = false;
+  if (*pdata_size + 1 > lbuf_left)
+    (void) extend_lbuf_up_to (c,
+                              *pdata_size + 1 - lbuf_left,
+                              &(c->rq.cntn.lbuf));
 
-  }
-  if (alloc_failed)
+  while ((0 != *pdata_size) &&
+         (c->rq.cntn.lbuf.size > p_data->lbuf_used))
   {
-    mhd_LOG_MSG (c->daemon, \
-                 MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM, \
-                 "Not enough large shared buffer memory to " \
-                 "parse POST request.");
-    p_data->parse_result =
-      MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM;
-    c->discard_request = true;
-    c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
-    return true;
-  }
-  memcpy (c->rq.cntn.lbuf.data + p_data->lbuf_used,
-          buf,
-          *pdata_size);
-  p_data->lbuf_used += *pdata_size;
-  *pdata_size = 0; /* All data has been moved */
-
-  switch (p_data->enc)
-  {
-  case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
-    return parse_post_urlenc (c,
-                              &(p_data->lbuf_used),
-                              c->rq.cntn.lbuf.data);
-  case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
-    return parse_post_mpart (c,
+    size_t data_size_before_parse;
+    size_t copy_size = *pdata_size;
+    lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
+    if (lbuf_left < copy_size)
+      copy_size = lbuf_left;
+
+    memcpy (c->rq.cntn.lbuf.data + p_data->lbuf_used,
+            buf,
+            copy_size);
+    p_data->lbuf_used += copy_size;
+    *pdata_size -= copy_size;
+
+    data_size_before_parse = p_data->lbuf_used;
+    switch (p_data->enc)
+    {
+    case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
+      if (parse_post_urlenc (c,
                              &(p_data->lbuf_used),
-                             c->rq.cntn.lbuf.data);
-  case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
-    return parse_post_text (c,
+                             c->rq.cntn.lbuf.data))
+        return true;
+      break;
+    case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
+      if (parse_post_mpart (c,
                             &(p_data->lbuf_used),
-                            c->rq.cntn.lbuf.data);
-  case MHD_HTTP_POST_ENCODING_OTHER:
-  default:
-    mhd_assert (0 && "Impossible value");
-    MHD_UNREACHABLE_;
-    p_data->parse_result =
-      MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
-    c->discard_request = true;
-    c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
-    break;
+                            c->rq.cntn.lbuf.data))
+        return true;
+      break;
+    case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
+      if (parse_post_text (c,
+                           &(p_data->lbuf_used),
+                           c->rq.cntn.lbuf.data))
+        return true;
+      break;
+    case MHD_HTTP_POST_ENCODING_OTHER:
+    default:
+      mhd_assert (0 && "Impossible value");
+      MHD_UNREACHABLE_;
+      p_data->parse_result =
+        MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
+      c->discard_request = true;
+      c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+      return true;
+    }
+    if (data_size_before_parse == p_data->lbuf_used)
+      break; /* Nothing consumed, not possible to add new data to the buffer now */
   }
-  return true;
+
+  if (0 != *pdata_size)
+    return report_low_lbuf_mem (c);
+
+  return false; /* Continue normal processing */
 }
 
 
@@ -2360,11 +2417,16 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
   size_t name_len;
   size_t value_start;
   size_t value_len;
+  bool need_more_space;
+  bool have_extra_space;
+  bool need_value_stream;
 
-  pos = p_data->next_parse_pos;  /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
-  mhd_assert (pos == p_data->next_parse_pos);
-  mhd_assert (pos < c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+  pos = p_data->next_parse_pos;  /* Points to the char AFTER the data */
+  mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   mhd_assert (*pdata_size >= pos);
+  have_extra_space = (pos < c->rq.cntn.lbuf.size);
+  need_more_space = false; /**< No space for zero termination */
+  need_value_stream = false; /**< Value cannot be zero-terminated and must be streamed */
   switch (uf->st)
   {
   case mhd_POST_UENC_ST_NOT_STARTED:
@@ -2382,9 +2444,16 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
                                                NULL);
     else
       name_len = pos - uf->name_idx;
-    buf[name_start + name_len] = 0; /* Zero-terminate the result, an extra byte is always available in the buffer */
-    value_start = 0;
-    value_len = 0;
+    mhd_assert (name_start + name_len <= pos);
+    if (! have_extra_space &&
+        (name_start + name_len == pos))
+      need_more_space = true;
+    else
+    {
+      buf[name_start + name_len] = 0; /* Zero-terminate the result, an extra byte is available */
+      value_start = 0;
+      value_len = 0;
+    }
     break;
   case mhd_POST_UENC_ST_EQ_FOUND:
     mhd_assert (pos == *pdata_size);
@@ -2392,7 +2461,10 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
     name_len = uf->name_len;
     value_start = pos;
     value_len = 0;
-    buf[value_start] = 0; /* Zero-terminate the result, an extra byte is always available */
+    if (! have_extra_space)
+      need_value_stream = true;
+    else
+      buf[value_start] = 0; /* Zero-terminate the result, an extra byte is available */
     break;
   case mhd_POST_UENC_ST_VALUE:
     mhd_assert (0 != uf->value_idx);
@@ -2426,7 +2498,11 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
       else
         value_len = pos - uf->value_idx;
     }
-    buf[value_start + value_len] = 0; /* Zero-terminate the result, an extra byte is always available in the buffer */
+    if (! have_extra_space &&
+        (value_start + value_len == pos))
+      need_value_stream = true;
+    else
+      buf[value_start + value_len] = 0; /* Zero-terminate the result, an extra byte is available in the buffer */
     break;
   case mhd_POST_UENC_ST_FULL_FIELD_FOUND:
     /* Full value was found, but the stream has been suspended by
@@ -2452,9 +2528,24 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
     return false;
   }
 
+  /* Have field data */
+
+  p_data->next_parse_pos = pos;
+
+  if (need_value_stream)
+  {
+    if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+      p_data->force_streamed = true;
+    else
+      need_more_space = true;
+  }
+
+  if (need_more_space)
+    return report_low_lbuf_mem (c);
+
   if (process_complete_field (c,
                               buf,
-                              &pos,
+                              &(p_data->next_parse_pos),
                               pdata_size,
                               p_data->field_start,
                               name_start,
@@ -2489,18 +2580,17 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
   size_t pos;
   bool not_terminated;
   bool add_field;
-  size_t field_pos;
-  size_t field_len;
+  size_t value_pos;
+  size_t value_len;
 
   pos = p_data->next_parse_pos;  /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
-  mhd_assert (pos == p_data->next_parse_pos);
-  mhd_assert (pos < c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+  mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   mhd_assert (*pdata_size >= pos);
 
   not_terminated = false;
   add_field = false;
-  field_pos = 0;
-  field_len = 0;
+  value_pos = 0;
+  value_len = 0;
 
   switch (mf->st)
   {
@@ -2532,8 +2622,8 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
     mhd_assert (0 != mf->f.value_idx);
     not_terminated = true;
     add_field = true;
-    field_pos = mf->f.value_idx;
-    field_len = pos - field_len;
+    value_pos = mf->f.value_idx;
+    value_len = pos - value_len;
     break;
   case mhd_POST_MPART_ST_DELIM_FOUND:
     mhd_assert (0 != mf->f.name_idx);
@@ -2543,8 +2633,8 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
     add_field = true;
     if (0 != mf->f.value_idx)
     {
-      field_pos = mf->f.value_idx;
-      field_len = pos -  mf->delim_check_start;
+      value_pos = mf->f.value_idx;
+      value_len = pos -  mf->delim_check_start;
     }
     break;
   case mhd_POST_MPART_ST_FULL_FIELD_FOUND:
@@ -2555,8 +2645,8 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
     add_field = true;
     if (0 != mf->f.value_idx)
     {
-      field_pos = mf->f.value_idx;
-      field_len = mf->f.value_len;
+      value_pos = mf->f.value_idx;
+      value_len = mf->f.value_len;
     }
     break;
   case mhd_POST_MPART_ST_EPILOGUE:
@@ -2587,9 +2677,18 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
 
   if (add_field)
   {
+    if (c->rq.cntn.lbuf.size > (value_pos + value_len))
+      buf [value_pos + value_len] = 0; /* Zero-terminate the value */
+    else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+      p_data->force_streamed = true;
+    else
+      return report_low_lbuf_mem (c);
+
+    p_data->next_parse_pos = pos;
+
     if (process_complete_field_all (c,
                                     buf,
-                                    &pos,
+                                    &p_data->next_parse_pos,
                                     pdata_size,
                                     p_data->field_start,
                                     mf->f.name_idx,
@@ -2600,8 +2699,8 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c,
                                     mf->f.cntn_type_len,
                                     mf->f.enc_idx,
                                     mf->f.enc_len,
-                                    field_pos,
-                                    field_len))
+                                    value_pos,
+                                    value_len))
       return true;
   }
 
@@ -2636,8 +2735,7 @@ check_post_leftovers_text (struct MHD_Connection *restrict c,
   size_t value_len;
 
   pos = p_data->next_parse_pos;  /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
-  mhd_assert (pos == p_data->next_parse_pos);
-  mhd_assert (pos < c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
+  mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   switch (tf->st)
   {
   case mhd_POST_TEXT_ST_NOT_STARTED:
@@ -2648,7 +2746,9 @@ check_post_leftovers_text (struct MHD_Connection *restrict c,
     mhd_assert (pos == *pdata_size);
     name_start = tf->name_idx;
     name_len = pos - name_start;
-    buf[pos] = 0; /* Zero-terminate the result, an extra byte is always available */
+    if (pos == c->rq.cntn.lbuf.size)
+      return report_low_lbuf_mem (c);
+    buf[pos] = 0; /* Zero-terminate the result, an extra byte is available */
     value_start = 0;
     value_len = 0;
     break;
@@ -2716,6 +2816,16 @@ check_post_leftovers_text (struct MHD_Connection *restrict c,
     p_data->parse_result = MHD_POST_PARSE_RES_OK_BAD_TERMINATION;
   }
 
+  if (0 != value_start)
+  {
+    if (c->rq.cntn.lbuf.size > (value_start + value_len))
+      buf [value_start + value_len] = 0; /* Zero-terminate the value */
+    else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
+      p_data->force_streamed = true;
+    else
+      return report_low_lbuf_mem (c);
+  }
+
   if (process_complete_field (c,
                               buf,
                               &pos,
@@ -2743,6 +2853,12 @@ static MHD_FN_PAR_NONNULL_ALL_ bool
 check_post_leftovers (struct MHD_Connection *restrict c)
 {
   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
+  // TODO: implement processing in the connection buffer
+  if (p_data->lbuf_used == c->rq.cntn.lbuf.size)
+    (void) extend_lbuf_up_to (c,
+                              1,
+                              &(c->rq.cntn.lbuf));
+
   switch (p_data->enc)
   {
   case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:

+ 7 - 3
src/mhd2/stream_process_request.c

@@ -3258,9 +3258,11 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
       state_updated = mhd_stream_post_parse (c,
                                              &cntn_data_ready,
                                              buffer_head);
-      mhd_assert (0 == cntn_data_ready); // TODO: support one chunk in-place processing?
+      // TODO: support one chunk in-place processing?
+      mhd_assert ((0 == cntn_data_ready) || \
+                  (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
+                  (MHD_CONNECTION_BODY_RECEIVING != c->state));
       c->rq.cntn.recv_size += size_provided;
-
     }
     else
 #endif /* HAVE_POST_PARSER */
@@ -3389,7 +3391,9 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c)
     state_updated = mhd_stream_post_parse (c,
                                            &size_provided,
                                            c->read_buffer);
-    mhd_assert (0 == size_provided);
+    mhd_assert ((0 == size_provided) || \
+                (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
+                (MHD_CONNECTION_BODY_RECEIVING != c->state));
     read_buf_reuse = true;
     c->rq.cntn.proc_size += cntn_data_ready;
     if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)