summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluwenpeng <[email protected]>2022-08-17 18:08:33 +0800
committerluwenpeng <[email protected]>2022-08-19 16:46:15 +0800
commit9cfa120ae79cfd429f1b77eb8ccb6036651e7233 (patch)
tree84bc425330833a790a9d035c9586e035a73eac2d
parentb9d93e042b383f4ed683008662e99efa562ba017 (diff)
feature: http decoder
* support parser uncompress request/response * define public API interface
-rw-r--r--sdk/include/http.h176
-rw-r--r--src/main.cpp2
-rw-r--r--src/plugin_manager/plugin_manager.cpp8
-rw-r--r--src/protocol_decoder/http/CMakeLists.txt15
-rw-r--r--src/protocol_decoder/http/http.cpp21
-rw-r--r--src/protocol_decoder/http/http_decoder.cpp506
-rw-r--r--src/protocol_decoder/http/http_decoder.h81
-rw-r--r--src/protocol_decoder/http/http_decoder_rstring.cpp179
-rw-r--r--src/protocol_decoder/http/http_decoder_rstring.h90
-rw-r--r--src/protocol_decoder/http/http_decoder_table.cpp344
-rw-r--r--src/protocol_decoder/http/http_decoder_table.h45
-rw-r--r--src/protocol_decoder/http/http_decoder_util.cpp16
-rw-r--r--src/protocol_decoder/http/http_decoder_util.h61
-rw-r--r--src/protocol_decoder/http/test/CMakeLists.txt38
-rw-r--r--src/protocol_decoder/http/test/http_decoder_rstring_test.cpp328
-rw-r--r--src/protocol_decoder/http/test/http_decoder_table_test.cpp431
-rw-r--r--src/protocol_decoder/http/test/http_decoder_test.cpp614
-rw-r--r--vendor/CMakeLists.txt16
-rw-r--r--vendor/llhttp-release-v6.0.9.tar.gzbin0 -> 41388 bytes
19 files changed, 2958 insertions, 13 deletions
diff --git a/sdk/include/http.h b/sdk/include/http.h
index e364ca0..b98a244 100644
--- a/sdk/include/http.h
+++ b/sdk/include/http.h
@@ -1,14 +1,184 @@
-#pragma once
+#ifndef _HTTP_H
+#define _HTTP_H
#ifdef __cpluscplus
extern "C"
{
#endif
+#include <stddef.h>
+
#include "session.h"
-void http_decoder(const struct stellar_session *session, enum session_event_type event, const char *payload, size_t len, void **ctx);
+/******************************************************************************
+ * Public API: For build in
+ ******************************************************************************/
+
+void http_entry(const struct stellar_session *session, enum session_event_type event, const char *payload, size_t len, void **ctx);
+
+/******************************************************************************
+ * Public API: For http consumer
+ ******************************************************************************/
+
+enum http_event
+{
+ HTTP_EVENT_NONE = 0x0,
+
+ HTTP_EVENT_REQ_LINE = 0x01,
+ HTTP_EVENT_REQ_HDR = 0x02,
+ HTTP_EVENT_REQ_BODY = 0x04,
+
+ HTTP_EVENT_RESP_LINE = 0x08,
+ HTTP_EVENT_RESP_HDR = 0x10,
+ HTTP_EVENT_RESP_BODY = 0x20,
+};
+
+struct http_request_line
+{
+ char *method;
+ size_t method_len;
+
+ char *uri;
+ size_t uri_len;
+
+ int major_version;
+ int minor_version;
+};
+
+struct http_status_line
+{
+ char *status;
+ size_t status_len;
+
+ int status_code;
+
+ int major_version;
+ int minor_version;
+};
+
+struct http_decoder *http_session_get_decoder(const struct stellar_session *http_session);
+enum http_event http_decoder_get_event(struct http_decoder *decoder);
+
+/*
+ * The data fetched by the http_decoder_fetch_xxx() API
+ * is only available during the lifetime of the current packet.
+ */
+void http_decoder_fetch_request_line(struct http_decoder *decoder, struct http_request_line *request_line);
+void http_decoder_fetch_status_line(struct http_decoder *decoder, struct http_status_line *status_line);
+
+void http_decoder_fetch_body(struct http_decoder *decoder, char **ptr, size_t *len);
+void http_decoder_fetch_next_header(struct http_decoder *decoder, int *iter_index, char **filed_ptr, size_t *filed_len, char **value_ptr, size_t *value_len);
+
+/******************************************************************************
+ * Example: How to implement http consumer
+ ******************************************************************************/
+/*
+void http_consumer_entry_example(const struct stellar_session *http_session, enum session_event_type event, const char *payload, size_t len, void **ctx)
+{
+ if (event & SESSION_EVENT_OPENING)
+ {
+ // malloc ctx
+ }
+
+ if (event & SESSION_EVENT_META)
+ {
+ struct http_decoder *decoder = http_decoder_get(http_session);
+ enum http_event ready_event = http_decoder_ready_event(decoder);
+
+ if (ready_event & HTTP_EVENT_REQ_LINE)
+ {
+ struct http_request_line request_line;
+ http_decoder_fetch_request_line(decoder, &request_line);
+ }
+
+ if (ready_event & HTTP_EVENT_RESP_LINE)
+ {
+ struct http_status_line status_line;
+ http_decoder_fetch_status_line(decoder, &status_line);
+ }
+
+ if (ready_event & HTTP_EVENT_REQ_HDR)
+ {
+ int iter_index = 0;
+ char *filed_ptr = NULL;
+ char *value_ptr = NULL;
+
+ size_t filed_len = 0;
+ size_t value_len = 0;
+
+ do
+ {
+ http_decoder_fetch_next_header(decoder, iter_index, &filed_ptr, &filed_len, &value_ptr, &value_len);
+ if (filed_ptr)
+ {
+ if (strcmp("Host", filed_ptr, filed_len) == 0)
+ {
+ // get Host value : value_ptr
+ }
+
+ // ....
+
+ if (strcmp("User-Agent", filed_ptr, filed_len) == 0)
+ {
+ // get User-Agen value : value_ptr
+ }
+ }
+ } while (filed_ptr);
+ }
+
+ if (ready_event & HTTP_EVENT_RESP_HDR)
+ {
+ int iter_index = 0;
+ char *filed_ptr = NULL;
+ char *value_ptr = NULL;
+
+ size_t filed_len = 0;
+ size_t value_len = 0;
+
+ do
+ {
+ http_decoder_fetch_next_header(decoder, iter_index, &filed_ptr, &filed_len, &value_ptr, &value_len);
+ if (filed_ptr)
+ {
+ if (strcmp("Server", filed_ptr, filed_len) == 0)
+ {
+ // get Server value : value_ptr
+ }
+
+ // ....
+
+ if (strcmp("content-length", filed_ptr, filed_len) == 0)
+ {
+ // get content-length value : value_ptr
+ }
+ }
+ } while (filed_ptr);
+ }
+
+ if (ready_event & HTTP_EVENT_REQ_BODY)
+ {
+ char *req_body_ptr = NULL;
+ size_t req_body_len = 0;
+ http_decoder_fetch_body(decoder, req_body_ptr, req_body_len);
+ }
+
+ if (ready_event & HTTP_EVENT_RESP_BODY)
+ {
+ char *resp_body_ptr = NULL;
+ size_t resp_body_len = 0;
+ http_decoder_fetch_body(decoder, resp_body_ptr, resp_body_len);
+ }
+ }
+
+ if (event & SESSION_EVENT_CLOSING)
+ {
+ // free ctx
+ }
+}
+*/
#ifdef __cpluscplus
}
-#endif \ No newline at end of file
+#endif
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index 19c54d9..8d94824 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -84,7 +84,7 @@ int main(int argc, char ** argv)
struct plugin_manager *plug_mgr = plugin_manager_create();
// register build-in plugin
- plugin_manager_register(plug_mgr, "HTTP", SESSION_EVENT_ALL, http_decoder);
+ plugin_manager_register(plug_mgr, "HTTP", SESSION_EVENT_ALL, http_entry);
// load external plugins
char file_path[] = "./plugs/plugins.inf";
diff --git a/src/plugin_manager/plugin_manager.cpp b/src/plugin_manager/plugin_manager.cpp
index a35ee82..b12cf28 100644
--- a/src/plugin_manager/plugin_manager.cpp
+++ b/src/plugin_manager/plugin_manager.cpp
@@ -396,14 +396,14 @@ int plugin_manager_register(struct plugin_manager *plug_mgr, const char *session
void plugin_manager_dispatch(struct plugin_manager *plug_mgr, struct stellar_event *event)
{
- const struct stellar_session *seesion = stellar_event_get_session(event);
+ const struct stellar_session *session = stellar_event_get_session(event);
struct session_plugin_ctx *plug_ctx = stellar_event_get_plugin_ctx(event);
enum session_event_type event_type = stellar_event_get_type(event);
const char *session_name = stellar_event_get_session_name(event);
uint16_t payload_len = stellar_event_get_payload_length(event);
const char *payload = stellar_event_get_payload(event);
- assert(seesion);
+ assert(session);
assert(session_name);
char event_str_buffer[1024] = {0};
@@ -441,7 +441,7 @@ void plugin_manager_dispatch(struct plugin_manager *plug_mgr, struct stellar_eve
{
plug_ctx->callback_index = i;
plugin_manager_log(DEBUG, "dispatch, run event_cb: %p, plugin status: 'taken over', session: %s, event: (%d, %s)", runtime->event_cb, session_name, event_type, event_str_buffer);
- runtime->event_cb(seesion, SESSION_EVENT_CLOSING, payload, payload_len, &runtime->cb_args);
+ runtime->event_cb(session, SESSION_EVENT_CLOSING, payload, payload_len, &runtime->cb_args);
continue;
}
else
@@ -455,7 +455,7 @@ void plugin_manager_dispatch(struct plugin_manager *plug_mgr, struct stellar_eve
{
plug_ctx->callback_index = i;
plugin_manager_log(DEBUG, "dispatch, run event_cb: %p, plugin status: 'normal', session: %s, event: (%d, %s)", runtime->event_cb, session_name, event_type, event_str_buffer);
- runtime->event_cb(seesion, event_type, payload, payload_len, &runtime->cb_args);
+ runtime->event_cb(session, event_type, payload, payload_len, &runtime->cb_args);
runtime->is_be_called = 1;
}
else
diff --git a/src/protocol_decoder/http/CMakeLists.txt b/src/protocol_decoder/http/CMakeLists.txt
index dd8482f..b8c5d66 100644
--- a/src/protocol_decoder/http/CMakeLists.txt
+++ b/src/protocol_decoder/http/CMakeLists.txt
@@ -1,6 +1,15 @@
-
-
add_library(http
http.cpp
+ http_decoder_util.cpp
+ http_decoder_rstring.cpp
+ http_decoder_table.cpp
+ http_decoder.cpp
+)
+
+target_link_libraries(http
+ llhttp-static
)
-target_include_directories(http PUBLIC ${CMAKE_SOURCE_DIR}) \ No newline at end of file
+
+target_include_directories(http PUBLIC ${CMAKE_SOURCE_DIR})
+
+add_subdirectory(test) \ No newline at end of file
diff --git a/src/protocol_decoder/http/http.cpp b/src/protocol_decoder/http/http.cpp
index 14e431c..dc74ab0 100644
--- a/src/protocol_decoder/http/http.cpp
+++ b/src/protocol_decoder/http/http.cpp
@@ -1,12 +1,31 @@
#include <stddef.h>
#include "sdk/include/session.h"
+#include "sdk/include/http.h"
-void http_decoder(const struct stellar_session *session, enum session_event_type event, const char *payload, size_t len, void **ctx)
+void http_entry(const struct stellar_session *session, enum session_event_type event, const char *payload, size_t len, void **ctx)
{
struct stellar_session_event_extras *info = NULL;
struct stellar_session *new_session = session_manager_session_derive(session, "HTTP");
session_manager_trigger_event(new_session, SESSION_EVENT_OPENING, info);
session_manager_trigger_event(new_session, SESSION_EVENT_META, info);
+}
+
+struct http_decoder *http_session_get_decoder(const struct stellar_session *http_session)
+{
+ return NULL;
+}
+
+enum http_event http_decoder_get_event(struct http_decoder *decoder)
+{
+ return HTTP_EVENT_NONE;
+}
+
+void http_decoder_fetch_request_line(struct http_decoder *decoder, struct http_request_line *request_line)
+{
+}
+
+void http_decoder_fetch_status_line(struct http_decoder *decoder, struct http_status_line *status_line)
+{
} \ No newline at end of file
diff --git a/src/protocol_decoder/http/http_decoder.cpp b/src/protocol_decoder/http/http_decoder.cpp
new file mode 100644
index 0000000..8f7707f
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder.cpp
@@ -0,0 +1,506 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "llhttp.h"
+#include "http_decoder_util.h"
+#include "http_decoder_table.h"
+#include "http_decoder.h"
+
+struct http_decoder
+{
+ llhttp_t parser;
+ llhttp_settings_t settings;
+ enum http_decoder_status status;
+ struct http_decoder_table *table;
+ int commit_count;
+};
+
+/******************************************************************************
+ * Private API
+ ******************************************************************************/
+
+static void printf_debug_info(const char *desc, const char *at, size_t length)
+{
+ if (at)
+ {
+ char *temp = http_decoder_safe_dup(at, length);
+ printf("%s: %s\n", desc, temp);
+ http_decoder_safe_free(temp);
+ }
+ else
+ {
+ printf("%s\n", desc);
+ }
+}
+
+/* Possible return values 0, -1, `HPE_PAUSED` */
+static int on_message_begin(llhttp_t *http)
+{
+ printf_debug_info("on_message_begin", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ if (decoder->table == NULL)
+ {
+ decoder->table = http_decoder_table_create();
+ }
+ else
+ {
+ http_decoder_table_reset(decoder->table);
+ }
+ decoder->commit_count = 0;
+ decoder->status = ON_MESSAGE_BEGIN;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, `HPE_PAUSED` */
+static int on_message_complete(llhttp_t *http)
+{
+ printf_debug_info("on_message_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ enum rstring_status status = http_decoder_table_status(decoder->table, HTTP_ITERM_BODY);
+ if (status == RSTRING_STATUS_REFER || status == RSTRING_STATUS_CACHE)
+ {
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_BODY);
+ decoder->commit_count++;
+ }
+ decoder->status = ON_MESSAGE_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_uri(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_uri", at, length);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_add(decoder->table, HTTP_ITERM_URI, at, length);
+ decoder->status = ON_URI;
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_uri_complete(llhttp_t *http)
+{
+ printf_debug_info("on_uri_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_URI);
+ decoder->commit_count++;
+ decoder->status = ON_URI_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_status(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_status", at, length);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_add(decoder->table, HTTP_ITERM_STATUS, at, length);
+ decoder->status = ON_STATUS;
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_status_complete(llhttp_t *http)
+{
+ printf_debug_info("on_status_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_STATUS);
+ decoder->commit_count++;
+ decoder->status = ON_STATUS_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_header_field(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_header_field", at, length);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_add(decoder->table, HTTP_ITERM_HEADER_FILED, at, length);
+ decoder->status = ON_HEADER_FIELD;
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_header_field_complete(llhttp_t *http)
+{
+ printf_debug_info("on_header_field_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_HEADER_FILED);
+ decoder->commit_count++;
+ decoder->status = ON_HEADER_FIELD_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_header_value(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_header_value", at, length);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_add(decoder->table, HTTP_ITERM_HEADER_VALUE, at, length);
+ decoder->status = ON_HEADER_VALUE;
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_header_value_complete(llhttp_t *http)
+{
+ printf_debug_info("on_header_value_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_HEADER_VALUE);
+ decoder->commit_count++;
+ decoder->status = ON_HEADER_VALUE_COMPLETE;
+
+ return 0;
+}
+
+/* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ * Possible return values 0, -1, `HPE_PAUSED`
+ */
+static int on_chunk_header(llhttp_t *http)
+{
+ printf_debug_info("on_chunk_header", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ decoder->status = ON_CHUNK_HEADER;
+
+ return 0;
+}
+
+/* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ * Possible return values 0, -1, `HPE_PAUSED`
+ */
+static int on_chunk_header_complete(llhttp_t *http)
+{
+ printf_debug_info("on_chunk_header_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ decoder->status = ON_CHUNK_HEADER_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values:
+ * 0 - Proceed normally
+ * 1 - Assume that request/response has no body, and proceed to parsing the next message
+ * 2 - Assume absence of body (as above) and make `llhttp_execute()` return `HPE_PAUSED_UPGRADE`
+ * -1 - Error `HPE_PAUSED`
+ */
+static int on_headers_complete(llhttp_t *http)
+{
+ printf_debug_info("on_headers_complete", NULL, 0);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ decoder->status = ON_HEADERS_COMPLETE;
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_body(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_body", at, length);
+
+ struct http_decoder *decoder = container_of(http, struct http_decoder, parser);
+ http_decoder_table_add(decoder->table, HTTP_ITERM_BODY, at, length);
+ decoder->status = ON_BODY;
+
+ return 0;
+}
+
+/******************************************************************************
+ * Manipulate http decoder
+ ******************************************************************************/
+
+struct http_decoder *http_decoder_create()
+{
+ struct http_decoder *decoder = http_decoder_safe_alloc(struct http_decoder, 1);
+
+ return decoder;
+}
+
+void http_decoder_destory(struct http_decoder *decoder)
+{
+ if (decoder)
+ {
+ http_decoder_table_destory(decoder->table);
+ http_decoder_safe_free(decoder);
+ }
+}
+
+void http_decoder_init(struct http_decoder *decoder)
+{
+ assert(decoder);
+
+ enum llhttp_type type = HTTP_BOTH; // HTTP_BOTH | HTTP_REQUEST | HTTP_RESPONSE
+
+ llhttp_settings_init(&decoder->settings);
+ llhttp_init(&decoder->parser, type, &decoder->settings);
+
+ decoder->settings.on_message_begin = on_message_begin;
+ decoder->settings.on_message_complete = on_message_complete;
+
+ decoder->settings.on_url = on_uri;
+ decoder->settings.on_url_complete = on_uri_complete;
+
+ decoder->settings.on_status = on_status;
+ decoder->settings.on_status_complete = on_status_complete;
+
+ decoder->settings.on_header_field = on_header_field;
+ decoder->settings.on_header_field_complete = on_header_field_complete;
+
+ decoder->settings.on_header_value = on_header_value;
+ decoder->settings.on_header_value_complete = on_header_value_complete;
+
+ decoder->settings.on_chunk_header = on_chunk_header;
+ decoder->settings.on_chunk_complete = on_chunk_header_complete;
+
+ decoder->settings.on_headers_complete = on_headers_complete;
+
+ decoder->settings.on_body = on_body;
+
+ decoder->status = ON_INIT;
+}
+
+void http_decoder_reset(struct http_decoder *decoder)
+{
+ if (decoder)
+ {
+ http_decoder_init(decoder);
+ http_decoder_table_reset(decoder->table);
+ decoder->commit_count = 0;
+ }
+}
+
+/*
+ * return 0 : new data that needs to be consumed by upper layers has been parsed
+ * return -1 : no new data
+ * return -2 : error or not http protocol
+ */
+int http_decoder_dispatch(struct http_decoder *decoder, const char *data, size_t len)
+{
+ assert(decoder);
+
+ enum llhttp_errno err = llhttp_execute(&decoder->parser, data, len);
+ if (err != HPE_OK)
+ {
+ fprintf(stderr, "llhttp_execute parse error: %s %s\n", llhttp_errno_name(err), decoder->parser.reason);
+ return -2;
+ }
+
+ if (decoder->table == NULL)
+ {
+ return -1;
+ }
+
+ if (http_decoder_table_status(decoder->table, HTTP_ITERM_URI) == RSTRING_STATUS_REFER)
+ {
+ http_decoder_table_cache(decoder->table, HTTP_ITERM_URI);
+ }
+
+ if (http_decoder_table_status(decoder->table, HTTP_ITERM_STATUS) == RSTRING_STATUS_REFER)
+ {
+ http_decoder_table_cache(decoder->table, HTTP_ITERM_STATUS);
+ }
+
+ if (http_decoder_table_status(decoder->table, HTTP_ITERM_HEADER_FILED) == RSTRING_STATUS_REFER)
+ {
+ http_decoder_table_cache(decoder->table, HTTP_ITERM_HEADER_FILED);
+ }
+
+ if (http_decoder_table_status(decoder->table, HTTP_ITERM_HEADER_VALUE) == RSTRING_STATUS_REFER)
+ {
+ http_decoder_table_cache(decoder->table, HTTP_ITERM_HEADER_VALUE);
+ }
+
+ // do not cache incomplete http body, submit immediately every time
+ if (http_decoder_table_status(decoder->table, HTTP_ITERM_BODY) == RSTRING_STATUS_REFER ||
+ http_decoder_table_status(decoder->table, HTTP_ITERM_BODY) == RSTRING_STATUS_CACHE)
+
+ {
+ http_decoder_table_commit(decoder->table, HTTP_ITERM_BODY);
+ }
+
+ if (decoder->commit_count)
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// remove the data that has been consumed by the upper layer
+void http_decoder_remove(struct http_decoder *decoder)
+{
+ assert(decoder);
+
+ if (decoder->table)
+ {
+ http_decoder_table_remove(decoder->table);
+ }
+ decoder->commit_count = 0;
+}
+
+// for debug
+void http_decoder_dump(struct http_decoder *decoder)
+{
+ uint64_t content_length = decoder->parser.content_length;
+ uint8_t type = decoder->parser.type;
+ uint8_t method = decoder->parser.method;
+ uint8_t http_major = decoder->parser.http_major;
+ uint8_t http_minor = decoder->parser.http_minor;
+ uint8_t header_state = decoder->parser.header_state;
+ uint8_t lenient_flags = decoder->parser.lenient_flags;
+ uint8_t upgrade = decoder->parser.upgrade;
+ uint8_t finish = decoder->parser.finish;
+ uint16_t flags = decoder->parser.flags;
+ uint16_t status_code = decoder->parser.status_code;
+
+ char *method_str = (char *)llhttp_method_name((llhttp_method_t)method);
+
+ printf("\n=====================================================\n");
+ printf("content_length: %lu, type: %d, header_state: %d, lenient_flags: %d, upgrade: %d, finish: %d, flags: %d\n",
+ content_length, type, header_state, lenient_flags, upgrade, finish, flags);
+ printf("method: %d %s, http_major: %d, http_minor: %d, status_code: %d\n",
+ method, method_str, http_major, http_minor, status_code);
+
+ if (decoder->table)
+ {
+ http_decoder_table_dump(decoder->table);
+ }
+ printf("=====================================================\n");
+}
+
+// for gtest
+enum http_decoder_status http_decoder_status(struct http_decoder *decoder)
+{
+ return decoder->status;
+}
+
+/******************************************************************************
+ * Consume decoded table
+ ******************************************************************************/
+
+// HTTP_DIR_UNKNOWN: Not Find
+enum http_dir http_decoder_fetch_dir(struct http_decoder *decoder)
+{
+ if (decoder->status >= ON_URI)
+ {
+ switch (decoder->parser.type)
+ {
+ case 1:
+ return HTTP_DIR_REQUEST;
+ break;
+ case 2:
+ return HTTP_DIR_RESPONSE;
+ default:
+ return HTTP_DIR_UNKNOWN;
+ break;
+ }
+ }
+ else
+ {
+ return HTTP_DIR_UNKNOWN;
+ }
+}
+
+// -1 : Not Find
+int http_decoder_fetch_status_code(struct http_decoder *decoder)
+{
+ if (decoder->status >= ON_STATUS_COMPLETE && http_decoder_fetch_dir(decoder) == HTTP_DIR_RESPONSE)
+ {
+ return decoder->parser.status_code;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// -1 : Not Find
+int http_decoder_fetch_major_version(struct http_decoder *decoder)
+{
+ if (decoder->status >= ON_HEADER_FIELD)
+ {
+ return decoder->parser.http_major;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// -1 : Not Find
+int http_decoder_fetch_minor_version(struct http_decoder *decoder)
+{
+ if (decoder->status >= ON_HEADER_FIELD)
+ {
+ return decoder->parser.http_minor;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// ptr == NULL : Not Find
+void http_decoder_fetch_method(struct http_decoder *decoder, char **ptr, size_t *len)
+{
+ if (decoder->status >= ON_URI && http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST)
+ {
+ const char *method_str = llhttp_method_name((llhttp_method_t)decoder->parser.method);
+ *ptr = (char *)method_str;
+ *len = strlen(method_str);
+ }
+ else
+ {
+ *ptr = NULL;
+ *len = 0;
+ }
+}
+
+// ptr == NULL : Not Find
+void http_decoder_fetch_uri(struct http_decoder *decoder, char **ptr, size_t *len)
+{
+ http_decoder_table_get_uri(decoder->table, ptr, len);
+}
+
+// ptr == NULL : Not Find
+void http_decoder_fetch_status(struct http_decoder *decoder, char **ptr, size_t *len)
+{
+ http_decoder_table_get_status(decoder->table, ptr, len);
+}
+
+// ptr == NULL : Not Find
+void http_decoder_fetch_body(struct http_decoder *decoder, char **ptr, size_t *len)
+{
+ http_decoder_table_get_body(decoder->table, ptr, len);
+}
+
+// filed_ptr == NULL : Not Find
+void http_decoder_fetch_next_header(struct http_decoder *decoder, int *iter_index,
+ char **filed_ptr, size_t *filed_len, char **value_ptr, size_t *value_len)
+{
+ http_decoder_table_next_header(decoder->table, iter_index, filed_ptr, filed_len, value_ptr, value_len);
+}
diff --git a/src/protocol_decoder/http/http_decoder.h b/src/protocol_decoder/http/http_decoder.h
new file mode 100644
index 0000000..5ca43a5
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder.h
@@ -0,0 +1,81 @@
+#ifndef _HTTP_DECODER_H
+#define _HTTP_DECODER_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+/******************************************************************************
+ * Manipulate http decoder(Per HTTP Session)
+ ******************************************************************************/
+
+enum http_decoder_status
+{
+ ON_INIT = 0x1,
+ ON_MESSAGE_BEGIN = 0x2,
+ ON_URI = 0x3,
+ ON_URI_COMPLETE = 0x4,
+ ON_STATUS = 0x5,
+ ON_STATUS_COMPLETE = 0x6,
+ ON_HEADER_FIELD = 0x7,
+ ON_HEADER_FIELD_COMPLETE = 0x8,
+ ON_HEADER_VALUE = 0x9,
+ ON_HEADER_VALUE_COMPLETE = 0xa,
+ ON_CHUNK_HEADER = 0xb,
+ ON_CHUNK_HEADER_COMPLETE = 0xc,
+ ON_HEADERS_COMPLETE = 0xd,
+ ON_BODY = 0xe,
+ ON_MESSAGE_COMPLETE = 0xf,
+};
+
+struct http_decoder;
+
+struct http_decoder *http_decoder_create();
+void http_decoder_destory(struct http_decoder *decoder);
+
+void http_decoder_init(struct http_decoder *decoder);
+void http_decoder_reset(struct http_decoder *decoder);
+
+/*
+ * return 0 : new data that needs to be consumed by upper layers has been parsed
+ * return -1 : no new data
+ * return -2 : error or not http protocol
+ */
+int http_decoder_dispatch(struct http_decoder *decoder, const char *data, size_t len);
+// remove the data that has been consumed by the upper layer
+void http_decoder_remove(struct http_decoder *decoder);
+
+void http_decoder_dump(struct http_decoder *decoder); // for debug
+enum http_decoder_status http_decoder_status(struct http_decoder *decoder); // for gtest
+
+/******************************************************************************
+ * Consume decoded data
+ ******************************************************************************/
+
+enum http_dir
+{
+ HTTP_DIR_UNKNOWN = 0x0,
+ HTTP_DIR_REQUEST = 0x1,
+ HTTP_DIR_RESPONSE = 0x2,
+};
+
+enum http_dir http_decoder_fetch_dir(struct http_decoder *decoder); // HTTP_DIR_UNKNOWN: Not Find
+int http_decoder_fetch_status_code(struct http_decoder *decoder); // -1 : Not Find
+int http_decoder_fetch_major_version(struct http_decoder *decoder); // -1 : Not Find
+int http_decoder_fetch_minor_version(struct http_decoder *decoder); // -1 : Not Find
+
+void http_decoder_fetch_method(struct http_decoder *decoder, char **ptr, size_t *len); // ptr == NULL : Not Find
+void http_decoder_fetch_uri(struct http_decoder *decoder, char **ptr, size_t *len); // ptr == NULL : Not Find
+void http_decoder_fetch_status(struct http_decoder *decoder, char **ptr, size_t *len); // ptr == NULL : Not Find
+void http_decoder_fetch_body(struct http_decoder *decoder, char **ptr, size_t *len); // ptr == NULL : Not Find
+void http_decoder_fetch_next_header(struct http_decoder *decoder, int *iter_index,
+ char **filed_ptr, size_t *filed_len, char **value_ptr, size_t *value_len); // filed_ptr == NULL : Not Find
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
diff --git a/src/protocol_decoder/http/http_decoder_rstring.cpp b/src/protocol_decoder/http/http_decoder_rstring.cpp
new file mode 100644
index 0000000..981d107
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_rstring.cpp
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "http_decoder_util.h"
+#include "http_decoder_rstring.h"
+
+static const char *rstring_status_to_desc(enum rstring_status status)
+{
+ switch (status)
+ {
+ case RSTRING_STATUS_INIT:
+ return "INIT";
+ break;
+ case RSTRING_STATUS_REFER:
+ return "REFER";
+ break;
+ case RSTRING_STATUS_CACHE:
+ return "CACHE";
+ break;
+ case RSTRING_STATUS_COMMIT:
+ return "COMMIT";
+ break;
+ default:
+ return "UNKNOWN";
+ break;
+ }
+}
+
+// RSTRING_STATUS_INIT -> RSTRING_STATUS_REFER
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_REFER
+void http_decoder_rstring_refer(struct http_decoder_rstring *rstr, const char *at, size_t length)
+{
+ assert(rstr);
+
+ switch (rstr->status)
+ {
+ case RSTRING_STATUS_INIT:
+ case RSTRING_STATUS_CACHE:
+ rstr->refer.str = (char *)at;
+ rstr->refer.len = length;
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->status = RSTRING_STATUS_REFER;
+}
+
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_CACHE
+void http_decoder_rstring_cache(struct http_decoder_rstring *rstr)
+{
+ assert(rstr);
+
+ switch (rstr->status)
+ {
+ case RSTRING_STATUS_REFER:
+ if (rstr->refer.len > 0)
+ {
+ rstr->cache.str = http_decoder_safe_realloc(char, rstr->cache.str, rstr->cache.len + rstr->refer.len);
+ memcpy(rstr->cache.str + rstr->cache.len, rstr->refer.str, rstr->refer.len);
+ rstr->cache.len += rstr->refer.len;
+
+ rstr->refer.str = NULL;
+ rstr->refer.len = 0;
+ }
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->status = RSTRING_STATUS_CACHE;
+}
+
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_COMMIT
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_COMMIT
+void http_decoder_rstring_commit(struct http_decoder_rstring *rstr)
+{
+ assert(rstr);
+
+ switch (rstr->status)
+ {
+ case RSTRING_STATUS_REFER:
+ if (rstr->cache.len)
+ {
+ http_decoder_rstring_cache(rstr);
+
+ rstr->commit.str = rstr->cache.str;
+ rstr->commit.len = rstr->cache.len;
+ // not overwrite rstr->cache.str
+ }
+ else
+ {
+ rstr->commit.str = rstr->refer.str;
+ rstr->commit.len = rstr->refer.len;
+
+ rstr->refer.str = NULL;
+ rstr->refer.len = 0;
+ }
+ break;
+ case RSTRING_STATUS_CACHE:
+ rstr->commit.str = rstr->cache.str;
+ rstr->commit.len = rstr->cache.len;
+ // not overwrite rstr->cache.str
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->status = RSTRING_STATUS_COMMIT;
+}
+
+// RSTRING_STATUS_INIT -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_COMMIT -> RSTRING_STATUS_INIT
+void http_decoder_rstring_reset(struct http_decoder_rstring *rstr)
+{
+ assert(rstr);
+
+ switch (rstr->status)
+ {
+ case RSTRING_STATUS_INIT:
+ case RSTRING_STATUS_REFER:
+ case RSTRING_STATUS_CACHE:
+ case RSTRING_STATUS_COMMIT:
+ http_decoder_safe_free(rstr->cache.str);
+ memset(rstr, 0, sizeof(struct http_decoder_rstring));
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->status = RSTRING_STATUS_INIT;
+}
+
+enum rstring_status http_decoder_rstring_status(struct http_decoder_rstring *rstr)
+{
+ return rstr->status;
+}
+
+void http_decoder_rstring_read(struct http_decoder_rstring *rstr, char **ptr, size_t *len)
+{
+ assert(rstr);
+
+ if (http_decoder_rstring_status(rstr) == RSTRING_STATUS_COMMIT)
+ {
+ *ptr = rstr->commit.str;
+ *len = rstr->commit.len;
+ }
+ else
+ {
+ *ptr = NULL;
+ *len = 0;
+ }
+}
+
+void http_decoder_rstring_dump(struct http_decoder_rstring *rstr, const char *desc)
+{
+ assert(rstr);
+
+ char *refer_str = http_decoder_safe_dup(rstr->refer.str, rstr->refer.len);
+ char *cache_str = http_decoder_safe_dup(rstr->cache.str, rstr->cache.len);
+ char *commit_str = http_decoder_safe_dup(rstr->commit.str, rstr->commit.len);
+
+ printf("%s: status: %s, refer: {len: %02zu, str: %s}, cache: {len: %02zu, str: %s}, commit: {len: %02zu, str: %s}\n",
+ desc, rstring_status_to_desc(rstr->status),
+ rstr->refer.len, refer_str,
+ rstr->cache.len, cache_str,
+ rstr->commit.len, commit_str);
+
+ http_decoder_safe_free(refer_str);
+ http_decoder_safe_free(cache_str);
+ http_decoder_safe_free(commit_str);
+} \ No newline at end of file
diff --git a/src/protocol_decoder/http/http_decoder_rstring.h b/src/protocol_decoder/http/http_decoder_rstring.h
new file mode 100644
index 0000000..0af2c89
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_rstring.h
@@ -0,0 +1,90 @@
+#ifndef _HTTP_DECODER_RSTRING_H
+#define _HTTP_DECODER_RSTRING_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+enum rstring_status
+{
+ RSTRING_STATUS_INIT,
+ RSTRING_STATUS_REFER,
+ RSTRING_STATUS_CACHE,
+ RSTRING_STATUS_COMMIT,
+};
+
+struct rstring
+{
+ char *str;
+ size_t len;
+};
+
+struct http_decoder_rstring
+{
+ struct rstring refer; // shallow copy
+ struct rstring cache; // deep copy
+ struct rstring commit;
+
+ enum rstring_status status;
+};
+
+/* state transition diagram
+ * +----------+
+ * | |
+ * \|/ |
+ * +------+ |
+ * | init | |
+ * +------+ |
+ * | |
+ * +---->| |
+ * | \|/ |
+ * | +-------+ |
+ * | | refer |--+ |
+ * | +-------+ | |
+ * | | | |
+ * | \|/ | |
+ * | +-------+ | |
+ * +--| cache | | |
+ * +-------+ | |
+ * | | |
+ * |<------+ |
+ * \|/ |
+ * +--------+ |
+ * | commit | |
+ * +--------+ |
+ * | |
+ * \|/ |
+ * +--------+ |
+ * | reset |----+
+ * +--------+
+ */
+
+// RSTRING_STATUS_INIT -> RSTRING_STATUS_REFER
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_REFER
+void http_decoder_rstring_refer(struct http_decoder_rstring *rstr, const char *at, size_t length);
+
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_CACHE
+void http_decoder_rstring_cache(struct http_decoder_rstring *rstr);
+
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_COMMIT
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_COMMIT
+void http_decoder_rstring_commit(struct http_decoder_rstring *rstr);
+
+// RSTRING_STATUS_INIT -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_REFER -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_CACHE -> RSTRING_STATUS_INIT
+// RSTRING_STATUS_COMMIT -> RSTRING_STATUS_INIT
+void http_decoder_rstring_reset(struct http_decoder_rstring *rstr);
+
+enum rstring_status http_decoder_rstring_status(struct http_decoder_rstring *rstr);
+void http_decoder_rstring_read(struct http_decoder_rstring *rstr, char **ptr, size_t *len);
+void http_decoder_rstring_dump(struct http_decoder_rstring *rstr, const char *desc);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
diff --git a/src/protocol_decoder/http/http_decoder_table.cpp b/src/protocol_decoder/http/http_decoder_table.cpp
new file mode 100644
index 0000000..b98ddeb
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_table.cpp
@@ -0,0 +1,344 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "http_decoder_util.h"
+#include "http_decoder_rstring.h"
+#include "http_decoder_table.h"
+
+struct http_decoder_header
+{
+ struct http_decoder_rstring filed;
+ struct http_decoder_rstring value;
+};
+
+struct http_decoder_table
+{
+ struct http_decoder_rstring uri;
+ struct http_decoder_rstring status;
+ struct http_decoder_rstring body;
+
+ int header_size;
+ int header_index;
+ struct http_decoder_header *headers;
+};
+
+struct http_decoder_table *http_decoder_table_create()
+{
+ struct http_decoder_table *table = http_decoder_safe_alloc(struct http_decoder_table, 1);
+ assert(table);
+
+ table->header_size = 16;
+ table->headers = http_decoder_safe_alloc(struct http_decoder_header, table->header_size);
+ assert(table->headers);
+
+ return table;
+}
+
+void http_decoder_table_destory(struct http_decoder_table *table)
+{
+ if (table)
+ {
+ http_decoder_table_reset(table);
+ http_decoder_safe_free(table->headers);
+ http_decoder_safe_free(table);
+ }
+}
+
+void http_decoder_table_reset(struct http_decoder_table *table)
+{
+ assert(table);
+
+ http_decoder_rstring_reset(&table->uri);
+ http_decoder_rstring_reset(&table->status);
+ http_decoder_rstring_reset(&table->body);
+
+ for (int i = 0; i < table->header_size; i++)
+ {
+ struct http_decoder_header *header = &table->headers[i];
+ http_decoder_rstring_reset(&header->filed);
+ http_decoder_rstring_reset(&header->value);
+ }
+
+ int header_size = table->header_size;
+ struct http_decoder_header *headers = table->headers;
+
+ memset(table->headers, 0, sizeof(struct http_decoder_header) * table->header_size);
+ memset(table, 0, sizeof(struct http_decoder_table));
+
+ table->header_size = header_size;
+ table->headers = headers;
+}
+
+void http_decoder_table_add(struct http_decoder_table *table, enum http_iterm type, const char *str, size_t len)
+{
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type)
+ {
+ case HTTP_ITERM_URI:
+ http_decoder_rstring_refer(&table->uri, str, len);
+ break;
+ case HTTP_ITERM_STATUS:
+ http_decoder_rstring_refer(&table->status, str, len);
+ break;
+ case HTTP_ITERM_HEADER_FILED:
+ if (table->header_index >= table->header_size)
+ {
+ table->headers = http_decoder_safe_realloc(struct http_decoder_header, table->headers, table->header_size * 2 * sizeof(struct http_decoder_header));
+ assert(table->headers);
+ table->header_size *= 2;
+ for (int i = table->header_index; i < table->header_size; i++)
+ {
+ header = &table->headers[i];
+ memset(header, 0, sizeof(struct http_decoder_header));
+ }
+ }
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_refer(&header->filed, str, len);
+ break;
+ case HTTP_ITERM_HEADER_VALUE:
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_refer(&header->value, str, len);
+ break;
+ case HTTP_ITERM_BODY:
+ http_decoder_rstring_refer(&table->body, str, len);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_cache(struct http_decoder_table *table, enum http_iterm type)
+{
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type)
+ {
+ case HTTP_ITERM_URI:
+ http_decoder_rstring_cache(&table->uri);
+ break;
+ case HTTP_ITERM_STATUS:
+ http_decoder_rstring_cache(&table->status);
+ break;
+ case HTTP_ITERM_HEADER_FILED:
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_cache(&header->filed);
+ break;
+ case HTTP_ITERM_HEADER_VALUE:
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_cache(&header->value);
+ break;
+ case HTTP_ITERM_BODY:
+ http_decoder_rstring_cache(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_commit(struct http_decoder_table *table, enum http_iterm type)
+{
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type)
+ {
+ case HTTP_ITERM_URI:
+ http_decoder_rstring_commit(&table->uri);
+ break;
+ case HTTP_ITERM_STATUS:
+ http_decoder_rstring_commit(&table->status);
+ break;
+ case HTTP_ITERM_HEADER_FILED:
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_commit(&header->filed);
+ break;
+ case HTTP_ITERM_HEADER_VALUE:
+ header = &table->headers[table->header_index];
+ http_decoder_rstring_commit(&header->value);
+
+ // inc index
+ table->header_index++;
+ break;
+ case HTTP_ITERM_BODY:
+ http_decoder_rstring_commit(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_remove(struct http_decoder_table *table)
+{
+ assert(table);
+
+ if (http_decoder_rstring_status(&table->uri) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_reset(&table->uri);
+ }
+
+ if (http_decoder_rstring_status(&table->status) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_reset(&table->status);
+ }
+
+ if (http_decoder_rstring_status(&table->body) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_reset(&table->body);
+ }
+
+ for (int i = 0; i <= table->header_index; i++)
+ {
+ struct http_decoder_header *header = &table->headers[i];
+ if (http_decoder_rstring_status(&header->filed) == RSTRING_STATUS_COMMIT && http_decoder_rstring_status(&header->value) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_reset(&header->filed);
+ http_decoder_rstring_reset(&header->value);
+ }
+ }
+
+ if (table->header_index != 0)
+ {
+ struct http_decoder_header *last_header = &table->headers[table->header_index];
+ if (http_decoder_rstring_status(&last_header->filed) == RSTRING_STATUS_CACHE || http_decoder_rstring_status(&last_header->value) == RSTRING_STATUS_CACHE)
+ {
+ memmove(&table->headers[0], last_header, sizeof(struct http_decoder_header));
+ memset(last_header, 0, sizeof(struct http_decoder_header));
+ }
+
+ table->header_index = 0;
+ }
+}
+
+void http_decoder_table_dump(struct http_decoder_table *table)
+{
+ assert(table);
+
+ http_decoder_rstring_dump(&table->uri, "uri");
+ http_decoder_rstring_dump(&table->status, "status");
+ http_decoder_rstring_dump(&table->body, "body");
+
+ for (int i = 0; i <= table->header_index; i++)
+ {
+ struct http_decoder_header *header = &table->headers[i];
+ http_decoder_rstring_dump(&header->filed, "filed");
+ http_decoder_rstring_dump(&header->value, "value");
+ }
+}
+
+enum rstring_status http_decoder_table_status(struct http_decoder_table *table, enum http_iterm type)
+{
+ struct http_decoder_header *header = NULL;
+ enum rstring_status status = RSTRING_STATUS_INIT;
+ assert(table);
+
+ switch (type)
+ {
+ case HTTP_ITERM_URI:
+ status = http_decoder_rstring_status(&table->uri);
+ break;
+ case HTTP_ITERM_STATUS:
+ status = http_decoder_rstring_status(&table->status);
+ break;
+ case HTTP_ITERM_HEADER_FILED:
+ header = &table->headers[table->header_index];
+ status = http_decoder_rstring_status(&header->filed);
+ break;
+ case HTTP_ITERM_HEADER_VALUE:
+ header = &table->headers[table->header_index];
+ status = http_decoder_rstring_status(&header->value);
+ break;
+ case HTTP_ITERM_BODY:
+ status = http_decoder_rstring_status(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ return status;
+}
+
+void http_decoder_table_get_uri(struct http_decoder_table *table, char **ptr, size_t *len)
+{
+ assert(table);
+
+ if (http_decoder_rstring_status(&table->uri) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_read(&table->uri, ptr, len);
+ }
+ else
+ {
+ *ptr = NULL;
+ *len = 0;
+ }
+}
+
+void http_decoder_table_get_status(struct http_decoder_table *table, char **ptr, size_t *len)
+{
+ assert(table);
+
+ if (http_decoder_rstring_status(&table->status) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_read(&table->status, ptr, len);
+ }
+ else
+ {
+ *ptr = NULL;
+ *len = 0;
+ }
+}
+
+void http_decoder_table_get_body(struct http_decoder_table *table, char **ptr, size_t *len)
+{
+ assert(table);
+
+ if (http_decoder_rstring_status(&table->body) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_read(&table->body, ptr, len);
+ }
+ else
+ {
+ *ptr = NULL;
+ *len = 0;
+ }
+}
+
+void http_decoder_table_next_header(struct http_decoder_table *table, int *iter_index, char **filed_ptr, size_t *filed_len, char **value_ptr, size_t *value_len)
+{
+ assert(table);
+
+ if (*iter_index <= table->header_index)
+ {
+ struct http_decoder_header *header = &table->headers[*iter_index];
+ if (http_decoder_rstring_status(&header->filed) == RSTRING_STATUS_COMMIT && http_decoder_rstring_status(&header->value) == RSTRING_STATUS_COMMIT)
+ {
+ http_decoder_rstring_read(&header->filed, filed_ptr, filed_len);
+ http_decoder_rstring_read(&header->value, value_ptr, value_len);
+ }
+ else
+ {
+ *filed_ptr = NULL;
+ *filed_len = 0;
+
+ *value_ptr = NULL;
+ *value_len = 0;
+ }
+ *iter_index = *iter_index + 1;
+ }
+ else
+ {
+ *filed_ptr = NULL;
+ *filed_len = 0;
+
+ *value_ptr = NULL;
+ *value_len = 0;
+ }
+} \ No newline at end of file
diff --git a/src/protocol_decoder/http/http_decoder_table.h b/src/protocol_decoder/http/http_decoder_table.h
new file mode 100644
index 0000000..48cdd89
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_table.h
@@ -0,0 +1,45 @@
+#ifndef _HTTP_DECODER_TABLE_H
+#define _HTTP_DECODER_TABLE_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+#include "http_decoder_rstring.h"
+
+enum http_iterm
+{
+ HTTP_ITERM_URI = 0x01,
+ HTTP_ITERM_STATUS = 0x02,
+ HTTP_ITERM_HEADER_FILED = 0x03,
+ HTTP_ITERM_HEADER_VALUE = 0x04,
+ HTTP_ITERM_BODY = 0x05,
+};
+
+struct http_decoder_table;
+
+struct http_decoder_table *http_decoder_table_create();
+void http_decoder_table_destory(struct http_decoder_table *table);
+void http_decoder_table_reset(struct http_decoder_table *table);
+
+void http_decoder_table_add(struct http_decoder_table *table, enum http_iterm type, const char *str, size_t len);
+void http_decoder_table_cache(struct http_decoder_table *table, enum http_iterm type);
+void http_decoder_table_commit(struct http_decoder_table *table, enum http_iterm type);
+void http_decoder_table_remove(struct http_decoder_table *table);
+void http_decoder_table_dump(struct http_decoder_table *table);
+enum rstring_status http_decoder_table_status(struct http_decoder_table *table, enum http_iterm type);
+
+void http_decoder_table_get_uri(struct http_decoder_table *table, char **ptr, size_t *len);
+void http_decoder_table_get_status(struct http_decoder_table *table, char **ptr, size_t *len);
+void http_decoder_table_get_body(struct http_decoder_table *table, char **ptr, size_t *len);
+
+void http_decoder_table_next_header(struct http_decoder_table *table, int *iter_index, char **filed_ptr, size_t *filed_len, char **value_ptr, size_t *value_len);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
diff --git a/src/protocol_decoder/http/http_decoder_util.cpp b/src/protocol_decoder/http/http_decoder_util.cpp
new file mode 100644
index 0000000..63a154d
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_util.cpp
@@ -0,0 +1,16 @@
+#include <string.h>
+
+#include "http_decoder_util.h"
+
+char *http_decoder_safe_dup(const char *str, size_t len)
+{
+ if (str == NULL || len == 0)
+ {
+ return NULL;
+ }
+
+ char *dup = http_decoder_safe_alloc(char, len + 1);
+ memcpy(dup, str, len);
+
+ return dup;
+} \ No newline at end of file
diff --git a/src/protocol_decoder/http/http_decoder_util.h b/src/protocol_decoder/http/http_decoder_util.h
new file mode 100644
index 0000000..de2bbb3
--- /dev/null
+++ b/src/protocol_decoder/http/http_decoder_util.h
@@ -0,0 +1,61 @@
+#ifndef _HTTP_DECODER_UTIL_H
+#define _HTTP_DECODER_UTIL_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+/******************************************************************************
+ * Malloc
+ ******************************************************************************/
+
+#define http_decoder_safe_realloc(type, ptr, number) ((type *)realloc(ptr, number))
+#define http_decoder_safe_alloc(type, number) ((type *)calloc(number, sizeof(type)))
+#define http_decoder_safe_free(ptr) \
+ { \
+ if (ptr) \
+ { \
+ free(ptr); \
+ ptr = NULL; \
+ } \
+ }
+char *http_decoder_safe_dup(const char *str, size_t len);
+
+/******************************************************************************
+ * Offset
+ ******************************************************************************/
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+#endif
+
+/*
+ * Return pointer to the wrapping struct instance.
+ *
+ * Example:
+ *
+ * struct wrapper {
+ * ...
+ * struct child c;
+ * ...
+ * };
+ *
+ * struct child *x = obtain(...);
+ * struct wrapper *w = container_of(x, struct wrapper, c);
+ */
+#ifndef container_of
+#define container_of(ptr, type, member) __extension__({ \
+ const typeof(((type *)0)->member) *_ptr = (ptr); \
+ __attribute__((unused)) type *_target_ptr = (type *)(ptr); \
+ (type *)(((uintptr_t)_ptr) - offsetof(type, member)); \
+})
+#endif
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
diff --git a/src/protocol_decoder/http/test/CMakeLists.txt b/src/protocol_decoder/http/test/CMakeLists.txt
new file mode 100644
index 0000000..54d8d44
--- /dev/null
+++ b/src/protocol_decoder/http/test/CMakeLists.txt
@@ -0,0 +1,38 @@
+add_executable(http_decoder_rstring_test
+ http_decoder_rstring_test.cpp
+)
+
+add_executable(http_decoder_table_test
+ http_decoder_table_test.cpp
+)
+
+add_executable(http_decoder_test
+ http_decoder_test.cpp
+)
+
+target_link_libraries(
+ http_decoder_rstring_test
+ gtest
+ http
+ llhttp-static
+)
+
+target_link_libraries(
+ http_decoder_table_test
+ gtest
+ http
+)
+
+target_link_libraries(
+ http_decoder_test
+ gtest
+ http
+)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,--export-dynamic")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--export-dynamic")
+
+include(GoogleTest)
+gtest_discover_tests(http_decoder_rstring_test)
+gtest_discover_tests(http_decoder_table_test)
+gtest_discover_tests(http_decoder_test) \ No newline at end of file
diff --git a/src/protocol_decoder/http/test/http_decoder_rstring_test.cpp b/src/protocol_decoder/http/test/http_decoder_rstring_test.cpp
new file mode 100644
index 0000000..046fb86
--- /dev/null
+++ b/src/protocol_decoder/http/test/http_decoder_rstring_test.cpp
@@ -0,0 +1,328 @@
+#include <gtest/gtest.h>
+
+#include "../http_decoder_util.h"
+#include "../http_decoder_rstring.h"
+
+TEST(HTTP_DECODER_RSTRING_TEST, refer_commit_reset)
+{
+ char *ptr = NULL;
+ size_t len = 0;
+ struct http_decoder_rstring rstr = {0};
+ char *hello_str = http_decoder_safe_dup("Hello111", 8);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, hello_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // commit
+ http_decoder_rstring_commit(&rstr);
+ http_decoder_rstring_dump(&rstr, "After commit");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_COMMIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 5);
+ EXPECT_TRUE(strncmp(rstr.commit.str, "Hello", 5) == 0);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(strncmp("Hello", ptr, len) == 0);
+
+ // reset
+ http_decoder_rstring_reset(&rstr);
+ http_decoder_rstring_dump(&rstr, "After reset");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_INIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ http_decoder_safe_free(hello_str);
+}
+
+TEST(HTTP_DECODER_RSTRING_TEST, refer_cache_commit_reset)
+{
+ char *ptr = NULL;
+ size_t len = 0;
+ struct http_decoder_rstring rstr = {0};
+ char *hello_str = http_decoder_safe_dup("Hello111", 8);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, hello_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // cache
+ http_decoder_rstring_cache(&rstr);
+ http_decoder_rstring_dump(&rstr, "After cache");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_CACHE);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ http_decoder_safe_free(hello_str);
+
+ // commit
+ http_decoder_rstring_commit(&rstr);
+ http_decoder_rstring_dump(&rstr, "After commit");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_COMMIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 5);
+ EXPECT_TRUE(strncmp(rstr.commit.str, "Hello", 5) == 0);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(strncmp("Hello", ptr, len) == 0);
+
+ // reset
+ http_decoder_rstring_reset(&rstr);
+ http_decoder_rstring_dump(&rstr, "After reset");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_INIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+}
+
+TEST(HTTP_DECODER_RSTRING_TEST, refer_cache_refer_commit_reset)
+{
+ char *ptr = NULL;
+ size_t len = 0;
+ struct http_decoder_rstring rstr = {0};
+ char *hello_str = http_decoder_safe_dup("Hello111", 8);
+ char *world_str = http_decoder_safe_dup("World222", 8);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, hello_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // cache
+ http_decoder_rstring_cache(&rstr);
+ http_decoder_rstring_dump(&rstr, "After cache");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_CACHE);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ http_decoder_safe_free(hello_str);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, world_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "World", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // commit
+ http_decoder_rstring_commit(&rstr);
+ http_decoder_rstring_dump(&rstr, "After commit");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_COMMIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 10);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "HelloWorld", 10) == 0);
+ EXPECT_TRUE(rstr.commit.len == 10);
+ EXPECT_TRUE(strncmp(rstr.commit.str, "HelloWorld", 10) == 0);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(strncmp("HelloWorld", ptr, len) == 0);
+
+ http_decoder_safe_free(world_str);
+
+ // reset
+ http_decoder_rstring_reset(&rstr);
+ http_decoder_rstring_dump(&rstr, "After reset");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_INIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+}
+
+TEST(HTTP_DECODER_RSTRING_TEST, refer_cache_refer_cache_commit_reset)
+{
+ char *ptr = NULL;
+ size_t len = 0;
+ struct http_decoder_rstring rstr = {0};
+ char *hello_str = http_decoder_safe_dup("Hello111", 8);
+ char *world_str = http_decoder_safe_dup("World222", 8);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, hello_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // cache
+ http_decoder_rstring_cache(&rstr);
+ http_decoder_rstring_dump(&rstr, "After cache");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_CACHE);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ http_decoder_safe_free(hello_str);
+
+ // refer
+ http_decoder_rstring_refer(&rstr, world_str, 5);
+ http_decoder_rstring_dump(&rstr, "After refer");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_REFER);
+ EXPECT_TRUE(rstr.refer.len == 5);
+ EXPECT_TRUE(strncmp(rstr.refer.str, "World", 5) == 0);
+ EXPECT_TRUE(rstr.cache.len == 5);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "Hello", 5) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ // cache
+ http_decoder_rstring_cache(&rstr);
+ http_decoder_rstring_dump(&rstr, "After cache");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_CACHE);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 10);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "HelloWorld", 10) == 0);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+
+ http_decoder_safe_free(world_str);
+
+ // commit
+ http_decoder_rstring_commit(&rstr);
+ http_decoder_rstring_dump(&rstr, "After commit");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_COMMIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 10);
+ EXPECT_TRUE(strncmp(rstr.cache.str, "HelloWorld", 10) == 0);
+ EXPECT_TRUE(rstr.commit.len == 10);
+ EXPECT_TRUE(strncmp(rstr.commit.str, "HelloWorld", 10) == 0);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(strncmp("HelloWorld", ptr, len) == 0);
+
+ // reset
+ http_decoder_rstring_reset(&rstr);
+ http_decoder_rstring_dump(&rstr, "After reset");
+
+ EXPECT_TRUE(http_decoder_rstring_status(&rstr) == RSTRING_STATUS_INIT);
+ EXPECT_TRUE(rstr.refer.len == 0);
+ EXPECT_TRUE(rstr.refer.str == NULL);
+ EXPECT_TRUE(rstr.cache.len == 0);
+ EXPECT_TRUE(rstr.cache.str == NULL);
+ EXPECT_TRUE(rstr.commit.len == 0);
+ EXPECT_TRUE(rstr.commit.str == NULL);
+
+ http_decoder_rstring_read(&rstr, &ptr, &len);
+ EXPECT_TRUE(NULL == ptr && len == 0);
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ ::testing::InitGoogleTest(&argc, argv);
+ ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/src/protocol_decoder/http/test/http_decoder_table_test.cpp b/src/protocol_decoder/http/test/http_decoder_table_test.cpp
new file mode 100644
index 0000000..d4d25f8
--- /dev/null
+++ b/src/protocol_decoder/http/test/http_decoder_table_test.cpp
@@ -0,0 +1,431 @@
+#include <gtest/gtest.h>
+
+#include "../http_decoder_table.h"
+
+struct key_val
+{
+ const char *key;
+ const char *val;
+};
+
+struct key_val simple_headers[] = {
+ {"Host", "www.baidu.com"},
+ {"User-Agent", "curl/7.64.1"},
+ {"connection", "close"},
+ {"Accept", "*/*"},
+};
+
+// At least 17 headers to trigger realloc
+struct key_val mutil_headers[] = {
+ {"Server", "nginx"},
+ {"content-length", "11"},
+ {"Date", "Mon, 15 Aug 2022 07:40:35 GMT"},
+ {"Content-Type", "text/html;charset=utf-8"},
+ {"Expires", "Mon, 15 Aug 2022 07:50:34 GMT"},
+ {"X-Cache-Lookup", "Hit From Disktank3"},
+ {"ETag", "346f24f3000e27ce6f3d3b61f2c6a83b"},
+ {"X-XSS-Protection", "0"},
+ {"X-Daa-Tunnel", "hop_count=1"},
+ {"X-Cache-Lookup", "Hit From Inner Cluster"},
+ {"X-Cache-Lookup", "Cache Miss"},
+ {"Last-Modified", "Mon, 15 Aug 2022 07:30:00 GMT"},
+ {"Cache-Control", "private, max-age=600"},
+ {"Content-Length", "51792"},
+ {"X-NWS-LOG-UUID", "13366651529734458115"},
+ {"Connection", "keep-alive"},
+ {"X-Cache-Lookup", "Cache Miss"},
+ {"Set-cookie", "aaa"},
+ {"Set-cookie", "bbb"},
+};
+
+const char *append = "000";
+const char *status = "OK";
+const char *uri = "index.html";
+const char *body = "hello world";
+
+TEST(HTTP_DECODER_TABLE_TEST, add_commit)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get uri
+ http_decoder_table_add(data, HTTP_ITERM_URI, uri, strlen(uri));
+ http_decoder_table_commit(data, HTTP_ITERM_URI);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(uri, key_str, key_len) == 0);
+
+ // add/get status
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, status, strlen(status));
+ http_decoder_table_commit(data, HTTP_ITERM_STATUS);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(status, key_str, key_len) == 0);
+
+ // add/get body
+ http_decoder_table_add(data, HTTP_ITERM_BODY, body, strlen(body));
+ http_decoder_table_commit(data, HTTP_ITERM_BODY);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(body, key_str, key_len) == 0);
+
+ // add/get header
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, simple_headers[i].key, strlen(simple_headers[i].key));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, simple_headers[i].val, strlen(simple_headers[i].val));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_VALUE);
+ }
+
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp(simple_headers[i].key, key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp(simple_headers[i].val, val_str, val_len) == 0);
+ }
+
+ // dump
+ printf("After commit:\n");
+ http_decoder_table_dump(data);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+TEST(HTTP_DECODER_TABLE_TEST, add_cache_commit)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get uri
+ http_decoder_table_add(data, HTTP_ITERM_URI, uri, strlen(uri));
+ http_decoder_table_cache(data, HTTP_ITERM_URI);
+ http_decoder_table_commit(data, HTTP_ITERM_URI);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(uri, key_str, key_len) == 0);
+
+ // add/get status
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, status, strlen(status));
+ http_decoder_table_cache(data, HTTP_ITERM_STATUS);
+ http_decoder_table_commit(data, HTTP_ITERM_STATUS);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(status, key_str, key_len) == 0);
+
+ // add/get body
+ http_decoder_table_add(data, HTTP_ITERM_BODY, body, strlen(body));
+ http_decoder_table_cache(data, HTTP_ITERM_BODY);
+ http_decoder_table_commit(data, HTTP_ITERM_BODY);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(body, key_str, key_len) == 0);
+
+ // add/get header
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, simple_headers[i].key, strlen(simple_headers[i].key));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_FILED);
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, simple_headers[i].val, strlen(simple_headers[i].val));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_VALUE);
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_VALUE);
+ }
+
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp(simple_headers[i].key, key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp(simple_headers[i].val, val_str, val_len) == 0);
+ }
+
+ // dump
+ printf("After commit:\n");
+ http_decoder_table_dump(data);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+TEST(HTTP_DECODER_TABLE_TEST, add_cache_add_commit)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get uri
+ http_decoder_table_add(data, HTTP_ITERM_URI, uri, strlen(uri));
+ http_decoder_table_cache(data, HTTP_ITERM_URI);
+ http_decoder_table_add(data, HTTP_ITERM_URI, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_URI);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(key_str, uri, strlen(uri)) == 0);
+ EXPECT_TRUE(strncmp(key_str + strlen(uri), append, strlen(append)) == 0);
+
+ // add/get status
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, status, strlen(status));
+ http_decoder_table_cache(data, HTTP_ITERM_STATUS);
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_STATUS);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(key_str, status, strlen(status)) == 0);
+ EXPECT_TRUE(strncmp(key_str + strlen(status), append, strlen(append)) == 0);
+
+ // add/get body
+ http_decoder_table_add(data, HTTP_ITERM_BODY, body, strlen(body));
+ http_decoder_table_cache(data, HTTP_ITERM_BODY);
+ http_decoder_table_add(data, HTTP_ITERM_BODY, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_BODY);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(key_str, body, strlen(body)) == 0);
+ EXPECT_TRUE(strncmp(key_str + strlen(body), append, strlen(append)) == 0);
+
+ // add/get header
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, simple_headers[i].key, strlen(simple_headers[i].key));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_FILED);
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, simple_headers[i].val, strlen(simple_headers[i].val));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_VALUE);
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_VALUE);
+ }
+
+ for (size_t i = 0; i < sizeof(simple_headers) / sizeof(simple_headers[0]); i++)
+ {
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+
+ EXPECT_TRUE(strncmp(key_str, simple_headers[i].key, strlen(simple_headers[i].key)) == 0);
+ EXPECT_TRUE(strncmp(key_str + strlen(simple_headers[i].key), append, strlen(append)) == 0);
+
+ EXPECT_TRUE(strncmp(val_str, simple_headers[i].val, strlen(simple_headers[i].val)) == 0);
+ EXPECT_TRUE(strncmp(val_str + strlen(simple_headers[i].val), append, strlen(append)) == 0);
+ }
+
+ // dump
+ printf("After commit:\n");
+ http_decoder_table_dump(data);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+TEST(HTTP_DECODER_TABLE_TEST, realloc_header)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get header
+ for (size_t i = 0; i < sizeof(mutil_headers) / sizeof(mutil_headers[0]); i++)
+ {
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, mutil_headers[i].key, strlen(mutil_headers[i].key));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_FILED);
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, mutil_headers[i].val, strlen(mutil_headers[i].val));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_VALUE);
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, append, strlen(append));
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_VALUE);
+ }
+
+ for (size_t i = 0; i < sizeof(mutil_headers) / sizeof(mutil_headers[0]); i++)
+ {
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+
+ EXPECT_TRUE(strncmp(key_str, mutil_headers[i].key, strlen(mutil_headers[i].key)) == 0);
+ EXPECT_TRUE(strncmp(key_str + strlen(mutil_headers[i].key), append, strlen(append)) == 0);
+
+ EXPECT_TRUE(strncmp(val_str, mutil_headers[i].val, strlen(mutil_headers[i].val)) == 0);
+ EXPECT_TRUE(strncmp(val_str + strlen(mutil_headers[i].val), append, strlen(append)) == 0);
+ }
+
+ // dump
+ printf("After commit:\n");
+ http_decoder_table_dump(data);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+TEST(HTTP_DECODER_TABLE_TEST, remove)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get uri
+ http_decoder_table_add(data, HTTP_ITERM_URI, uri, strlen(uri));
+ http_decoder_table_cache(data, HTTP_ITERM_URI);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get status
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, status, strlen(status));
+ http_decoder_table_cache(data, HTTP_ITERM_STATUS);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get body
+ http_decoder_table_add(data, HTTP_ITERM_BODY, body, strlen(body));
+ http_decoder_table_cache(data, HTTP_ITERM_BODY);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get header
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, simple_headers[0].key, strlen(simple_headers[0].key));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, simple_headers[0].val, strlen(simple_headers[0].val));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_VALUE);
+
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+ EXPECT_TRUE(val_str == NULL && val_len == 0);
+
+ // dump
+ printf("After cache:\n");
+ http_decoder_table_dump(data);
+
+ http_decoder_table_remove(data);
+
+ printf("After remove:\n");
+ http_decoder_table_dump(data);
+
+ http_decoder_table_commit(data, HTTP_ITERM_URI);
+ http_decoder_table_commit(data, HTTP_ITERM_STATUS);
+ http_decoder_table_commit(data, HTTP_ITERM_BODY);
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_FILED);
+ http_decoder_table_commit(data, HTTP_ITERM_HEADER_VALUE);
+
+ printf("After commit:\n");
+ http_decoder_table_dump(data);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(uri, key_str, key_len) == 0);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(status, key_str, key_len) == 0);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(body, key_str, key_len) == 0);
+
+ iter_index = 0;
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+
+ EXPECT_TRUE(strncmp(simple_headers[0].key, key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp(simple_headers[0].val, val_str, val_len) == 0);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+TEST(HTTP_DECODER_TABLE_TEST, reset)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ // create
+ struct http_decoder_table *data = http_decoder_table_create();
+ EXPECT_TRUE(data != NULL);
+
+ // add/get uri
+ http_decoder_table_add(data, HTTP_ITERM_URI, uri, strlen(uri));
+ http_decoder_table_cache(data, HTTP_ITERM_URI);
+
+ http_decoder_table_get_uri(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get status
+ http_decoder_table_add(data, HTTP_ITERM_STATUS, status, strlen(status));
+ http_decoder_table_cache(data, HTTP_ITERM_STATUS);
+
+ http_decoder_table_get_status(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get body
+ http_decoder_table_add(data, HTTP_ITERM_BODY, body, strlen(body));
+ http_decoder_table_cache(data, HTTP_ITERM_BODY);
+
+ http_decoder_table_get_body(data, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ // add/get header
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_FILED, simple_headers[0].key, strlen(simple_headers[0].key));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_FILED);
+
+ http_decoder_table_add(data, HTTP_ITERM_HEADER_VALUE, simple_headers[0].val, strlen(simple_headers[0].val));
+ http_decoder_table_cache(data, HTTP_ITERM_HEADER_VALUE);
+
+ http_decoder_table_next_header(data, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+ EXPECT_TRUE(val_str == NULL && val_len == 0);
+
+ // dump
+ printf("After cache:\n");
+ http_decoder_table_dump(data);
+
+ http_decoder_table_reset(data);
+
+ printf("After reset:\n");
+ http_decoder_table_dump(data);
+
+ // destory
+ http_decoder_table_destory(data);
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ ::testing::InitGoogleTest(&argc, argv);
+ ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/src/protocol_decoder/http/test/http_decoder_test.cpp b/src/protocol_decoder/http/test/http_decoder_test.cpp
new file mode 100644
index 0000000..a627077
--- /dev/null
+++ b/src/protocol_decoder/http/test/http_decoder_test.cpp
@@ -0,0 +1,614 @@
+#include "../http_decoder.h"
+
+#include <gtest/gtest.h>
+
+const char *request_post = "POST /index.html HTTP/1.1\r\nconnection:close\r\ncontent-length: 1\r\n\r\n1\r\n\r\n";
+const char *request_get = "GET /index.html HTTP/1.1\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.64.1\r\nAccept: */*\r\n\r\n";
+const char *response_200 = "HTTP/1.1 200 OK\r\nServer: nginx\r\ncontent-length: 11\r\n\r\nhello:world\r\n\r\n";
+
+/******************************************************************************
+ * complete http request/response
+ ******************************************************************************/
+
+#if 1
+TEST(HTTP_DECODER_TEST, complete_post_request)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_post, strlen(request_post)) == 0);
+ http_decoder_dump(decoder);
+
+ // check data
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("POST", key_str, key_len) == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("1", key_str, key_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("connection", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("close", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("content-length", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("1", val_str, val_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, complete_get_request)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_get, strlen(request_get)) == 0);
+ http_decoder_dump(decoder);
+
+ // check data
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("GET", key_str, key_len) == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("Host", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("www.baidu.com", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("User-Agent", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("curl/7.64.1", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("Accept", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("*/*", val_str, val_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, complete_response)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+ EXPECT_TRUE(http_decoder_dispatch(decoder, response_200, strlen(response_200)) == 0);
+ http_decoder_dump(decoder);
+
+ // check data
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_RESPONSE);
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == 200);
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("OK", key_str, key_len) == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("hello:world", key_str, key_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("Server", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("nginx", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("content-length", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("11", val_str, val_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+/******************************************************************************
+ * uncomplete http request
+ ******************************************************************************/
+
+#if 1
+TEST(HTTP_DECODER_TEST, uncomplete_post_request)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ for (size_t i = 0; i < strlen(request_post); i++)
+ {
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_post + i, 1) != -2);
+ }
+ http_decoder_dump(decoder);
+
+ // check data
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("POST", key_str, key_len) == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("1", key_str, key_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("connection", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("close", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("content-length", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("1", val_str, val_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, uncomplete_get_request)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ for (size_t i = 0; i < strlen(request_get); i++)
+ {
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_get + i, 1) != -2);
+ }
+ http_decoder_dump(decoder);
+
+ // check data
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("GET", key_str, key_len) == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("Host", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("www.baidu.com", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("User-Agent", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("curl/7.64.1", val_str, val_len) == 0);
+
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ EXPECT_TRUE(strncmp("Accept", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("*/*", val_str, val_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+/******************************************************************************
+ * uncomplete http request/response and consume meta
+ ******************************************************************************/
+
+#if 1
+TEST(HTTP_DECODER_TEST, uncomplete_post_request_and_consume_meta)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+ int next_header = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ for (size_t i = 0; i < strlen(request_post); i++)
+ {
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_post + i, 1) != -2);
+
+ if (http_decoder_status(decoder) >= ON_URI)
+ {
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ }
+
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+ }
+
+ if (http_decoder_status(decoder) >= ON_URI)
+ {
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("POST", key_str, key_len) == 0);
+ }
+
+ if (http_decoder_status(decoder) >= ON_URI_COMPLETE)
+ {
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+ }
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ if (http_decoder_status(decoder) >= ON_BODY)
+ {
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("1", key_str, key_len) == 0);
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ if (next_header == 0)
+ {
+ EXPECT_TRUE(strncmp("connection", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("close", val_str, val_len) == 0);
+
+ next_header++;
+ }
+ else if (next_header == 1)
+ {
+ EXPECT_TRUE(strncmp("content-length", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("1", val_str, val_len) == 0);
+ }
+ }
+
+ http_decoder_dump(decoder);
+ http_decoder_remove(decoder);
+ }
+ http_decoder_dump(decoder);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, uncomplete_get_request_and_consume_meta)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+ int next_header = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ for (size_t i = 0; i < strlen(request_get); i++)
+ {
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_get + i, 1) != -2);
+
+ if (http_decoder_status(decoder) >= ON_URI)
+ {
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_REQUEST);
+ }
+
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == -1); // unused
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+ }
+
+ if (http_decoder_status(decoder) >= ON_URI)
+ {
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("GET", key_str, key_len) == 0);
+ }
+
+ if (http_decoder_status(decoder) >= ON_URI_COMPLETE)
+ {
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("/index.html", key_str, key_len) == 0);
+ }
+
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ if (next_header == 0)
+ {
+ EXPECT_TRUE(strncmp("Host", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("www.baidu.com", val_str, val_len) == 0);
+
+ next_header++;
+ }
+ else if (next_header == 1)
+ {
+ EXPECT_TRUE(strncmp("User-Agent", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("curl/7.64.1", val_str, val_len) == 0);
+
+ next_header++;
+ }
+ else if (next_header == 2)
+ {
+ EXPECT_TRUE(strncmp("Accept", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("*/*", val_str, val_len) == 0);
+ }
+ }
+
+ http_decoder_dump(decoder);
+ http_decoder_remove(decoder);
+ }
+ http_decoder_dump(decoder);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, uncomplete_response_and_consume_meta)
+{
+ char *key_str = NULL;
+ char *val_str = NULL;
+ size_t key_len = 0;
+ size_t val_len = 0;
+ int iter_index = 0;
+ int next_header = 0;
+
+ char body_data[] = "hello:world";
+ int body_offset = 0;
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ for (size_t i = 0; i < strlen(response_200); i++)
+ {
+ EXPECT_TRUE(http_decoder_dispatch(decoder, response_200 + i, 1) != -2);
+
+ if (http_decoder_status(decoder) >= ON_URI)
+ {
+ EXPECT_TRUE(http_decoder_fetch_dir(decoder) == HTTP_DIR_RESPONSE);
+ }
+
+ if (http_decoder_status(decoder) >= ON_STATUS_COMPLETE)
+ {
+ EXPECT_TRUE(http_decoder_fetch_status_code(decoder) == 200);
+ http_decoder_fetch_status(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("OK", key_str, key_len) == 0);
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_major_version(decoder) == 1);
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ EXPECT_TRUE(http_decoder_fetch_minor_version(decoder) == 1);
+ }
+
+ http_decoder_fetch_method(decoder, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ http_decoder_fetch_uri(decoder, &key_str, &key_len);
+ EXPECT_TRUE(key_str == NULL && key_len == 0);
+
+ if (http_decoder_status(decoder) >= ON_BODY)
+ {
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ if (key_str)
+ {
+ EXPECT_TRUE(strncmp(body_data + body_offset, key_str, key_len) == 0);
+ body_offset += key_len;
+ }
+ }
+
+ if (http_decoder_status(decoder) >= ON_HEADER_FIELD)
+ {
+ http_decoder_fetch_next_header(decoder, &iter_index, &key_str, &key_len, &val_str, &val_len);
+ if (next_header == 0)
+ {
+ EXPECT_TRUE(strncmp("Server", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("nginx", val_str, val_len) == 0);
+
+ next_header++;
+ }
+ else if (next_header == 1)
+ {
+ EXPECT_TRUE(strncmp("content-length", key_str, key_len) == 0);
+ EXPECT_TRUE(strncmp("11", val_str, val_len) == 0);
+ }
+ }
+
+ http_decoder_dump(decoder);
+ http_decoder_remove(decoder);
+ }
+ http_decoder_dump(decoder);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+/******************************************************************************
+ * error test
+ ******************************************************************************/
+
+#if 1
+TEST(HTTP_DECODER_TEST, error_test_without_http_version)
+{
+ const char *request_part_1 = "POST /index.html ";
+ const char *request_part_2 = "\r\nconnection:close\r\ncontent-length: 1\r\n\r\n1\r\n\r\n";
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_part_1, strlen(request_part_1)) != -2);
+ http_decoder_dump(decoder);
+
+ // error occur
+ EXPECT_TRUE(http_decoder_dispatch(decoder, request_part_2, strlen(request_part_2)) == -2);
+ http_decoder_reset(decoder);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, error_test_without_content_length)
+{
+ char *key_str = NULL;
+ size_t key_len = 0;
+ const char *without_content_length = "POST /index.html HTTP/1.1\r\nconnection:close\r\n\r\n12345\r\n\r\n";
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ EXPECT_TRUE(http_decoder_dispatch(decoder, without_content_length, strlen(without_content_length)) != -2);
+ http_decoder_dump(decoder);
+
+ // no content-length, so skip parsing the request body
+ EXPECT_TRUE(http_decoder_status(decoder) == ON_MESSAGE_COMPLETE);
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(NULL == key_str && key_len == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+// escape vulnerability
+TEST(HTTP_DECODER_TEST, error_test_content_length_too_small)
+{
+ char *key_str = NULL;
+ size_t key_len = 0;
+ const char *content_length_too_small = "POST /index.html HTTP/1.1\r\nconnection:close\r\ncontent-length: 2\r\n\r\n12345\r\n\r\n";
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ EXPECT_TRUE(http_decoder_dispatch(decoder, content_length_too_small, strlen(content_length_too_small)) != -2);
+ http_decoder_dump(decoder);
+
+ // only parse data of the length specified by content-length
+ EXPECT_TRUE(http_decoder_status(decoder) == ON_MESSAGE_COMPLETE);
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("12", key_str, key_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+#if 1
+TEST(HTTP_DECODER_TEST, error_test_content_length_too_big)
+{
+ char *key_str = NULL;
+ size_t key_len = 0;
+ const char *content_length_too_big = "POST /index.html HTTP/1.1\r\nconnection:close\r\ncontent-length: 20\r\n\r\n12345\r\n\r\n";
+ const char *last_body = "12345678901";
+
+ struct http_decoder *decoder = http_decoder_create();
+ http_decoder_init(decoder);
+
+ // parser content_length_too_big
+ EXPECT_TRUE(http_decoder_dispatch(decoder, content_length_too_big, strlen(content_length_too_big)) != -2);
+ http_decoder_dump(decoder);
+
+ EXPECT_TRUE(http_decoder_status(decoder) == ON_BODY);
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp("12345\r\n\r\n", key_str, key_len) == 0);
+
+ // parser last_body
+ http_decoder_remove(decoder);
+ EXPECT_TRUE(http_decoder_dispatch(decoder, last_body, strlen(last_body)) != -2);
+ http_decoder_dump(decoder);
+
+ EXPECT_TRUE(http_decoder_status(decoder) == ON_MESSAGE_COMPLETE);
+ http_decoder_fetch_body(decoder, &key_str, &key_len);
+ EXPECT_TRUE(strncmp(last_body, key_str, key_len) == 0);
+
+ http_decoder_destory(decoder);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ ::testing::InitGoogleTest(&argc, argv);
+ ret = RUN_ALL_TESTS();
+ return ret;
+} \ No newline at end of file
diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt
index 6799ed0..971eb17 100644
--- a/vendor/CMakeLists.txt
+++ b/vendor/CMakeLists.txt
@@ -18,4 +18,18 @@ set_property(TARGET gtest PROPERTY INTERFACE_LINK_LIBRARIES pthread)
add_library(gmock STATIC IMPORTED GLOBAL)
add_dependencies(gmock googletest)
set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgmock.a)
-set_property(TARGET gmock PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) \ No newline at end of file
+set_property(TARGET gmock PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include)
+
+# llhttp
+ExternalProject_Add(llhttp PREFIX llhttp
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/llhttp-release-v6.0.9.tar.gz
+ URL_MD5 3f6e9a3837e4ec6998f809efcd733170
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBUILD_STATIC_LIBS=ON)
+
+ExternalProject_Get_Property(llhttp INSTALL_DIR)
+file(MAKE_DIRECTORY ${INSTALL_DIR}/include)
+
+add_library(llhttp-static STATIC IMPORTED GLOBAL)
+add_dependencies(llhttp-static llhttp)
+set_property(TARGET llhttp-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib64/libllhttp.a)
+set_property(TARGET llhttp-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) \ No newline at end of file
diff --git a/vendor/llhttp-release-v6.0.9.tar.gz b/vendor/llhttp-release-v6.0.9.tar.gz
new file mode 100644
index 0000000..f6155cf
--- /dev/null
+++ b/vendor/llhttp-release-v6.0.9.tar.gz
Binary files differ