diff options
author | 2024-09-04 18:04:54 +0200 | |
---|---|---|
committer | 2024-09-08 22:41:27 +0000 | |
commit | 9bb0762357015ca49ab8376308eda021d35d6f25 (patch) | |
tree | b7b7e3c0b4e53f6daf88e780f1e66bd16b1b0282 /src/plugins/http | |
parent | c4b5d10115d4370488ac14eb0ba7295b049a0615 (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.c | 71 | ||||
-rw-r--r-- | src/plugins/http/http_plugin.rst | 44 |
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 |