aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/http
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2024-09-04 18:04:54 +0200
committerFlorin Coras <florin.coras@gmail.com>2024-09-08 22:41:27 +0000
commit9bb0762357015ca49ab8376308eda021d35d6f25 (patch)
treeb7b7e3c0b4e53f6daf88e780f1e66bd16b1b0282 /src/plugins/http
parentc4b5d10115d4370488ac14eb0ba7295b049a0615 (diff)
http: large POST handling
Type: improvement Change-Id: I28b8e8ccbff6f97e669b0048011b187decbfc892 Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src/plugins/http')
-rw-r--r--src/plugins/http/http.c71
-rw-r--r--src/plugins/http/http_plugin.rst44
2 files changed, 87 insertions, 28 deletions
diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c
index a32f6f6cb84..d01ee7f0fc6 100644
--- a/src/plugins/http/http.c
+++ b/src/plugins/http/http.c
@@ -527,13 +527,18 @@ v_find_index (u8 *vec, u32 offset, u32 num, char *str)
static void
http_identify_optional_query (http_conn_t *hc)
{
- u32 pos = vec_search (hc->rx_buf, '?');
- if (~0 != pos)
+ int i;
+ for (i = hc->target_path_offset;
+ i < (hc->target_path_offset + hc->target_path_len); i++)
{
- hc->target_query_offset = pos + 1;
- hc->target_query_len =
- hc->target_path_offset + hc->target_path_len - hc->target_query_offset;
- hc->target_path_len = hc->target_path_len - hc->target_query_len - 1;
+ if (hc->rx_buf[i] == '?')
+ {
+ hc->target_query_offset = i + 1;
+ hc->target_query_len = hc->target_path_offset + hc->target_path_len -
+ hc->target_query_offset;
+ hc->target_path_len = hc->target_path_len - hc->target_query_len - 1;
+ break;
+ }
}
}
@@ -674,7 +679,9 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec)
}
/* parse request-target */
+ HTTP_DBG (0, "http at %d", i);
target_len = i - hc->target_path_offset;
+ HTTP_DBG (0, "target_len %d", target_len);
if (target_len < 1)
{
clib_warning ("request-target not present");
@@ -911,7 +918,7 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
http_msg_t msg = {};
app_worker_t *app_wrk;
session_t *as;
- u32 len, max_enq;
+ u32 len, max_enq, body_sent;
http_status_code_t ec;
http_main_t *hm = &http_main;
@@ -972,16 +979,16 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
http_read_message_drop (hc, len);
- if (hc->body_len == 0)
+ body_sent = len - hc->control_data_len;
+ hc->to_recv = hc->body_len - body_sent;
+ if (hc->to_recv == 0)
{
- /* no response body, we are done */
- hc->to_recv = 0;
+ /* all sent, we are done */
http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
}
else
{
- /* stream response body */
- hc->to_recv = hc->body_len;
+ /* stream rest of the response body */
http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
}
@@ -1006,7 +1013,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
http_msg_t msg;
session_t *as;
int rv;
- u32 len, max_enq;
+ u32 len, max_enq, max_deq, body_sent;
rv = http_read_message (hc);
@@ -1034,16 +1041,20 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
if (rv)
goto error;
- /* send "control data" and request body */
+ /* send at least "control data" which is necessary minimum,
+ * if there is some space send also portion of body */
as = session_get_from_handle (hc->h_pa_session_handle);
- len = hc->control_data_len + hc->body_len;
max_enq = svm_fifo_max_enqueue (as->rx_fifo);
- if (max_enq < len)
+ if (max_enq < hc->control_data_len)
{
- /* TODO stream body of large POST */
- clib_warning ("not enough room for data in app's rx fifo");
+ clib_warning ("not enough room for control data in app's rx fifo");
+ ec = HTTP_STATUS_INTERNAL_ERROR;
goto error;
}
+ /* do not dequeue more than one HTTP request, we do not support pipelining */
+ max_deq =
+ clib_min (hc->control_data_len + hc->body_len, vec_len (hc->rx_buf));
+ len = clib_min (max_enq, max_deq);
msg.type = HTTP_MSG_REQUEST;
msg.method_type = hc->method;
@@ -1065,9 +1076,21 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
ASSERT (rv == (sizeof (msg) + len));
- /* drop everything, we do not support pipelining */
- http_read_message_drop_all (hc);
- http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
+ body_sent = len - hc->control_data_len;
+ hc->to_recv = hc->body_len - body_sent;
+ if (hc->to_recv == 0)
+ {
+ /* drop everything, we do not support pipelining */
+ http_read_message_drop_all (hc);
+ /* all sent, we are done */
+ http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
+ }
+ else
+ {
+ http_read_message_drop (hc, len);
+ /* stream rest of the response body */
+ http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
+ }
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
if (app_wrk)
@@ -1408,8 +1431,12 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
hc->to_recv -= rv;
HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv);
+ /* Finished transaction:
+ * server back to HTTP_STATE_WAIT_APP_REPLY
+ * client to HTTP_STATE_WAIT_APP_METHOD */
if (hc->to_recv == 0)
- http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
+ http_state_change (hc, hc->is_server ? HTTP_STATE_WAIT_APP_REPLY :
+ HTTP_STATE_WAIT_APP_METHOD);
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
if (app_wrk)
diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst
index 4daef79b9cf..feb2c7fe039 100644
--- a/src/plugins/http/http_plugin.rst
+++ b/src/plugins/http/http_plugin.rst
@@ -144,16 +144,48 @@ Following example shows how to parse headers:
vec_free (headers);
}
-Finally application reads body:
+Finally application reads body (if any), which might be received in multiple pieces (depends on size), so we might need some state machine in ``builtin_app_rx_callback``.
+We will add following members to our session context structure:
+
+.. code-block:: C
+
+ typedef struct
+ {
+ /* ... */
+ u32 to_recv;
+ u8 *resp_body;
+ } session_ctx_t;
+
+First we prepare vector for response body, do it only once when you are reading metadata:
+
+.. code-block:: C
+
+ /* drop everything up to body */
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset);
+ ctx->to_recv = msg.data.body_len;
+ /* prepare vector for response body */
+ vec_validate (ctx->resp_body, msg.data.body_len - 1);
+ vec_reset_length (ctx->resp_body);
+
+Now we can start reading body content, following block of code could be executed multiple times:
.. code-block:: C
- u8 *body = 0;
- if (msg.data.body_len)
+ /* dequeue */
+ u32 n_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+ /* current offset */
+ u32 curr = vec_len (ctx->resp_body);
+ rv = svm_fifo_dequeue (ts->rx_fifo, n_deq, ctx->resp_body + curr);
+ ASSERT (rv == n_deq);
+ /* update length of the vector */
+ vec_set_len (ctx->resp_body, curr + n_deq);
+ /* update number of remaining bytes to receive */
+ ctx->to_recv -= rv;
+ /* check if all data received */
+ if (ctx->to_recv == 0)
{
- vec_validate (body, msg.data.body_len - 1);
- rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len, body);
- ASSERT (rv == msg.data.body_len);
+ /* we are done */
+ /* send 200 OK response */
}
Sending data