aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/http
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/http')
-rw-r--r--src/plugins/http/http.c152
-rw-r--r--src/plugins/http/http.h110
-rw-r--r--src/plugins/http/http_plugin.rst54
3 files changed, 181 insertions, 135 deletions
diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c
index 666f45c7d8f..69b661d0611 100644
--- a/src/plugins/http/http.c
+++ b/src/plugins/http/http.c
@@ -468,14 +468,12 @@ static const char *connection_upgrade_template = "Connection: upgrade\r\n"
*/
static const char *http_get_request_template = "GET %s HTTP/1.1\r\n"
"Host: %v\r\n"
- "User-Agent: %v\r\n"
- "%s";
+ "User-Agent: %v\r\n";
static const char *http_post_request_template = "POST %s HTTP/1.1\r\n"
"Host: %v\r\n"
"User-Agent: %v\r\n"
- "Content-Length: %llu\r\n"
- "%s";
+ "Content-Length: %llu\r\n";
static u32
http_send_data (http_conn_t *hc, u8 *data, u32 length)
@@ -1368,6 +1366,76 @@ error:
return HTTP_SM_ERROR;
}
+static void
+http_write_app_headers (http_conn_t *hc, http_msg_t *msg, u8 **tx_buf)
+{
+ http_main_t *hm = &http_main;
+ session_t *as;
+ u8 *app_headers, *p, *end;
+ u32 *tmp;
+ int rv;
+
+ as = session_get_from_handle (hc->h_pa_session_handle);
+
+ /* read app header list */
+ if (msg->data.type == HTTP_MSG_DATA_PTR)
+ {
+ uword app_headers_ptr;
+ rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr),
+ (u8 *) &app_headers_ptr);
+ ASSERT (rv == sizeof (app_headers_ptr));
+ app_headers = uword_to_pointer (app_headers_ptr, u8 *);
+ }
+ else
+ {
+ app_headers = hm->app_header_lists[hc->c_thread_index];
+ rv = svm_fifo_dequeue (as->tx_fifo, msg->data.headers_len, app_headers);
+ ASSERT (rv == msg->data.headers_len);
+ }
+
+ /* serialize app headers to tx_buf */
+ end = app_headers + msg->data.headers_len;
+ while (app_headers < end)
+ {
+ /* custom header name? */
+ tmp = (u32 *) app_headers;
+ if (PREDICT_FALSE (*tmp & HTTP_CUSTOM_HEADER_NAME_BIT))
+ {
+ http_custom_token_t *name, *value;
+ name = (http_custom_token_t *) app_headers;
+ u32 name_len = name->len & ~HTTP_CUSTOM_HEADER_NAME_BIT;
+ app_headers += sizeof (http_custom_token_t) + name_len;
+ value = (http_custom_token_t *) app_headers;
+ app_headers += sizeof (http_custom_token_t) + value->len;
+ vec_add2 (*tx_buf, p, name_len + value->len + 4);
+ clib_memcpy (p, name->token, name_len);
+ p += name_len;
+ *p++ = ':';
+ *p++ = ' ';
+ clib_memcpy (p, value->token, value->len);
+ p += value->len;
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+ else
+ {
+ http_app_header_t *header;
+ header = (http_app_header_t *) app_headers;
+ app_headers += sizeof (http_app_header_t) + header->value.len;
+ http_token_t name = { http_header_name_token (header->name) };
+ vec_add2 (*tx_buf, p, name.len + header->value.len + 4);
+ clib_memcpy (p, name.base, name.len);
+ p += name.len;
+ *p++ = ':';
+ *p++ = ' ';
+ clib_memcpy (p, header->value.token, header->value.len);
+ p += header->value.len;
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+ }
+}
+
static http_sm_result_t
http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
{
@@ -1407,6 +1475,8 @@ http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
return HTTP_SM_ERROR;
}
+ response = hm->tx_bufs[hc->c_thread_index];
+ vec_reset_length (response);
/*
* Add "protocol layer" headers:
* - current time
@@ -1414,11 +1484,12 @@ http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
* - data length
*/
now = clib_timebase_now (&hm->timebase);
- response = format (0, http_response_template, http_status_code_str[msg.code],
- /* Date */
- format_clib_timebase_time, now,
- /* Server */
- hc->app_name);
+ response =
+ format (response, http_response_template, http_status_code_str[msg.code],
+ /* Date */
+ format_clib_timebase_time, now,
+ /* Server */
+ hc->app_name);
/* RFC9110 8.6: A server MUST NOT send Content-Length header field in a
* 2xx (Successful) response to CONNECT or with a status code of 101
@@ -1449,28 +1520,10 @@ http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
if (msg.data.headers_len)
{
HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len);
- if (msg.data.type == HTTP_MSG_DATA_PTR)
- {
- uword app_headers_ptr;
- rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr),
- (u8 *) &app_headers_ptr);
- ASSERT (rv == sizeof (app_headers_ptr));
- vec_append (response, uword_to_pointer (app_headers_ptr, u8 *));
- }
- else
- {
- u32 orig_len = vec_len (response);
- vec_resize (response, msg.data.headers_len);
- u8 *p = response + orig_len;
- rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p);
- ASSERT (rv == msg.data.headers_len);
- }
- }
- else
- {
- /* No headers from app */
- response = format (response, "\r\n");
+ http_write_app_headers (hc, &msg, &response);
}
+ /* Add empty line after headers */
+ response = format (response, "\r\n");
HTTP_DBG (3, "%v", response);
sent = http_send_data (hc, response, vec_len (response));
@@ -1478,10 +1531,8 @@ http_req_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
{
clib_warning ("sending status-line and headers failed!");
sc = HTTP_STATUS_INTERNAL_ERROR;
- vec_free (response);
goto error;
}
- vec_free (response);
if (msg.data.body_len)
{
@@ -1513,6 +1564,7 @@ error:
static http_sm_result_t
http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
{
+ http_main_t *hm = &http_main;
http_msg_t msg;
session_t *as;
u8 *target_buff = 0, *request = 0, *target;
@@ -1556,6 +1608,8 @@ http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
target = target_buff;
}
+ request = hm->tx_bufs[hc->c_thread_index];
+ vec_reset_length (request);
/* currently we support only GET and POST method */
if (msg.method_type == HTTP_REQ_GET)
{
@@ -1569,15 +1623,13 @@ http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
* - host
* - user agent
*/
- request = format (0, http_get_request_template,
+ request = format (request, http_get_request_template,
/* target */
target,
/* Host */
hc->host,
/* User-Agent */
- hc->app_name,
- /* Any headers from app? */
- msg.data.headers_len ? "" : "\r\n");
+ hc->app_name);
next_state = HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY;
sm_result = HTTP_SM_STOP;
@@ -1595,7 +1647,7 @@ http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
* - user agent
* - content length
*/
- request = format (0, http_post_request_template,
+ request = format (request, http_post_request_template,
/* target */
target,
/* Host */
@@ -1603,9 +1655,7 @@ http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
/* User-Agent */
hc->app_name,
/* Content-Length */
- msg.data.body_len,
- /* Any headers from app? */
- msg.data.headers_len ? "" : "\r\n");
+ msg.data.body_len);
http_buffer_init (&hc->req.tx_buf, msg_to_buf_type[msg.data.type],
as->tx_fifo, msg.data.body_len);
@@ -1623,23 +1673,10 @@ http_req_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
if (msg.data.headers_len)
{
HTTP_DBG (0, "got headers from app, len %d", msg.data.headers_len);
- if (msg.data.type == HTTP_MSG_DATA_PTR)
- {
- uword app_headers_ptr;
- rv = svm_fifo_dequeue (as->tx_fifo, sizeof (app_headers_ptr),
- (u8 *) &app_headers_ptr);
- ASSERT (rv == sizeof (app_headers_ptr));
- vec_append (request, uword_to_pointer (app_headers_ptr, u8 *));
- }
- else
- {
- u32 orig_len = vec_len (request);
- vec_resize (request, msg.data.headers_len);
- u8 *p = request + orig_len;
- rv = svm_fifo_dequeue (as->tx_fifo, msg.data.headers_len, p);
- ASSERT (rv == msg.data.headers_len);
- }
+ http_write_app_headers (hc, &msg, &request);
}
+ /* Add empty line after headers */
+ request = format (request, "\r\n");
HTTP_DBG (3, "%v", request);
sent = http_send_data (hc, request, vec_len (request));
@@ -1661,7 +1698,6 @@ error:
done:
vec_free (target_buff);
- vec_free (request);
return sm_result;
}
@@ -2297,6 +2333,7 @@ http_transport_enable (vlib_main_t *vm, u8 is_en)
vec_validate (hm->wrk, num_threads - 1);
vec_validate (hm->rx_bufs, num_threads - 1);
vec_validate (hm->tx_bufs, num_threads - 1);
+ vec_validate (hm->app_header_lists, num_threads - 1);
for (i = 0; i < num_threads; i++)
{
vec_validate (hm->rx_bufs[i],
@@ -2305,6 +2342,7 @@ http_transport_enable (vlib_main_t *vm, u8 is_en)
vec_validate (hm->tx_bufs[i],
HTTP_UDP_PAYLOAD_MAX_LEN +
HTTP_UDP_PROXY_DATAGRAM_CAPSULE_OVERHEAD);
+ vec_validate (hm->app_header_lists[i], 32 << 10);
}
clib_timebase_init (&hm->timebase, 0 /* GMT */, CLIB_TIMEBASE_DAYLIGHT_NONE,
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h
index 637452e0474..d61ac0b08c7 100644
--- a/src/plugins/http/http.h
+++ b/src/plugins/http/http.h
@@ -525,6 +525,7 @@ typedef struct http_main_
u8 **rx_bufs;
u8 **tx_bufs;
+ u8 **app_header_lists;
clib_timebase_t timebase;
@@ -1135,65 +1136,72 @@ http_get_header (http_header_table_t *header_table, const char *name,
return 0;
}
-/**
- * Add header to the list.
- *
- * @param headers Header list.
- * @param name Pointer to header's name buffer.
- * @param name_len Length of the name.
- * @param value Pointer to header's value buffer.
- * @param value_len Length of the value.
- *
- * @note Headers added at protocol layer: Date, Server, Content-Length
- */
+typedef struct
+{
+ u32 len; /**< length of the header data buffer */
+ u32 tail_offset; /**< current tail in header data */
+ u8 *buf; /**< start of header data */
+} http_headers_ctx_t;
+
+typedef struct
+{
+ u32 len;
+ u8 token[0];
+} http_custom_token_t;
+
+typedef struct
+{
+ u32 name;
+ http_custom_token_t value;
+} http_app_header_t;
+
+/* Use high bit of header name length as custom header name bit. */
+#define HTTP_CUSTOM_HEADER_NAME_BIT (1 << 31)
+
always_inline void
-http_add_header (http_header_t **headers, const char *name, uword name_len,
- const char *value, uword value_len)
+http_init_headers_ctx (http_headers_ctx_t *ctx, u8 *buf, u32 len)
{
- http_header_t *header;
- vec_add2 (*headers, header, 1);
- header->name.base = (char *) name;
- header->name.len = name_len;
- header->value.base = (char *) value;
- header->value.len = value_len;
+ ctx->len = len;
+ ctx->tail_offset = 0;
+ ctx->buf = buf;
}
-/**
- * Serialize the header list.
- *
- * @param headers Header list to serialize.
- *
- * @return New vector with serialized headers.
- *
- * The caller is always responsible to free the returned vector.
- */
-always_inline u8 *
-http_serialize_headers (http_header_t *headers)
+always_inline void
+http_add_header (http_headers_ctx_t *ctx, http_header_name_t name,
+ const char *value, uword value_len)
{
- u8 *headers_buf = 0, *dst;
- u32 headers_buf_len = 2;
- http_header_t *header;
+ http_app_header_t *header;
- vec_foreach (header, headers)
- headers_buf_len += header->name.len + header->value.len + 4;
+ ASSERT ((ctx->tail_offset + sizeof (http_app_header_t) + value_len) <
+ ctx->len);
- vec_validate (headers_buf, headers_buf_len - 1);
- dst = headers_buf;
+ header = (http_app_header_t *) (ctx->buf + ctx->tail_offset);
+ header->name = (u32) name;
+ header->value.len = (u32) value_len;
+ clib_memcpy (header->value.token, (u8 *) value, value_len);
+ ctx->tail_offset += sizeof (http_app_header_t) + value_len;
+}
- vec_foreach (header, headers)
- {
- clib_memcpy (dst, header->name.base, header->name.len);
- dst += header->name.len;
- *dst++ = ':';
- *dst++ = ' ';
- clib_memcpy (dst, header->value.base, header->value.len);
- dst += header->value.len;
- *dst++ = '\r';
- *dst++ = '\n';
- }
- *dst++ = '\r';
- *dst = '\n';
- return headers_buf;
+always_inline void
+http_add_custom_header (http_headers_ctx_t *ctx, const char *name,
+ uword name_len, const char *value, uword value_len)
+{
+ http_custom_token_t *token;
+
+ ASSERT ((ctx->tail_offset + 2 * sizeof (http_custom_token_t) + name_len +
+ value_len) < ctx->len);
+
+ /* name */
+ token = (http_custom_token_t *) (ctx->buf + ctx->tail_offset);
+ token->len = (u32) name_len;
+ clib_memcpy (token->token, (u8 *) name, token->len);
+ token->len |= HTTP_CUSTOM_HEADER_NAME_BIT;
+ ctx->tail_offset += sizeof (http_custom_token_t) + name_len;
+ /* value */
+ token = (http_custom_token_t *) (ctx->buf + ctx->tail_offset);
+ token->len = (u32) value_len;
+ clib_memcpy (token->token, (u8 *) value, token->len);
+ ctx->tail_offset += sizeof (http_custom_token_t) + value_len;
}
typedef enum http_uri_host_type_
diff --git a/src/plugins/http/http_plugin.rst b/src/plugins/http/http_plugin.rst
index 55c5afc3a2d..995e55e6f0f 100644
--- a/src/plugins/http/http_plugin.rst
+++ b/src/plugins/http/http_plugin.rst
@@ -16,10 +16,10 @@ Usage
The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``,
``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_build_header_table``, ``http_get_header``,
-``http_reset_header_table``, ``http_free_header_table``, ``http_add_header``, ``http_validate_target_syntax``,
-``http_serialize_headers``, ``http_parse_authority``, ``http_serialize_authority``, ``http_parse_masque_host_port``,
-``http_decap_udp_payload_datagram``, ``http_encap_udp_payload_datagram``. ``http_token_is``, ``http_token_is_case``,
-``http_token_contains``
+``http_reset_header_table``, ``http_free_header_table``, ``http_init_headers_ctx``, ``http_add_header``,
+``http_add_custom_header``, ``http_validate_target_syntax``, ``http_parse_authority``, ``http_serialize_authority``,
+``http_parse_masque_host_port``, ``http_decap_udp_payload_datagram``, ``http_encap_udp_payload_datagram``,
+``http_token_is``, ``http_token_is_case``, ``http_token_contains``
It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications.
@@ -253,10 +253,9 @@ Application should set following items:
* header section offset and length
* body offset and length
-Application could pass headers back to HTTP layer. Header list is created dynamically as vector of ``http_header_t``,
-where we store only pointers to buffers (zero copy).
-Well known header names are predefined.
-The list is serialized just before you send buffer to HTTP layer.
+Application could pass headers back to HTTP layer. Header list is created dynamically using ``http_headers_ctx_t``, which must be initialized with preallocated buffer.
+Well known header names are predefined and are added using ``http_add_header``, for headers with custom names use ``http_add_custom_header``.
+Header list buffer is sent buffer to HTTP layer in raw, current length is stored ``tail_offset`` member of ``http_headers_ctx_t``.
.. note::
Following headers are added at protocol layer and **MUST NOT** be set by application: Date, Server, Content-Length, Connection, Upgrade
@@ -268,18 +267,20 @@ Following example shows how to create headers section:
#include <http/http.h>
#include <http/http_header_names.h>
#include <http/http_content_types.h>
- http_header_t *resp_headers = 0;
+ http_headers_ctx_t resp_headers;
u8 *headers_buf = 0;
- http_add_header (resp_headers,
- http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ /* allocate buffer for response header list */
+ vec_validate (headers_buf, 1023);
+ /* initialize header list context */
+ http_init_headers_ctx (&resp_headers, headers_buf, vec_len (headers_buf));
+ /* add headers to the list */
+ http_add_header (&resp_headers, HTTP_HEADER_CONTENT_TYPE,
http_content_type_token (HTTP_CONTENT_TEXT_HTML));
- http_add_header (resp_headers,
- http_header_name_token (HTTP_HEADER_CACHE_CONTROL),
+ http_add_header (&resp_headers, HTTP_HEADER_CACHE_CONTROL,
http_token_lit ("max-age=600"));
- http_add_header (resp_headers,
- http_header_name_token (HTTP_HEADER_LOCATION),
- (const char *) redirect, vec_len (redirect));
- headers_buf = http_serialize_headers (resp_headers);
+ http_add_custom_header (&resp_headers,
+ http_token_lit ("X-Frame-Options"),
+ (const char *) x_frame_opt, vec_len (x_frame_opt));
The example below show how to create and send response HTTP message metadata:
@@ -289,7 +290,7 @@ The example below show how to create and send response HTTP message metadata:
msg.type = HTTP_MSG_REPLY;
msg.code = HTTP_STATUS_MOVED
msg.data.headers_offset = 0;
- msg.data.headers_len = vec_len (headers_buf);
+ msg.data.headers_len = resp_headers.tail_offset;
msg.data.type = HTTP_MSG_DATA_INLINE;
msg.data.body_len = vec_len (tx_buf);
msg.data.body_offset = msg.data.headers_len;
@@ -298,11 +299,11 @@ The example below show how to create and send response HTTP message metadata:
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
-Next you will send your serialized headers:
+Next you will send your headers:
.. code-block:: C
- rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf);
+ rv = svm_fifo_enqueue (ts->tx_fifo, msg.data.headers_len, headers_buf);
ASSERT (rv == msg.data.headers_len);
vec_free (headers_buf);
@@ -377,13 +378,12 @@ The example below shows how to create headers section:
#include <http/http.h>
#include <http/http_header_names.h>
#include <http/http_content_types.h>
- http_header_t *req_headers = 0;
+ http_headers_ctx_t *req_headers;
u8 *headers_buf = 0;
- http_add_header (req_headers,
- http_header_name_token (HTTP_HEADER_ACCEPT),
+ vec_validate (headers_buf, 63);
+ http_init_headers_ctx (&eq_headers, headers_buf, vec_len (headers_buf));
+ http_add_header (req_headers, HTTP_HEADER_ACCEPT,
http_content_type_token (HTTP_CONTENT_TEXT_HTML));
- headers_buf = http_serialize_headers (req_headers);
- vec_free (hs->req_headers);
Following example shows how to set message metadata:
@@ -398,7 +398,7 @@ Following example shows how to set message metadata:
msg.data.target_path_len = vec_len (target);
/* custom headers */
msg.data.headers_offset = msg.data.target_path_len;
- msg.data.headers_len = vec_len (headers_buf);
+ msg.data.headers_len = headers.tail_offset;
/* no request body because we are doing GET request */
msg.data.body_len = 0;
/* data type and total length */
@@ -411,7 +411,7 @@ Finally application sends everything to HTTP layer:
svm_fifo_seg_t segs[3] = { { (u8 *) &msg, sizeof (msg) }, /* message metadata */
{ target, vec_len (target) }, /* request target */
- { headers_buf, vec_len (headers_buf) } }; /* serialized headers */
+ { headers_buf, msg.data.headers_len } }; /* headers */
rv = svm_fifo_enqueue_segments (as->tx_fifo, segs, 3, 0 /* allow partial */);
vec_free (headers_buf);
if (rv < 0 || rv != sizeof (msg) + msg.data.len)