aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/http/http.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/http/http.h')
-rw-r--r--src/plugins/http/http.h282
1 files changed, 211 insertions, 71 deletions
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h
index 844235cbdbb..7405d3d3bf7 100644
--- a/src/plugins/http/http.h
+++ b/src/plugins/http/http.h
@@ -352,12 +352,36 @@ typedef enum http_header_name_
#undef _
} http_header_name_t;
+#define HTTP_BOOLEAN_TRUE "?1"
+
+#define foreach_http_upgrade_proto \
+ _ (CONNECT_UDP, "connect-udp") \
+ _ (CONNECT_IP, "connect-ip") \
+ _ (WEBSOCKET, "websocket")
+
+typedef enum http_upgrade_proto_
+{
+ HTTP_UPGRADE_PROTO_NA =
+ 0, /* indicating standard CONNECT where protocol is omitted */
+#define _(sym, str) HTTP_UPGRADE_PROTO_##sym,
+ foreach_http_upgrade_proto
+#undef _
+} http_upgrade_proto_t;
+
typedef enum http_msg_data_type_
{
HTTP_MSG_DATA_INLINE,
HTTP_MSG_DATA_PTR
} http_msg_data_type_t;
+typedef struct http_field_line_
+{
+ u32 name_offset;
+ u32 name_len;
+ u32 value_offset;
+ u32 value_len;
+} http_field_line_t;
+
typedef struct http_msg_data_
{
http_msg_data_type_t type;
@@ -371,6 +395,8 @@ typedef struct http_msg_data_
u32 headers_len;
u32 body_offset;
u64 body_len;
+ uword headers_ctx;
+ http_upgrade_proto_t upgrade_proto;
u8 data[0];
} http_msg_data_t;
@@ -422,6 +448,13 @@ typedef struct http_req_
u32 body_offset;
u64 body_len;
+
+ http_field_line_t *headers;
+ uword content_len_header_index;
+ uword connection_header_index;
+ uword upgrade_header_index;
+
+ http_upgrade_proto_t upgrade_proto;
} http_req_t;
typedef struct http_tc_
@@ -477,6 +510,27 @@ typedef struct http_main_
u32 fifo_size;
} http_main_t;
+always_inline u8 *
+format_http_bytes (u8 *s, va_list *va)
+{
+ u8 *bytes = va_arg (*va, u8 *);
+ int n_bytes = va_arg (*va, int);
+ uword i;
+
+ if (n_bytes == 0)
+ return s;
+
+ for (i = 0; i < n_bytes; i++)
+ {
+ if (isprint (bytes[i]))
+ s = format (s, "%c", bytes[i]);
+ else
+ s = format (s, "\\x%02x", bytes[i]);
+ }
+
+ return s;
+}
+
always_inline int
_validate_target_syntax (u8 *target, u32 len, int is_query, int *is_encoded)
{
@@ -767,22 +821,94 @@ _parse_field_value (u8 **pos, u8 *end, u8 **field_value_start,
typedef struct
{
- u8 *name;
- u8 *value;
-} http_header_ht_t;
-
-typedef struct
-{
http_token_t name;
http_token_t value;
} http_header_t;
typedef struct
{
- http_header_ht_t *headers;
+ http_header_t *headers;
uword *value_by_name;
+ u8 *buf;
+ char **concatenated_values;
} http_header_table_t;
+#define HTTP_HEADER_TABLE_NULL \
+ { \
+ .headers = 0, .value_by_name = 0, .buf = 0, .concatenated_values = 0, \
+ }
+
+always_inline u8
+http_token_is (const char *actual, uword actual_len, const char *expected,
+ uword expected_len)
+{
+ ASSERT (actual != 0);
+ if (actual_len != expected_len)
+ return 0;
+ return memcmp (actual, expected, expected_len) == 0 ? 1 : 0;
+}
+
+always_inline u8
+http_token_is_case (const char *actual, uword actual_len, const char *expected,
+ uword expected_len)
+{
+ uword i;
+ ASSERT (actual != 0);
+ if (actual_len != expected_len)
+ return 0;
+ for (i = 0; i < expected_len; i++)
+ {
+ if (tolower (actual[i]) != expected[i])
+ return 0;
+ }
+ return 1;
+}
+
+always_inline u8
+http_token_contains (const char *haystack, uword haystack_len,
+ const char *needle, uword needle_len)
+{
+ uword end_index, i;
+ ASSERT (haystack != 0);
+ if (haystack_len < needle_len)
+ return 0;
+ end_index = haystack_len - needle_len;
+ for (i = 0; i <= end_index; i++)
+ {
+ if (!memcmp (haystack + i, needle, needle_len))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Reset header table before reuse.
+ *
+ * @param ht Header table to reset.
+ */
+always_inline void
+http_reset_header_table (http_header_table_t *ht)
+{
+ int i;
+ for (i = 0; i < vec_len (ht->concatenated_values); i++)
+ vec_free (ht->concatenated_values[i]);
+ vec_reset_length (ht->concatenated_values);
+ vec_reset_length (ht->headers);
+ vec_reset_length (ht->buf);
+ hash_free (ht->value_by_name);
+}
+
+/**
+ * Initialize header table input buffer.
+ * @param ht Header table.
+ * @param msg HTTP transport message metadata.
+ */
+always_inline void
+http_init_header_table_buf (http_header_table_t *ht, http_msg_t msg)
+{
+ vec_validate (ht->buf, msg.data.headers_len - 1);
+}
+
/**
* Free header table's memory.
*
@@ -791,86 +917,87 @@ typedef struct
always_inline void
http_free_header_table (http_header_table_t *ht)
{
- http_header_ht_t *header;
- vec_foreach (header, ht->headers)
- {
- vec_free (header->name);
- vec_free (header->value);
- }
+ int i;
+ for (i = 0; i < vec_len (ht->concatenated_values); i++)
+ vec_free (ht->concatenated_values[i]);
+ vec_free (ht->concatenated_values);
vec_free (ht->headers);
+ vec_free (ht->buf);
hash_free (ht->value_by_name);
- clib_mem_free (ht);
+}
+
+static uword
+_http_ht_hash_key_sum (hash_t *h, uword key)
+{
+ http_token_t *name = uword_to_pointer (key, http_token_t *);
+ return hash_memory (name->base, name->len, 0);
+}
+
+static uword
+_http_ht_hash_key_equal (hash_t *h, uword key1, uword key2)
+{
+ http_token_t *name1 = uword_to_pointer (key1, http_token_t *);
+ http_token_t *name2 = uword_to_pointer (key2, http_token_t *);
+ return name1 && name2 &&
+ http_token_is (name1->base, name1->len, name2->base, name2->len);
}
/**
- * Parse headers in given vector.
+ * Build header table.
*
- * @param headers Vector to parse.
- * @param [out] header_table Parsed headers in case of success.
- *
- * @return @c 0 on success.
+ * @param header_table Header table with loaded buffer.
+ * @param msg HTTP transport message metadata.
*
- * The caller is responsible to free the returned @c header_table
- * using @c http_free_header_table .
+ * @note If reusing already allocated header table use
+ * @c http_reset_header_table first.
*/
-always_inline int
-http_parse_headers (u8 *headers, http_header_table_t **header_table)
+always_inline void
+http_build_header_table (http_header_table_t *ht, http_msg_t msg)
{
- u8 *pos, *end, *name_start, *value_start, *name;
- u32 name_len, value_len;
- int rv;
- http_header_ht_t *header;
- http_header_table_t *ht;
+ http_token_t name;
+ http_header_t *header;
+ http_field_line_t *field_lines, *field_line;
uword *p;
- end = headers + vec_len (headers);
- pos = headers;
+ ASSERT (ht);
+ field_lines = uword_to_pointer (msg.data.headers_ctx, http_field_line_t *);
+ ht->value_by_name =
+ hash_create2 (0, 0, sizeof (uword), _http_ht_hash_key_sum,
+ _http_ht_hash_key_equal, 0, 0);
- ht = clib_mem_alloc (sizeof (*ht));
- ht->value_by_name = hash_create_string (0, sizeof (uword));
- ht->headers = 0;
- do
+ vec_foreach (field_line, field_lines)
{
- rv = _parse_field_name (&pos, end, &name_start, &name_len);
- if (rv != 0)
- {
- http_free_header_table (ht);
- return rv;
- }
- rv = _parse_field_value (&pos, end, &value_start, &value_len);
- if (rv != 0)
- {
- http_free_header_table (ht);
- return rv;
- }
- name = vec_new (u8, name_len);
- clib_memcpy (name, name_start, name_len);
- vec_terminate_c_string (name);
+ name.base = (char *) (ht->buf + field_line->name_offset);
+ name.len = field_line->name_len;
/* check if header is repeated */
- p = hash_get_mem (ht->value_by_name, name);
+ p = hash_get_mem (ht->value_by_name, &name);
if (p)
{
- /* if yes combine values */
+ char *new_value = 0;
header = vec_elt_at_index (ht->headers, p[0]);
- vec_pop (header->value); /* drop null byte */
- header->value = format (header->value, ", %U%c", format_ascii_bytes,
- value_start, value_len, 0);
- vec_free (name);
+ u32 new_len = header->value.len + field_line->value_len + 2;
+ vec_validate (new_value, new_len - 1);
+ clib_memcpy (new_value, header->value.base, header->value.len);
+ new_value[header->value.len] = ',';
+ new_value[header->value.len + 1] = ' ';
+ clib_memcpy (new_value + header->value.len + 2,
+ ht->buf + field_line->value_offset,
+ field_line->value_len);
+ vec_add1 (ht->concatenated_values, new_value);
+ header->value.base = new_value;
+ header->value.len = new_len;
continue;
}
/* or create new record */
- vec_add2 (ht->headers, header, sizeof (*header));
- header->name = name;
- header->value = vec_new (u8, value_len);
- clib_memcpy (header->value, value_start, value_len);
- vec_terminate_c_string (header->value);
- hash_set_mem (ht->value_by_name, header->name, header - ht->headers);
+ vec_add2 (ht->headers, header, 1);
+ header->name.base = name.base;
+ header->name.len = name.len;
+ header->value.base = (char *) (ht->buf + field_line->value_offset);
+ header->value.len = field_line->value_len;
+ HTTP_DBG (1, "value: %U", format_http_bytes, header->value.base,
+ header->value.len);
+ hash_set_mem (ht->value_by_name, &header->name, header - ht->headers);
}
- while (pos != end);
-
- *header_table = ht;
-
- return 0;
}
/**
@@ -881,17 +1008,19 @@ http_parse_headers (u8 *headers, http_header_table_t **header_table)
*
* @return Header's value in case of success, @c 0 otherwise.
*/
-always_inline const char *
-http_get_header (http_header_table_t *header_table, const char *name)
+always_inline const http_header_t *
+http_get_header (http_header_table_t *header_table, const char *name,
+ uword name_len)
{
uword *p;
- http_header_ht_t *header;
+ http_header_t *header;
+ http_token_t name_token = { (char *) name, name_len };
- p = hash_get_mem (header_table->value_by_name, name);
+ p = hash_get_mem (header_table->value_by_name, &name_token);
if (p)
{
header = vec_elt_at_index (header_table->headers, p[0]);
- return (const char *) header->value;
+ return header;
}
return 0;
@@ -1340,6 +1469,12 @@ _http_parse_capsule (u8 *data, u64 len, u64 *type, u8 *value_offset,
return -1;
}
+ if (p == end)
+ {
+ clib_warning ("capsule length missing");
+ return -1;
+ }
+
capsule_value_len = _http_decode_varint (&p, end);
if (capsule_value_len == HTTP_INVALID_VARINT)
{
@@ -1389,6 +1524,11 @@ http_decap_udp_payload_datagram (u8 *data, u64 len, u8 *payload_offset,
}
p += value_offset;
+ if (p == end)
+ {
+ clib_warning ("context ID missing");
+ return -1;
+ }
/* context ID field should be zero (RFC9298 section 4) */
context_id = _http_decode_varint (&p, end);