commit 9b52a435ef23eef4703e19a8e6c6c3b402f2a1b2
parent 1d164613b04e391f0ef93c66665e91da62b3a192
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Wed, 31 Dec 2025 16:28:59 +0100
Fixed buffer shortage processing
Diffstat:
3 files changed, 168 insertions(+), 160 deletions(-)
diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c
@@ -3770,8 +3770,10 @@ handle_req_chunk_size_line_no_space (struct MHD_Connection *c,
* stored in the memory pool (like some header).
* @param c the connection to process
* @param stage the receive stage where the exhaustion happens.
+ * @return 'true' if connection should NOT be closed,
+ * 'false' if connection is closing
*/
-static MHD_FN_PAR_NONNULL_ALL_ void
+static MHD_FN_PAR_NONNULL_ALL_ bool
handle_recv_no_space (struct MHD_Connection *c,
enum MHD_ProcRecvDataStage stage)
{
@@ -3809,7 +3811,7 @@ handle_recv_no_space (struct MHD_Connection *c,
"No space left in the read buffer when " \
"receiving the initial part of " \
"the request line.");
- return;
+ return false;
case MHD_PROC_RECV_URI:
case MHD_PROC_RECV_HTTPVER:
/* Some data has been received, but the request line is incomplete */
@@ -3823,7 +3825,7 @@ handle_recv_no_space (struct MHD_Connection *c,
mhd_RESPOND_WITH_ERROR_STATIC (c,
MHD_HTTP_STATUS_URI_TOO_LONG,
ERR_RSP_MSG_REQUEST_TOO_BIG);
- return;
+ return true;
}
mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
"No space left in the read buffer when " \
@@ -3831,17 +3833,17 @@ handle_recv_no_space (struct MHD_Connection *c,
"the request line. " \
"The request uses non-standard HTTP request " \
"method token.");
- return;
+ return false;
case MHD_PROC_RECV_HEADERS:
handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset);
- return;
+ return true;
case MHD_PROC_RECV_BODY_NORMAL:
/* A header probably has been added to a suspended connection and
it took precisely all the space in the buffer.
Very low probability. */
mhd_assert (! c->rq.have_chunked_upload);
handle_req_headers_no_space (c, NULL, 0); // FIXME: check
- return;
+ return true;
case MHD_PROC_RECV_BODY_CHUNKED:
mhd_assert (c->rq.have_chunked_upload);
if (c->rq.current_chunk_offset != c->rq.current_chunk_size)
@@ -3866,16 +3868,17 @@ handle_recv_no_space (struct MHD_Connection *c,
c->read_buffer_offset);
}
}
- return;
+ return true;
case MHD_PROC_RECV_FOOTERS:
handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset);
- return;
+ return true;
/* The next cases should not be possible */
case MHD_PROC_RECV_COOKIE:
default:
break;
}
mhd_UNREACHABLE ();
+ return false;
}
@@ -3968,9 +3971,11 @@ try_grow_read_buffer (struct MHD_Connection *restrict connection,
}
-MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnectionBufferGrowResult
mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
{
+ enum MHD_ProcRecvDataStage stage;
+ bool res;
/**
* The increase of read buffer size is desirable.
*/
@@ -4024,78 +4029,78 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
}
if (! rbuff_grow_desired)
- return true; /* No need to increase the buffer */
+ return mhd_CONN_BUFF_GROW_OK; /* No need to increase the buffer */
if (try_grow_read_buffer (c, rbuff_grow_required))
- return true; /* Buffer increase succeed */
+ return mhd_CONN_BUFF_GROW_OK; /* Buffer increase succeed */
if (! rbuff_grow_required)
- return true; /* Can continue without buffer increase */
+ return mhd_CONN_BUFF_GROW_OK; /* Can continue without buffer increase */
/* Failed to increase the read buffer size, but need to read the data
from the network.
No more space left in the buffer, no more space to increase the buffer. */
- if (1)
+ switch (c->stage)
{
- enum MHD_ProcRecvDataStage stage;
-
- switch (c->stage)
- {
- case mhd_HTTP_STAGE_INIT:
- stage = MHD_PROC_RECV_INIT;
- break;
- case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
- if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd)
- stage = MHD_PROC_RECV_METHOD;
- else if (0 == c->rq.req_target_len)
- stage = MHD_PROC_RECV_URI;
- else
- stage = MHD_PROC_RECV_HTTPVER;
- break;
- case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
- stage = MHD_PROC_RECV_HEADERS;
- break;
- case mhd_HTTP_STAGE_BODY_RECEIVING:
- stage = c->rq.have_chunked_upload ?
- MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
- break;
- case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
- stage = MHD_PROC_RECV_FOOTERS;
- break;
- case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
- case mhd_HTTP_STAGE_HEADERS_RECEIVED:
- case mhd_HTTP_STAGE_HEADERS_PROCESSED:
- case mhd_HTTP_STAGE_CONTINUE_SENDING:
- case mhd_HTTP_STAGE_BODY_RECEIVED:
- case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
- case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
- case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
- case mhd_HTTP_STAGE_START_REPLY:
- case mhd_HTTP_STAGE_HEADERS_SENDING:
- case mhd_HTTP_STAGE_HEADERS_SENT:
- case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
- case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
- case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
- case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
- case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
- case mhd_HTTP_STAGE_FOOTERS_SENDING:
- case mhd_HTTP_STAGE_FULL_REPLY_SENT:
- case mhd_HTTP_STAGE_PRE_CLOSING:
- case mhd_HTTP_STAGE_CLOSED:
+ case mhd_HTTP_STAGE_INIT:
+ stage = MHD_PROC_RECV_INIT;
+ break;
+ case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
+ if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd)
+ stage = MHD_PROC_RECV_METHOD;
+ else if (0 == c->rq.req_target_len)
+ stage = MHD_PROC_RECV_URI;
+ else
+ stage = MHD_PROC_RECV_HTTPVER;
+ break;
+ case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
+ stage = MHD_PROC_RECV_HEADERS;
+ break;
+ case mhd_HTTP_STAGE_BODY_RECEIVING:
+ stage = c->rq.have_chunked_upload ?
+ MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
+ break;
+ case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
+ stage = MHD_PROC_RECV_FOOTERS;
+ break;
+ case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
+ case mhd_HTTP_STAGE_HEADERS_RECEIVED:
+ case mhd_HTTP_STAGE_HEADERS_PROCESSED:
+ case mhd_HTTP_STAGE_CONTINUE_SENDING:
+ case mhd_HTTP_STAGE_BODY_RECEIVED:
+ case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
+ case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
+ case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
+ case mhd_HTTP_STAGE_START_REPLY:
+ case mhd_HTTP_STAGE_HEADERS_SENDING:
+ case mhd_HTTP_STAGE_HEADERS_SENT:
+ case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
+ case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
+ case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
+ case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
+ case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
+ case mhd_HTTP_STAGE_FOOTERS_SENDING:
+ case mhd_HTTP_STAGE_FULL_REPLY_SENT:
+ case mhd_HTTP_STAGE_PRE_CLOSING:
+ case mhd_HTTP_STAGE_CLOSED:
#ifdef MHD_SUPPORT_UPGRADE
- case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
- case mhd_HTTP_STAGE_UPGRADING:
- case mhd_HTTP_STAGE_UPGRADED:
- case mhd_HTTP_STAGE_UPGRADED_CLEANING:
+ case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
+ case mhd_HTTP_STAGE_UPGRADING:
+ case mhd_HTTP_STAGE_UPGRADED:
+ case mhd_HTTP_STAGE_UPGRADED_CLEANING:
#endif /* MHD_SUPPORT_UPGRADE */
- default:
- mhd_UNREACHABLE ();
- stage = MHD_PROC_RECV_BODY_NORMAL;
- break;
- }
-
- handle_recv_no_space (c, stage);
+ default:
+ mhd_UNREACHABLE ();
+ stage = MHD_PROC_RECV_BODY_NORMAL;
+ break;
}
- return false;
+
+ res = handle_recv_no_space (c, stage);
+
+ mhd_assert (! res || ! c->dbg.closing_started);
+ mhd_assert (res || c->dbg.closing_started);
+
+ return
+ res ? mhd_CONN_BUFF_GROW_ERR_REPLY : mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE;
}
diff --git a/src/mhd2/stream_process_request.h b/src/mhd2/stream_process_request.h
@@ -226,6 +226,25 @@ mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
+ * Result of the connection buffer growing
+ */
+enum mhd_ConnectionBufferGrowResult
+{
+ /**
+ * No error
+ */
+ mhd_CONN_BUFF_GROW_OK = 0u,
+ /**
+ * Failed to grow buffer, the connection is closing
+ */
+ mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE,
+ /**
+ * Failed to grow buffer, the error reply is pending
+ */
+ mhd_CONN_BUFF_GROW_ERR_REPLY
+};
+
+/**
* Check whether enough space is available in the read buffer for the next
* operation.
* Handles grow of the buffer if required and error conditions (when buffer
@@ -233,12 +252,11 @@ MHD_FN_PAR_NONNULL_ALL_;
* Must be called only when processing the event loop states and when
* reading is required for the next phase.
* @param c the connection to check
- * @return true if connection handled successfully and enough buffer
- * is available,
- * false if not enough buffer is available and the loop's states
- * must be processed again as connection is in the error state.
+ * @return #mhd_CONN_BUFF_GROW_OK if connection handled successfully and
+ * enough buffer is available,
+ * error code otherwise.
*/
-MHD_INTERNAL bool
+MHD_INTERNAL enum mhd_ConnectionBufferGrowResult
mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c
@@ -276,56 +276,6 @@ finish_resume (struct MHD_Connection *restrict c)
}
-/**
- * Update current processing state: need to receive, need to send.
- * Mark stream as ready or not ready for processing.
- * Grow the receive buffer if neccesary, close stream if no buffer space left,
- * but connection needs to receive.
- * @param c the connection to update
- * @return true if connection states updated successfully,
- * false if connection has been prepared for closing
- */
-static MHD_FN_PAR_NONNULL_ALL_ bool
-update_active_state (struct MHD_Connection *restrict c)
-{
- /* Do not update states of suspended connection */
- mhd_assert (! c->suspended);
-
- if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))
- {
- mhd_assert (0 && "Should be handled earlier");
- mhd_conn_start_closing_skt_err (c);
- return false;
- }
-
- mhd_conn_event_loop_state_update (c);
-
- if (! mhd_C_IS_HTTP2 (c))
- {
- if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
- {
- /* Check whether the space is available to receive data */
- if (! mhd_stream_check_and_grow_read_buffer_space (c))
- {
- mhd_assert (c->discard_request);
- return false;
- }
- }
- }
-
- /* Current MHD design assumes that data must be always processes when
- * available. If it is not possible, connection must be suspended. */
- mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
-
- /* Sockets errors must be already handled */
- mhd_assert (0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY));
-
- mhd_conn_mark_ready_update (c);
-
- return true;
-}
-
-
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_conn_process_data (struct MHD_Connection *restrict c)
{
@@ -364,7 +314,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
{
if (! mhd_h2_conn_process_data (c))
return false;
- update_active_state (c);
+ mhd_conn_event_loop_state_update (c);
+ mhd_conn_mark_ready_update (c);
return true;
}
#endif /* MHD_SUPPORT_HTTP2 */
@@ -408,7 +359,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_assert (! c->suspended);
- while (! c->suspended)
+ while (! 0)
{
#ifdef MHD_SUPPORT_HTTPS
mhd_assert (! mhd_C_HAS_TLS (c) || \
@@ -626,45 +577,79 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_UNREACHABLE ();
break;
}
- break;
- }
- mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage);
+ mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage);
- if (mhd_HTTP_STAGE_PRE_CLOSING == c->stage)
- {
- mhd_assert (0 && "Pre-closing should be already caught in the loop");
- mhd_UNREACHABLE ();
- return false;
- }
+ if (mhd_HTTP_STAGE_PRE_CLOSING == c->stage)
+ {
+ mhd_assert (0 && "Pre-closing should be already caught in the loop");
+ mhd_UNREACHABLE ();
+ return false;
+ }
- if (c->suspended)
- {
- /* Do not perform any network activity while suspended */
- c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+ if (c->suspended)
+ {
+ /* Do not perform any network activity while suspended */
+ c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
+
+ mhd_conn_mark_unready (c, d);
+ mhd_conn_deinit_activity_timeout (c);
+ #ifdef mhd_DEBUG_SUSPEND_RESUME
+ fprintf (stderr,
+ "%%%%%% Connection suspended, FD: %2llu\n",
+ (unsigned long long) c->sk.fd);
+ #endif /* mhd_DEBUG_SUSPEND_RESUME */
+ return true;
+ }
- mhd_conn_mark_unready (c, d);
- mhd_conn_deinit_activity_timeout (c);
-#ifdef mhd_DEBUG_SUSPEND_RESUME
- fprintf (stderr,
- "%%%%%% Connection suspended, FD: %2llu\n",
- (unsigned long long) c->sk.fd);
-#endif /* mhd_DEBUG_SUSPEND_RESUME */
- return true;
- }
+ if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
+ {
+ mhd_conn_start_closing (c,
+ (mhd_HTTP_STAGE_INIT == c->stage) ?
+ mhd_CONN_CLOSE_HTTP_COMPLETED :
+ mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
+ NULL);
+ return false;
+ }
- if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
- {
- mhd_conn_start_closing (c,
- (mhd_HTTP_STAGE_INIT == c->stage) ?
- mhd_CONN_CLOSE_HTTP_COMPLETED :
- mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
- NULL);
- return false;
- }
+ if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))
+ {
+ mhd_assert (0 && "Should be handled earlier");
+ mhd_conn_start_closing_skt_err (c);
+ return false;
+ }
- if (! update_active_state (c))
- return false;
+ mhd_conn_event_loop_state_update (c);
+
+ if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
+ {
+ /* Check whether the space is available to receive data */
+ switch (mhd_stream_check_and_grow_read_buffer_space (c))
+ {
+ case mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE:
+ mhd_assert (c->discard_request);
+ return false;
+ case mhd_CONN_BUFF_GROW_ERR_REPLY:
+ continue; /* Process error reply */
+ case mhd_CONN_BUFF_GROW_OK:
+ break;
+ default:
+ mhd_UNREACHABLE ();
+ break;
+ }
+ }
+
+ /* Current MHD design assumes that data must be always processes when
+ * available. If it is not possible, connection must be suspended. */
+ mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
+
+ /* Sockets errors must be already handled */
+ mhd_assert (0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY));
+
+ mhd_conn_mark_ready_update (c);
+
+ break;
+ }
return true;
}