diff options
Diffstat (limited to 'src/plugins/http/http_private.h')
-rw-r--r-- | src/plugins/http/http_private.h | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/src/plugins/http/http_private.h b/src/plugins/http/http_private.h new file mode 100644 index 00000000000..187d1fe8215 --- /dev/null +++ b/src/plugins/http/http_private.h @@ -0,0 +1,672 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#ifndef SRC_PLUGINS_HTTP_HTTP_PRIVATE_H_ +#define SRC_PLUGINS_HTTP_HTTP_PRIVATE_H_ + +#include <vppinfra/time_range.h> +#include <vnet/session/application.h> +#include <vnet/session/session.h> +#include <vnet/session/transport.h> +#include <http/http.h> +#include <http/http_buffer.h> + +#define HTTP_FIFO_THRESH (16 << 10) + +typedef u32 http_conn_handle_t; + +typedef struct http_conn_id_ +{ + union + { + session_handle_t app_session_handle; + u32 parent_app_api_ctx; + }; + session_handle_t tc_session_handle; + u32 parent_app_wrk_index; +} http_conn_id_t; + +STATIC_ASSERT (sizeof (http_conn_id_t) <= TRANSPORT_CONN_ID_LEN, + "ctx id must be less than TRANSPORT_CONN_ID_LEN"); + +#define foreach_http_conn_state \ + _ (LISTEN, "LISTEN") \ + _ (CONNECTING, "CONNECTING") \ + _ (ESTABLISHED, "ESTABLISHED") \ + _ (TRANSPORT_CLOSED, "TRANSPORT-CLOSED") \ + _ (APP_CLOSED, "APP-CLOSED") \ + _ (CLOSED, "CLOSED") + +typedef enum http_conn_state_ +{ +#define _(s, str) HTTP_CONN_STATE_##s, + foreach_http_conn_state +#undef _ +} http_conn_state_t; + +#define foreach_http_req_state \ + _ (0, IDLE, "idle") \ + _ (1, WAIT_APP_METHOD, "wait app method") \ + _ (2, WAIT_TRANSPORT_REPLY, "wait transport reply") \ + _ (3, TRANSPORT_IO_MORE_DATA, "transport io more data") \ + _ (4, WAIT_TRANSPORT_METHOD, "wait transport method") \ + _ (5, WAIT_APP_REPLY, "wait app reply") \ + _ (6, APP_IO_MORE_DATA, "app io more data") \ + _ (7, TUNNEL, "tunnel") \ + _ (8, UDP_TUNNEL, "udp tunnel") + +typedef enum http_req_state_ +{ +#define _(n, s, str) HTTP_REQ_STATE_##s = n, + foreach_http_req_state +#undef _ + HTTP_REQ_N_STATES +} http_req_state_t; + +typedef enum http_target_form_ +{ + HTTP_TARGET_ORIGIN_FORM, + HTTP_TARGET_ABSOLUTE_FORM, + HTTP_TARGET_AUTHORITY_FORM, + HTTP_TARGET_ASTERISK_FORM +} http_target_form_t; + +typedef enum http_version_ +{ + HTTP_VERSION_1, + HTTP_VERSION_2, + HTTP_VERSION_3, + HTTP_VERSION_NA = 7, +} http_version_t; + +typedef struct http_req_ +{ + /* in case of multiplexing we have app session for each stream */ + session_handle_t app_session_handle; + u32 as_fifo_offset; /* for peek */ + + http_req_state_t state; /* state-machine state */ + + http_buffer_t tx_buf; /* message body from app to be sent */ + + /* + * for parsing of incoming message from transport + */ + u32 rx_buf_offset; /* current offset during parsing */ + u32 control_data_len; /* start line + headers + empty line */ + + union + { + u64 to_recv; /* remaining bytes of body to receive from transport */ + u64 to_skip; /* remaining bytes of capsule to skip */ + }; + + u8 is_tunnel; + + /* + * parsed metadata for app + */ + union + { + http_status_code_t status_code; + http_req_method_t method; + }; + + http_target_form_t target_form; + u8 *target; + http_url_scheme_t scheme; + u32 target_authority_offset; + u32 target_authority_len; + u32 target_path_offset; + u32 target_path_len; + u32 target_query_offset; + u32 target_query_len; + + u32 headers_offset; + u32 headers_len; + + u32 body_offset; + u64 body_len; + + http_field_line_t *headers; + uword content_len_header_index; + uword connection_header_index; + uword upgrade_header_index; + uword host_header_index; + + http_upgrade_proto_t upgrade_proto; +} http_req_t; + +typedef struct http_tc_ +{ + union + { + transport_connection_t connection; + http_conn_id_t c_http_conn_id; + }; +#define h_tc_session_handle c_http_conn_id.tc_session_handle +#define h_pa_wrk_index c_http_conn_id.parent_app_wrk_index +#define h_pa_session_handle c_http_conn_id.app_session_handle +#define h_pa_app_api_ctx c_http_conn_id.parent_app_api_ctx +#define h_hc_index connection.c_index + + http_version_t version; + http_conn_state_t state; + u32 timer_handle; + u32 timeout; + u8 pending_timer; + u8 *app_name; + u8 *host; + u8 is_server; + http_udp_tunnel_mode_t udp_tunnel_mode; + + http_req_t *req_pool; /* multiplexing => request per stream */ +} http_conn_t; + +typedef struct http_worker_ +{ + http_conn_t *conn_pool; +} http_worker_t; + +typedef struct http_main_ +{ + http_worker_t *wrk; + http_conn_t *listener_pool; + http_conn_t *ho_conn_pool; + u32 app_index; + + u8 **rx_bufs; + u8 **tx_bufs; + u8 **app_header_lists; + + clib_timebase_t timebase; + + http_status_code_t *sc_by_u16; + /* + * Runtime config + */ + u8 is_init; + + /* + * Config + */ + u64 first_seg_size; + u64 add_seg_size; + u32 fifo_size; +} http_main_t; + +typedef struct http_engine_vft_ +{ + void (*app_tx_callback) (http_conn_t *hc, transport_send_params_t *sp); + void (*app_rx_evt_callback) (http_conn_t *hc); + void (*app_close_callback) (http_conn_t *hc); + void (*app_reset_callback) (http_conn_t *hc); + void (*transport_rx_callback) (http_conn_t *hc); + void (*transport_close_callback) (http_conn_t *hc); +} http_engine_vft_t; + +void http_register_engine (const http_engine_vft_t *vft, + http_version_t version); + +/* HTTP state machine result */ +typedef enum http_sm_result_t_ +{ + HTTP_SM_STOP = 0, + HTTP_SM_CONTINUE = 1, + HTTP_SM_ERROR = -1, +} http_sm_result_t; + +typedef http_sm_result_t (*http_sm_handler) (http_conn_t *hc, http_req_t *req, + transport_send_params_t *sp); + +#define expect_char(c) \ + if (*p++ != c) \ + { \ + clib_warning ("unexpected character"); \ + return -1; \ + } + +#define parse_int(val, mul) \ + do \ + { \ + if (!isdigit (*p)) \ + { \ + clib_warning ("expected digit"); \ + return -1; \ + } \ + val += mul * (*p++ - '0'); \ + } \ + while (0) + +#define http_field_line_value_token(_fl, _req, _rx_buf) \ + (const char *) ((_rx_buf) + (_req)->headers_offset + (_fl)->value_offset), \ + (_fl)->value_len + +u8 *format_http_req_state (u8 *s, va_list *va); +u8 *format_http_conn_state (u8 *s, va_list *args); +u8 *format_http_time_now (u8 *s, va_list *args); + +/** + * @brief Find the first occurrence of the string in the vector. + * + * @param vec The vector to be scanned. + * @param offset Search offset in the vector. + * @param num Maximum number of characters to be searched if non-zero. + * @param str The string to be searched. + * + * @return @c -1 if the string is not found within the vector; index otherwise. + */ +int http_v_find_index (u8 *vec, u32 offset, u32 num, char *str); + +/** + * Disconnect HTTP connection. + * + * @param hc HTTP connection to disconnect. + */ +void http_disconnect_transport (http_conn_t *hc); + +/** + * Convert numeric representation of status code to @c http_status_code_t. + * + * @param status_code Status code within the range of 100 to 599, inclusive. + * + * @return Registered status code or in case of unrecognized status code as + * equivalent to the x00 status code of that class. + */ +http_status_code_t http_sc_by_u16 (u16 status_code); + +/** + * Read header list sent by app. + * + * @param hc HTTP connection. + * @param msg HTTP msg sent by app. + * + * @return Pointer to the header list. + * + * @note For immediate processing, not for buffering. + */ +u8 *http_get_app_header_list (http_conn_t *hc, http_msg_t *msg); + +/** + * Get pre-allocated TX buffer/vector. + * + * @param hc HTTP connection. + * + * @return Pointer to the vector. + * + * @note Vector length is reset to zero, use as temporary storage. + */ +u8 *http_get_tx_buf (http_conn_t *hc); + +/** + * Get pre-allocated RX buffer/vector. + * + * @param hc HTTP connection. + * + * @return Pointer to the vector. + * + * @note Vector length is reset to zero, use as temporary storage. + */ +u8 *http_get_rx_buf (http_conn_t *hc); + +/** + * Read request target path sent by app. + * + * @param hc HTTP connection. + * @param msg HTTP msg sent by app. + * + * @return Pointer to the target path. + * + * @note Valid only with request lifetime. + */ +u8 *http_get_app_target (http_req_t *req, http_msg_t *msg); + +/** + * Initialize per-request HTTP TX buffer. + * + * @param req HTTP request. + * @param msg HTTP msg sent by app. + * + * @note Use for streaming of body sent by app. + */ +void http_req_tx_buffer_init (http_req_t *req, http_msg_t *msg); + +/** + * Allocate new request within given HTTP connection. + * + * @param hc HTTP connection. + * + * @return Request index in per-connection pool. + */ +always_inline u32 +http_alloc_req (http_conn_t *hc) +{ + http_req_t *req; + pool_get_zero (hc->req_pool, req); + req->app_session_handle = SESSION_INVALID_HANDLE; + return (req - hc->req_pool); +} + +/** + * Get request in per-connection pool. + * + * @param hc HTTP connection. + * @param req_index Request index. + * + * @return Pointer to the request data. + */ +always_inline http_req_t * +http_get_req (http_conn_t *hc, u32 req_index) +{ + return pool_elt_at_index (hc->req_pool, req_index); +} + +/** + * Get request in per-connection pool if valid. + * + * @param hc HTTP connection. + * @param req_index Request index. + * + * @return Pointer to the request data or @c 0 if not valid. + */ +always_inline http_req_t * +http_get_req_if_valid (http_conn_t *hc, u32 req_index) +{ + if (pool_is_free_index (hc->req_pool, req_index)) + return 0; + return pool_elt_at_index (hc->req_pool, req_index); +} + +/** + * Free request in per-connection pool. + * + * @param hc HTTP connection. + * @param req Pointer to the request. + */ +always_inline void +http_req_free (http_conn_t *hc, http_req_t *req) +{ + vec_free (req->headers); + vec_free (req->target); + http_buffer_free (&req->tx_buf); + if (CLIB_DEBUG) + memset (req, 0xba, sizeof (*req)); + pool_put (hc->req_pool, req); +} + +/** + * Change state of given HTTP request. + * + * @param req HTTP request. + * @param state New state. + */ +always_inline void +http_req_state_change (http_req_t *req, http_req_state_t state) +{ + HTTP_DBG (1, "changing http req state: %U -> %U", format_http_req_state, + req->state, format_http_req_state, state); + ASSERT (req->state != HTTP_REQ_STATE_TUNNEL); + req->state = state; +} + +/** + * Send RX event to the app worker. + * + * @param req HTTP request. + */ +always_inline void +http_app_worker_rx_notify (http_req_t *req) +{ + session_t *as; + app_worker_t *app_wrk; + + as = session_get_from_handle (req->app_session_handle); + app_wrk = app_worker_get_if_valid (as->app_wrk_index); + if (app_wrk) + app_worker_rx_notify (app_wrk, as); +} + +/** + * Get underlying transport protocol of the HTTP connection. + * + * @param hc HTTP connection. + * + * @return Transport protocol, @ref transport_proto_t. + */ +always_inline transport_proto_t +http_get_transport_proto (http_conn_t *hc) +{ + return session_get_transport_proto ( + session_get_from_handle (hc->h_tc_session_handle)); +} + +/** + * Read HTTP msg sent by app. + * + * @param req HTTP request. + * @param msg HTTP msq will be stored here. + */ +always_inline void +http_get_app_msg (http_req_t *req, http_msg_t *msg) +{ + session_t *as; + int rv; + + as = session_get_from_handle (req->app_session_handle); + rv = svm_fifo_dequeue (as->tx_fifo, sizeof (*msg), (u8 *) msg); + ASSERT (rv == sizeof (*msg)); +} + +/* Abstraction of app session fifo operations */ + +always_inline void +http_io_as_want_deq_ntf (http_req_t *req) +{ + session_t *as = session_get_from_handle (req->app_session_handle); + svm_fifo_add_want_deq_ntf (as->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); +} + +always_inline u32 +http_io_as_max_write (http_req_t *req) +{ + session_t *as = session_get_from_handle (req->app_session_handle); + return svm_fifo_max_enqueue_prod (as->rx_fifo); +} + +always_inline u32 +http_io_as_max_read (http_req_t *req) +{ + session_t *as = session_get_from_handle (req->app_session_handle); + return svm_fifo_max_dequeue_cons (as->tx_fifo); +} + +always_inline u32 +http_io_as_write_segs (http_req_t *req, const svm_fifo_seg_t segs[], + u32 n_segs) +{ + int n_written; + session_t *as = session_get_from_handle (req->app_session_handle); + n_written = svm_fifo_enqueue_segments (as->rx_fifo, segs, n_segs, 0); + ASSERT (n_written > 0); + return (u32) n_written; +} + +always_inline u32 +http_io_as_read (http_req_t *req, u8 *buf, u32 len, u8 peek) +{ + int n_read; + session_t *as = session_get_from_handle (req->app_session_handle); + + if (peek) + { + n_read = svm_fifo_peek (as->tx_fifo, req->as_fifo_offset, len, buf); + ASSERT (n_read > 0); + req->as_fifo_offset += len; + return (u32) n_read; + } + + n_read = svm_fifo_dequeue (as->tx_fifo, len, buf); + ASSERT (n_read == len); + return (u32) n_read; +} + +always_inline void +http_io_as_read_segs (http_req_t *req, svm_fifo_seg_t *segs, u32 *n_segs, + u32 max_bytes) +{ + int n_read; + session_t *as = session_get_from_handle (req->app_session_handle); + n_read = svm_fifo_segments (as->tx_fifo, 0, segs, n_segs, max_bytes); + ASSERT (n_read > 0); +} + +always_inline void +http_io_as_drain (http_req_t *req, u32 len) +{ + session_t *as = session_get_from_handle (req->app_session_handle); + svm_fifo_dequeue_drop (as->tx_fifo, len); + req->as_fifo_offset = 0; +} + +always_inline void +http_io_as_drain_all (http_req_t *req) +{ + session_t *as = session_get_from_handle (req->app_session_handle); + svm_fifo_dequeue_drop_all (as->tx_fifo); + req->as_fifo_offset = 0; +} + +/* Abstraction of transport session fifo operations */ + +always_inline u32 +http_io_ts_max_read (http_conn_t *hc) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + return svm_fifo_max_dequeue_cons (ts->rx_fifo); +} + +always_inline u32 +http_io_ts_max_write (http_conn_t *hc, transport_send_params_t *sp) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + return clib_min (svm_fifo_max_enqueue_prod (ts->tx_fifo), + sp->max_burst_size); +} + +always_inline u32 +http_io_ts_read (http_conn_t *hc, u8 *buf, u32 len, u8 peek) +{ + int n_read; + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + + if (peek) + { + n_read = svm_fifo_peek (ts->rx_fifo, 0, len, buf); + ASSERT (n_read > 0); + return (u32) n_read; + } + + n_read = svm_fifo_dequeue (ts->rx_fifo, len, buf); + ASSERT (n_read == len); + return (u32) n_read; +} + +always_inline void +http_io_ts_read_segs (http_conn_t *hc, svm_fifo_seg_t *segs, u32 *n_segs, + u32 max_bytes) +{ + int n_read; + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + n_read = svm_fifo_segments (ts->rx_fifo, 0, segs, n_segs, max_bytes); + ASSERT (n_read > 0); +} + +always_inline void +http_io_ts_drain (http_conn_t *hc, u32 len) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + svm_fifo_dequeue_drop (ts->rx_fifo, len); +} + +always_inline void +http_io_ts_drain_all (http_conn_t *hc) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + svm_fifo_dequeue_drop_all (ts->rx_fifo); +} + +always_inline void +http_io_ts_after_read (http_conn_t *hc, u8 clear_evt) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + if (clear_evt) + { + if (svm_fifo_is_empty_cons (ts->rx_fifo)) + svm_fifo_unset_event (ts->rx_fifo); + } + else + { + if (svm_fifo_max_dequeue_cons (ts->rx_fifo)) + session_program_rx_io_evt (hc->h_tc_session_handle); + } +} + +always_inline void +http_io_ts_write (http_conn_t *hc, u8 *data, u32 len, + transport_send_params_t *sp) +{ + int n_written; + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + + n_written = svm_fifo_enqueue (ts->tx_fifo, len, data); + ASSERT (n_written == len); + if (sp) + { + ASSERT (sp->max_burst_size >= len); + sp->bytes_dequeued += len; + sp->max_burst_size -= len; + } +} + +always_inline u32 +http_io_ts_write_segs (http_conn_t *hc, const svm_fifo_seg_t segs[], + u32 n_segs, transport_send_params_t *sp) +{ + int n_written; + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + n_written = svm_fifo_enqueue_segments (ts->tx_fifo, segs, n_segs, 0); + ASSERT (n_written > 0); + sp->bytes_dequeued += n_written; + sp->max_burst_size -= n_written; + return (u32) n_written; +} + +always_inline void +http_io_ts_after_write (http_conn_t *hc, transport_send_params_t *sp, u8 flush, + u8 written) +{ + session_t *ts = session_get_from_handle (hc->h_tc_session_handle); + + if (!flush) + { + if (written && svm_fifo_set_event (ts->tx_fifo)) + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX); + + if (sp && (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH)) + { + /* Deschedule http session and wait for deq notification if + * underlying ts tx fifo almost full */ + svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); + transport_connection_deschedule (&hc->connection); + sp->flags |= TRANSPORT_SND_F_DESCHED; + } + } + else + { + if (written && svm_fifo_set_event (ts->tx_fifo)) + session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX_FLUSH); + } +} + +#endif /* SRC_PLUGINS_HTTP_HTTP_PRIVATE_H_ */ |