summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author刘文坛 <[email protected]>2023-12-14 06:48:44 +0000
committer刘文坛 <[email protected]>2023-12-14 06:48:44 +0000
commita4254d7b60b1aee385c1531ab8c6f7f69b074b90 (patch)
tree411ce4bb8a0019a9ecac2344be6c1f99d3f0b928
parent37117f9f350c0c7b624b8f74f0b03cb8ee20be72 (diff)
[HTTP_DECODER]first simple http unit_test passed
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--CMakeLists.txt4
-rw-r--r--examples/stellar_plugin/simple_plugin.toml2
-rw-r--r--include/stellar/session.h2
-rw-r--r--include/stellar/utils.h4
-rw-r--r--src/http_decoder/CMakeLists.txt9
-rw-r--r--src/http_decoder/http_content_decompress.c261
-rw-r--r--src/http_decoder/http_content_decompress.h50
-rw-r--r--src/http_decoder/http_decoder.c504
-rw-r--r--src/http_decoder/http_decoder.h93
-rw-r--r--src/http_decoder/http_decoder.toml4
-rw-r--r--src/http_decoder/http_decoder_half.c729
-rw-r--r--src/http_decoder/http_decoder_half.h82
-rw-r--r--src/http_decoder/http_decoder_string.c176
-rw-r--r--src/http_decoder/http_decoder_string.h87
-rw-r--r--src/http_decoder/http_decoder_table.c464
-rw-r--r--src/http_decoder/http_decoder_table.h82
-rw-r--r--src/http_decoder/http_decoder_utils.c25
-rw-r--r--src/http_decoder/http_decoder_utils.h65
-rw-r--r--src/http_decoder/version.map7
-rw-r--r--src/stellar_on_sapp/start_loader.inf12
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/http_decoder/CMakeLists.txt40
-rw-r--r--test/http_decoder/http_decoder_gtest.cpp185
-rw-r--r--test/http_decoder/http_pcap/http_simple.pcapbin0 -> 1359 bytes
-rw-r--r--test/http_decoder/http_pcap/simple_http.pcapbin0 -> 1359 bytes
-rw-r--r--test/http_decoder/test_env/conflist.inf9
-rw-r--r--test/http_decoder/test_env/sapp4.el8.x86_64.rpmbin0 -> 1052968 bytes
-rw-r--r--test/http_decoder/test_env/spec.toml11
-rw-r--r--test/http_decoder/test_env/start_loader.inf17
-rw-r--r--test/http_decoder/test_env/tsg_l7_protocol.conf57
-rw-r--r--test/http_decoder/test_result_json/empty_array.json1
-rw-r--r--test/http_decoder/test_result_json/http_simple_result.json28
-rw-r--r--test/http_decoder/test_result_json/proto_identify_http.json10
-rw-r--r--vendor/CMakeLists.txt15
-rw-r--r--vendor/llhttp-release-v9.1.3.tar.gzbin0 -> 37771 bytes
36 files changed, 3029 insertions, 12 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0258f1d..74ecd3f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,8 +3,8 @@ variables:
BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/
BUILD_IMAGE_CENTOS7: "git.mesalab.cn:7443/mesa_platform/build-env:centos7-for-sapp"
BUILD_IMAGE_CENTOS8: "git.mesalab.cn:7443/mesa_platform/build-env:rocky8-for-sapp"
- INSTALL_DEPENDENCY_LIBRARY:
- sapp sapp-devel framework_env
+ INSTALL_DEPENDENCY_LIBRARY: sapp sapp-devel framework_env libMESA_prof_load-devel
+ libcjson-devel zlib-devel brotli-devel
SYMBOL_TARGET: stellar-c
TEST_NAME: gtest_stellar-c
INSTALL_PREFIX: "/opt/tsg/"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22dc9b8..90bef99 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,8 @@ if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set (CMAKE_INSTALL_PREFIX "/opt/tsg/" CACHE PATH "default install path" FORCE)
endif()
+LINK_DIRECTORIES(/opt/MESA/lib /usr/lib64)
+
find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck)
if (CMAKE_CXX_CPPCHECK)
list(
@@ -76,13 +78,13 @@ add_subdirectory(vendor)
add_subdirectory(deps/toml)
add_subdirectory(src/adapter)
add_subdirectory(src/stellar_on_sapp)
+add_subdirectory(src/http_decoder)
add_subdirectory(examples/sapp_plugin)
add_subdirectory(examples/stellar_plugin)
enable_testing()
add_subdirectory(test)
-
install(FILES include/stellar/session_exdata.h DESTINATION ${CMAKE_INSTALL_PREFIX}/framework/include/stellar COMPONENT Headers)
install(FILES include/stellar/session_mq.h DESTINATION ${CMAKE_INSTALL_PREFIX}/framework/include/stellar COMPONENT Headers)
install(FILES include/stellar/session.h DESTINATION ${CMAKE_INSTALL_PREFIX}/framework/include/stellar COMPONENT Headers)
diff --git a/examples/stellar_plugin/simple_plugin.toml b/examples/stellar_plugin/simple_plugin.toml
index 24019e7..ce08b65 100644
--- a/examples/stellar_plugin/simple_plugin.toml
+++ b/examples/stellar_plugin/simple_plugin.toml
@@ -7,4 +7,4 @@ exit = "simple_stellar_event_plugin_exit"
[[plugin]]
path = "./stellar_plugin/simple_stellar_plugin.so"
init = "simple_stellar_mq_plugin_init"
-exit = "simple_stellar_mq_plugin_exit"
+exit = "simple_stellar_mq_plugin_exit" \ No newline at end of file
diff --git a/include/stellar/session.h b/include/stellar/session.h
index 6ce5625..e8f51f5 100644
--- a/include/stellar/session.h
+++ b/include/stellar/session.h
@@ -110,7 +110,7 @@ const struct packet *session_get0_current_packet(struct session *sess);
#define PACKET_DIRECTION_C2S 0
#define PACKET_DIRECTION_S2C 1
-int packet_get_direction(const struct packet *pkt);
+int packet_get_direction(const struct packet *pkt);
const char *packet_get0_data(const struct packet *pkt, size_t *data_len);
int packet_arrive_time(const struct packet *pkt, struct timeval *ts);
diff --git a/include/stellar/utils.h b/include/stellar/utils.h
index 7beafbb..eb5e705 100644
--- a/include/stellar/utils.h
+++ b/include/stellar/utils.h
@@ -5,7 +5,9 @@
#define CALLOC(type, number) ((type *)calloc(sizeof(type), number))
-#define FREE(p) {free(p);p=NULL;}
+#define REALLOC(type, ptr, number) ((type *)realloc(ptr, (number) * sizeof(type)))
+
+#define FREE(p) {free(p); p = NULL;}
#define TRUE 1
#define FALSE 0
diff --git a/src/http_decoder/CMakeLists.txt b/src/http_decoder/CMakeLists.txt
new file mode 100644
index 0000000..1655631
--- /dev/null
+++ b/src/http_decoder/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_definitions(-fPIC)
+
+set(HTTP_SRC http_decoder.c http_decoder_utils.c http_decoder_half.c
+ http_decoder_table.c http_decoder_string.c http_content_decompress.c)
+
+add_library(http_decoder SHARED ${HTTP_SRC})
+#set_target_properties(http_decoder_shared PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/version.map")
+target_link_libraries(http_decoder brotlidec llhttp-static)
+set_target_properties(http_decoder PROPERTIES PREFIX "") \ No newline at end of file
diff --git a/src/http_decoder/http_content_decompress.c b/src/http_decoder/http_content_decompress.c
new file mode 100644
index 0000000..fa99677
--- /dev/null
+++ b/src/http_decoder/http_content_decompress.c
@@ -0,0 +1,261 @@
+/*
+**********************************************************************************************
+* File: http_content_decompress.c
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <zlib.h>
+#include <string.h>
+#include <assert.h>
+#include <brotli/decode.h>
+
+#include "stellar/utils.h"
+#include "http_decoder_utils.h"
+#include "http_content_decompress.h"
+
+#define BUFFER_SIZE (1024 * 1024 * 4)
+
+struct http_content_decompress {
+ enum http_content_encoding encoding;
+
+ z_stream *z_stream_ptr;
+ BrotliDecoderState *brdec_state;
+
+ char *buffer;
+ size_t buffer_size;
+};
+
+enum http_content_encoding http_content_encoding_str2int(const char *content_encoding)
+{
+ if (strcasestr(content_encoding, "gzip") != NULL) {
+ return HTTP_CONTENT_ENCODING_GZIP;
+ }
+
+ if (strcasestr(content_encoding, "deflate") != NULL) {
+ return HTTP_CONTENT_ENCODING_DEFLATE;
+ }
+
+ if (strcasestr(content_encoding, "br") != NULL) {
+ return HTTP_CONTENT_ENCODING_BR;
+ }
+
+ return HTTP_CONTENT_ENCODING_NONE;
+}
+
+const char *http_content_encoding_int2str(enum http_content_encoding content_encoding)
+{
+ if (content_encoding == HTTP_CONTENT_ENCODING_GZIP) {
+ return "gzip";
+ }
+
+ if (content_encoding == HTTP_CONTENT_ENCODING_DEFLATE) {
+ return "deflate";
+ }
+
+ if (content_encoding == HTTP_CONTENT_ENCODING_BR) {
+ return "br";
+ }
+
+ return "unknown";
+}
+
+struct http_content_decompress *
+http_content_decompress_create(enum http_content_encoding encoding)
+{
+ struct http_content_decompress *decompress = CALLOC(struct http_content_decompress, 1);
+ assert(decompress);
+
+ decompress->encoding = encoding;
+ decompress->z_stream_ptr = NULL;
+ decompress->brdec_state = NULL;
+
+ decompress->buffer = CALLOC(char, BUFFER_SIZE);
+ assert(decompress->buffer);
+ decompress->buffer_size = BUFFER_SIZE;
+
+ if (encoding == HTTP_CONTENT_ENCODING_GZIP ||
+ encoding == HTTP_CONTENT_ENCODING_DEFLATE) {
+ decompress->z_stream_ptr = CALLOC(z_stream, 1);
+ assert(decompress->z_stream_ptr);
+
+ decompress->z_stream_ptr->zalloc = NULL;
+ decompress->z_stream_ptr->zfree = NULL;
+ decompress->z_stream_ptr->opaque = NULL;
+ decompress->z_stream_ptr->avail_in = 0;
+ decompress->z_stream_ptr->next_in = Z_NULL;
+
+ if (encoding == HTTP_CONTENT_ENCODING_GZIP) {
+ if (inflateInit2(decompress->z_stream_ptr, MAX_WBITS + 16) != Z_OK) {
+ goto error;
+ }
+ }
+
+ if (encoding == HTTP_CONTENT_ENCODING_DEFLATE) {
+ if (inflateInit2(decompress->z_stream_ptr, -MAX_WBITS) != Z_OK) {
+ goto error;
+ }
+ }
+ }
+
+ if (encoding == HTTP_CONTENT_ENCODING_BR) {
+ decompress->brdec_state = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+ if (decompress->brdec_state == NULL) {
+ goto error;
+ }
+ }
+
+ return decompress;
+
+error:
+ http_content_decompress_destroy(decompress);
+ return NULL;
+}
+
+void http_content_decompress_destroy(struct http_content_decompress *decompress)
+{
+ if (NULL == decompress) {
+ return;
+ }
+
+ if (decompress->z_stream_ptr != NULL) {
+ inflateEnd(decompress->z_stream_ptr);
+ FREE(decompress->z_stream_ptr);
+ }
+
+ if (decompress->brdec_state) {
+ BrotliDecoderDestroyInstance(decompress->brdec_state);
+ decompress->brdec_state = NULL;
+ }
+
+ FREE(decompress->buffer);
+ FREE(decompress);
+}
+
+static int http_content_decompress_write_zlib(struct http_content_decompress *decompress,
+ const char *indata, size_t indata_len,
+ char **outdata, size_t *outdata_len)
+{
+ z_stream *z_stream_ptr = decompress->z_stream_ptr;
+ z_stream_ptr->avail_in = (unsigned int)indata_len;
+ z_stream_ptr->next_in = (unsigned char *)indata;
+
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ int ret = 0;
+ do {
+ z_stream_ptr->avail_out = (unsigned int)decompress->buffer_size;
+ z_stream_ptr->next_out = (unsigned char *)decompress->buffer;
+
+ ret = inflate(z_stream_ptr, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT ||
+ ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
+ (void)inflateEnd(z_stream_ptr);
+ return -1;
+ }
+
+ size_t have = decompress->buffer_size - z_stream_ptr->avail_out;
+ if (have > 0) {
+ if (NULL == *outdata) {
+ http_decoder_log(DEBUG, "%s alloc outbuffer %zu bytes",
+ http_content_encoding_int2str(decompress->encoding), have);
+ *outdata = safe_dup(decompress->buffer, have);
+ *outdata_len = have;
+ } else {
+ http_decoder_log(DEBUG, "%s realloc outbuffer %zu bytes",
+ http_content_encoding_int2str(decompress->encoding),
+ *outdata_len + have + 1);
+ *outdata = REALLOC(char, *outdata, *outdata_len + have + 1);
+ memcpy(*outdata + *outdata_len, decompress->buffer, have);
+ *outdata_len = *outdata_len + have;
+ memset(*outdata + *outdata_len, 0, 1);
+ }
+ }
+ } while (0 == z_stream_ptr->avail_out);
+
+ return 0;
+}
+
+static int http_content_decompress_write_br(struct http_content_decompress *decompress,
+ const char *indata, size_t indata_len,
+ char **outdata, size_t *outdata_len)
+{
+ size_t available_out;
+ size_t available_in = indata_len;
+ const unsigned char *next_in = (const unsigned char *)indata;
+ unsigned char *next_out;
+
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ int ret;
+ for (;;) {
+ available_out = decompress->buffer_size;
+ next_out = (unsigned char *)decompress->buffer;
+
+ ret = BrotliDecoderDecompressStream(decompress->brdec_state, &available_in,
+ &next_in, &available_out, &next_out, 0);
+ size_t have = decompress->buffer_size - available_out;
+ if (have > 0) {
+ if (NULL == *outdata) {
+ http_decoder_log(DEBUG, "%s alloc outbuffer %zu bytes",
+ http_content_encoding_int2str(decompress->encoding), have);
+ *outdata = safe_dup(decompress->buffer, have);
+ *outdata_len = have;
+ } else {
+ http_decoder_log(DEBUG, "%s realloc outbuffer %zu bytes",
+ http_content_encoding_int2str(decompress->encoding),
+ *outdata_len + have + 1);
+ *outdata = REALLOC(char, *outdata, *outdata_len + have + 1);
+ memcpy(*outdata + *outdata_len, decompress->buffer, have);
+ *outdata_len = *outdata_len + have;
+ memset(*outdata + *outdata_len, 0, 1);
+ }
+ }
+
+ if (ret == BROTLI_DECODER_RESULT_SUCCESS ||
+ ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
+ return 0;
+ }
+
+ if (ret == BROTLI_DECODER_RESULT_ERROR) {
+ BrotliDecoderErrorCode errcode = BrotliDecoderGetErrorCode(decompress->brdec_state);
+ http_decoder_log(ERROR, "BrotliDecoderDecompressStream() failed: errno = %d, %s",
+ errcode, BrotliDecoderErrorString(errcode));
+ return -1;
+ }
+
+ assert(ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
+ }
+}
+
+int http_content_decompress_write(struct http_content_decompress *decompress, const char *indata,
+ size_t indata_len, char **outdata, size_t *outdata_len)
+{
+ assert(decompress);
+ assert(indata);
+ assert(indata_len > 0);
+ assert(outdata);
+ assert(outdata_len);
+
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ if (decompress->encoding == HTTP_CONTENT_ENCODING_GZIP ||
+ decompress->encoding == HTTP_CONTENT_ENCODING_DEFLATE) {
+ return http_content_decompress_write_zlib(decompress, indata, indata_len,
+ outdata, outdata_len);
+ }
+
+ if (decompress->encoding == HTTP_CONTENT_ENCODING_BR) {
+ return http_content_decompress_write_br(decompress, indata, indata_len,
+ outdata, outdata_len);
+ }
+
+ assert(0);
+ return -1;
+} \ No newline at end of file
diff --git a/src/http_decoder/http_content_decompress.h b/src/http_decoder/http_content_decompress.h
new file mode 100644
index 0000000..66ea567
--- /dev/null
+++ b/src/http_decoder/http_content_decompress.h
@@ -0,0 +1,50 @@
+/*
+**********************************************************************************************
+* File: http_content_decompress.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_CONTENT_DECOMPRESS_H_
+#define _HTTP_CONTENT_DECOMPRESS_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+// yum install -y zlib-devel
+// yum install -y brotli-devel
+
+enum http_content_encoding {
+ HTTP_CONTENT_ENCODING_NONE = 0,
+ HTTP_CONTENT_ENCODING_GZIP = 1 << 1,
+ HTTP_CONTENT_ENCODING_DEFLATE = 1 << 2,
+ HTTP_CONTENT_ENCODING_BR = 1 << 3,
+};
+
+struct http_content_decompress;
+
+enum http_content_encoding http_content_encoding_str2int(const char *content_encoding);
+
+const char *http_content_encoding_int2str(enum http_content_encoding content_encoding);
+
+struct http_content_decompress *http_content_decompress_create(enum http_content_encoding encoding);
+
+void http_content_decompress_destroy(struct http_content_decompress *decompress);
+
+// return 0 : success
+// return -1 : error
+int http_content_decompress_write(struct http_content_decompress *decompress, const char *indata,
+ size_t indata_len, char **outdata, size_t *outdata_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/http_decoder.c b/src/http_decoder/http_decoder.c
new file mode 100644
index 0000000..ce767fb
--- /dev/null
+++ b/src/http_decoder/http_decoder.c
@@ -0,0 +1,504 @@
+/*
+**********************************************************************************************
+* File: http_decoder_entry.c
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "stellar/utils.h"
+#include "stellar/session.h"
+#include "stellar/session_mq.h"
+#include "stellar/session_exdata.h"
+#include "http_decoder.h"
+#include "http_decoder_half.h"
+#include "http_decoder_table.h"
+#include "llhttp.h"
+
+#define HTTP_IDENTIFY_LEN 16
+#define HTTP_DECODER_RESULT_QUEUE_SIZE 16
+
+#define HTTP_DECODER_IS_CACHE_LINE 1
+#define HTTP_DECODER_IS_CACHE_BODY 1
+#define HTTP_DECODER_IS_CACHE_HEADER 1
+
+const char *http_decoder_topic = "HTTP_DECODER_MESSAGE";
+
+struct http_decoder_result {
+ struct session *ref_session;
+ struct http_decoder_half_data *req_data;
+ struct http_decoder_half_data *res_data;
+};
+
+struct http_decoder_result_queue {
+ size_t req_index;
+ size_t res_index;
+ size_t del_index;
+ size_t queue_size;
+ struct http_decoder_result **array;
+};
+
+/**
+ * NOTE: http_message don't have the ownership of data
+*/
+struct http_message {
+ enum http_message_type type;
+ struct http_decoder_half_data *data;
+};
+
+struct http_decoder {
+ struct http_decoder_half *c2s_half;
+ struct http_decoder_half *s2c_half;
+};
+
+struct http_decoder_context {
+ int plugin_id;
+ int topic_id;
+ int ex_data_idx;
+ struct stellar *st;
+ struct http_decoder *decoder;
+ struct http_decoder_result_queue *ref_queue;
+};
+
+struct http_decoder_result *http_decoder_result_new()
+{
+ struct http_decoder_result *result = CALLOC(struct http_decoder_result, 1);
+ assert(result);
+
+ result->req_data = NULL;
+ result->res_data = NULL;
+
+ return result;
+}
+
+void http_decoder_result_free(struct http_decoder_result *result)
+{
+ if (NULL == result) {
+ return;
+ }
+
+ if (result->req_data != NULL) {
+ http_decoder_half_data_free(result->req_data);
+ result->req_data = NULL;
+ }
+
+ if (result->res_data != NULL) {
+ http_decoder_half_data_free(result->res_data);
+ result->res_data = NULL;
+ }
+
+ FREE(result);
+}
+
+// Create a new http result and add it to the queue
+static void http_decoder_result_queue_push(struct http_decoder_result_queue *queue, size_t index)
+{
+ assert(queue);
+ assert(index < queue->queue_size);
+
+ if (queue->array[index] == NULL) {
+ queue->array[index] = http_decoder_result_new();
+ assert(queue->array[index]);
+ }
+}
+
+// Remove the http result from the queue but not destroy it
+static void http_decoder_result_queue_pop(struct http_decoder_result_queue *queue, size_t index)
+{
+ assert(queue);
+ assert(index < queue->queue_size);
+
+ if (queue->array[index] != NULL) {
+ http_decoder_result_free(queue->array[index]);
+ queue->array[index] = NULL;
+ }
+}
+
+static void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue)
+{
+ assert(queue);
+
+ queue->req_index++;
+ queue->req_index = queue->req_index % queue->queue_size;
+}
+
+static void http_decoder_result_queue_inc_res_index(struct http_decoder_result_queue *queue)
+{
+ assert(queue);
+
+ queue->res_index++;
+ queue->res_index = queue->res_index % queue->queue_size;
+}
+
+static void http_decoder_result_queue_inc_del_index(struct http_decoder_result_queue *queue)
+{
+ assert(queue);
+
+ queue->del_index++;
+ queue->del_index = queue->del_index % queue->queue_size;
+}
+
+static void http_decoder_result_queue_gc(struct http_decoder_result_queue *queue, size_t index)
+{
+ assert(queue);
+ assert(index < queue->queue_size);
+
+ if (index == queue->del_index) {
+ http_decoder_result_queue_pop(queue, index);
+ http_decoder_result_queue_inc_del_index(queue);
+ }
+}
+
+static void http_event_handler(enum http_event event,
+ struct http_decoder_half_data **data,
+ void *cb_args)
+{
+ struct http_decoder_context *ctx = (struct http_decoder_context *)cb_args;
+ assert(ctx);
+
+ printf("ctx->topic_id:%d\n", ctx->topic_id);
+ assert(ctx->ref_queue);
+ struct http_decoder_result_queue *queue = ctx->ref_queue;
+
+ switch (event) {
+ case HTTP_EVENT_INIT:
+ *data = http_decoder_half_data_new();
+ break;
+ case HTTP_EVENT_REQ_LINE:
+ if (NULL == queue->array[queue->req_index]) {
+ http_decoder_result_queue_push(queue, queue->req_index);
+ }
+ queue->array[queue->req_index]->req_data = *data;
+ break;
+ case HTTP_EVENT_REQ_HDR:
+ break;
+ case HTTP_EVENT_REQ_HDR_END:
+ break;
+ case HTTP_EVENT_REQ_BODY_BEGIN:
+ break;
+ case HTTP_EVENT_REQ_BODY_DATA:
+ break;
+ case HTTP_EVENT_REQ_BODY_END:
+ break;
+ case HTTP_EVENT_REQ_END:
+ http_decoder_result_queue_inc_req_index(queue);
+ http_decoder_result_queue_gc(queue, queue->req_index);
+ break;
+ case HTTP_EVENT_RES_LINE:
+ queue->array[queue->res_index]->res_data = *data;
+ break;
+ case HTTP_EVENT_RES_HDR:
+ break;
+ case HTTP_EVENT_RES_HDR_END:
+ break;
+ case HTTP_EVENT_RES_BODY_BEGIN:
+ break;
+ case HTTP_EVENT_RES_BODY_DATA:
+ break;
+ case HTTP_EVENT_RES_BODY_END:
+ break;
+ case HTTP_EVENT_RES_END:
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static struct http_decoder *http_decoder_new()
+{
+ struct http_decoder *decoder = CALLOC(struct http_decoder, 1);
+ assert(decoder);
+
+ decoder->c2s_half = http_decoder_half_new();
+ decoder->s2c_half = http_decoder_half_new();
+
+ return decoder;
+}
+
+void http_decoder_reinit(struct http_decoder *decoder, void *args)
+{
+ http_decoder_half_init(decoder->c2s_half, http_event_handler, args,
+ HTTP_DECODER_IS_CACHE_LINE,
+ HTTP_DECODER_IS_CACHE_BODY,
+ HTTP_DECODER_IS_CACHE_HEADER);
+
+ http_decoder_half_init(decoder->s2c_half, http_event_handler, args,
+ HTTP_DECODER_IS_CACHE_LINE,
+ HTTP_DECODER_IS_CACHE_BODY,
+ HTTP_DECODER_IS_CACHE_HEADER);
+}
+
+static struct http_decoder_result_queue *http_decoder_result_queue_new(size_t queue_size)
+{
+ struct http_decoder_result_queue *queue = CALLOC(struct http_decoder_result_queue, 1);
+ assert(queue);
+
+ queue->del_index = 0;
+ queue->req_index = 0;
+ queue->res_index = 0;
+ queue->queue_size = queue_size;
+
+ queue->array = CALLOC(struct http_decoder_result *, queue->queue_size);
+ assert(queue->array);
+
+ return queue;
+}
+
+static void http_decoder_result_queue_free(struct http_decoder_result_queue *queue)
+{
+ if (NULL == queue) {
+ return;
+ }
+
+ if (queue->array != NULL) {
+ for (size_t i = 0; i < queue->queue_size; i++) {
+ if (queue->array[i] != NULL) {
+ http_decoder_result_free(queue->array[i]);
+ queue->array[i] = NULL;
+ }
+ }
+
+ FREE(queue->array);
+ }
+
+ FREE(queue);
+}
+
+static struct http_decoder_context *http_decoder_context_new()
+{
+ struct http_decoder_context *ctx = CALLOC(struct http_decoder_context, 1);
+ assert(ctx);
+
+ return ctx;
+}
+
+static void http_decoder_context_free(struct http_decoder_context *ctx)
+{
+ if (NULL == ctx) {
+ return;
+ }
+}
+
+static int http_protocol_identify(const char *data, size_t data_len)
+{
+ enum llhttp_type type = HTTP_BOTH;
+ llhttp_t parser;
+ llhttp_settings_t settings;
+ enum llhttp_errno error;
+
+ llhttp_settings_init(&settings);
+ llhttp_init(&parser, type, &settings);
+
+ error = llhttp_execute(&parser, data, data_len);
+ if (error != HPE_OK) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int http_decoder_entry(struct session *s, int events, const struct packet *pkt, void *cb_arg)
+{
+ int ret = 0;
+ size_t payload_len = 0;
+ struct http_decoder_context *ctx = (struct http_decoder_context *)cb_arg;
+
+ const char *payload = session_get0_current_payload(s, &payload_len);
+ size_t http_identify_len = payload_len > HTTP_IDENTIFY_LEN ? HTTP_IDENTIFY_LEN : payload_len;
+
+ struct http_decoder_result_queue *queue = session_get_ex_data(s, ctx->ex_data_idx);
+ if (NULL == queue) {
+ ret = http_protocol_identify(payload, http_identify_len);
+ printf("http_protocol_identify ret:%d\n", ret);
+ if (ret < 0) {
+ //ignore this session's event
+ struct session_event *s_event = session_get_intrinsic_event(s, ctx->plugin_id);
+ session_event_assign(s_event, ctx->st, s, 0, http_decoder_entry, ctx);
+ return 0;
+ }
+
+ queue = http_decoder_result_queue_new(HTTP_DECODER_RESULT_QUEUE_SIZE);
+ session_set_ex_data(s, ctx->ex_data_idx, queue);
+ }
+
+ ctx->ref_queue = queue;
+
+ int dir = packet_get_direction(pkt);
+ if (dir < 0) {
+ return -1;
+ }
+
+ // printf("\n-------------------------------------------\n");
+ // for (size_t i = 0; i < payload_len; i++) {
+ // printf(" %x", payload[i]);
+ // }
+ // printf("\n-------------------------------------------\n");
+
+ if (NULL == ctx->decoder) {
+ ctx->decoder = http_decoder_new();
+ http_decoder_reinit(ctx->decoder, ctx);
+ }
+
+ struct http_decoder_half *cur_half = NULL;
+ if (dir == PACKET_DIRECTION_C2S) {
+ cur_half = ctx->decoder->c2s_half;
+ } else {
+ cur_half = ctx->decoder->s2c_half;
+ }
+
+ ret = http_decoder_half_parse(cur_half, payload, payload_len);
+ if (ret < 0) {
+ if (dir == PACKET_DIRECTION_C2S) {
+ http_decoder_result_queue_pop(queue, queue->req_index);
+ } else {
+ http_decoder_result_queue_pop(queue, queue->res_index);
+ }
+ }
+
+ // struct http_message *msg = CALLOC(struct http_message, 1);
+ // msg->type = HTTP_MESSAGE_REQ_LINE;
+ // session_mq_publish_message(s, ctx->topic_id, msg);
+
+ return 0;
+}
+
+static void http_decoder_ex_data_free(struct session *s, int idx, void *ex_ptr, void *arg)
+{
+ if (ex_ptr != NULL) {
+ FREE(ex_ptr);
+ }
+}
+
+void http_message_free(void *msg, void *cb_arg)
+{
+ if (NULL == msg) {
+ return;
+ }
+
+ struct http_message *message = (struct http_message *)msg;
+ message->data = NULL; //don't have memory's ownership
+
+ FREE(message);
+}
+
+void *http_decoder_init(struct stellar *st)
+{
+ struct http_decoder_context *ctx = http_decoder_context_new();
+
+ ctx->st = st;
+ ctx->ex_data_idx = stellar_session_get_ex_new_index(st, "HTTP_DECODER",
+ http_decoder_ex_data_free, NULL);
+
+ int plugin_id = stellar_plugin_register(st, SESS_EV_TCP, http_decoder_entry, ctx);
+ if (plugin_id >= 0) {
+ ctx->plugin_id = plugin_id;
+ }
+
+ int topic_id = session_mq_get_topic_id(st, http_decoder_topic);
+ if (topic_id < 0) {
+ topic_id = session_mq_create_topic(st, http_decoder_topic, http_message_free, NULL);
+ }
+ ctx->topic_id = topic_id;
+
+ printf("http_decoder_init: ex_data_idx:%d, plugin_id:%d, topic_id:%d\n",
+ ctx->ex_data_idx, ctx->plugin_id, ctx->topic_id);
+ return ctx;
+}
+
+void http_decoder_exit(void *ctx)
+{
+ if (NULL == ctx) {
+ return;
+ }
+
+ FREE(ctx);
+}
+
+enum http_message_type http_message_type(struct http_message *msg)
+{
+ if (NULL == msg) {
+ return HTTP_MESSAGE_MAX;
+ }
+
+ return msg->type;
+}
+
+int http_message_get_request_line(struct http_message *msg, struct http_request_line *line)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_LINE ||
+ NULL == line) {
+ return -1;
+ }
+
+ return http_decoder_half_data_get_request_line(msg->data, line);
+}
+
+int http_message_get_response_line(struct http_message *msg, struct http_response_line *line)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_RES_LINE ||
+ NULL == line) {
+ return -1;
+ }
+
+ return http_decoder_half_data_get_response_line(msg->data, line);
+}
+
+int http_message_get_request_header(struct http_message *msg, struct hstring *key,
+ struct http_header *header_array, size_t array_size)
+{
+ if (NULL == msg ||
+ (msg->type != HTTP_MESSAGE_REQ_HEADER && msg->type != HTTP_MESSAGE_REQ_HEADER_END) ||
+ NULL == header_array || 0 == array_size) {
+ return -1;
+ }
+
+ return http_decoder_half_data_get_header(msg->data, key, header_array, array_size);
+}
+
+int http_message_get_response_header(struct http_message *msg, struct hstring *key,
+ struct http_header *header_array, size_t array_size)
+{
+ if (NULL == msg ||
+ (msg->type != HTTP_MESSAGE_RES_HEADER && msg->type != HTTP_MESSAGE_RES_HEADER_END) ||
+ NULL == header_array || 0 == array_size) {
+ return -1;
+ }
+
+ return http_decoder_half_data_get_header(msg->data, key, header_array, array_size);
+}
+
+int http_message_request_header_next(struct http_message *msg, struct http_header *header)
+{
+ return 0;
+}
+
+int http_message_response_header_next(struct http_message *msg, struct http_header *header)
+{
+ return 0;
+}
+
+void http_message_get_request_raw_body(struct http_message *msg, struct hstring *body)
+{
+
+}
+
+void http_message_get_response_raw_body(struct http_message *msg, struct hstring *body)
+{
+
+}
+
+void http_message_get_request_decompress_body(struct http_message *msg, struct hstring *body)
+{
+
+}
+
+void http_message_get_response_decompress_body(struct http_message *msg, struct hstring *body)
+{
+
+} \ No newline at end of file
diff --git a/src/http_decoder/http_decoder.h b/src/http_decoder/http_decoder.h
new file mode 100644
index 0000000..ed5724e
--- /dev/null
+++ b/src/http_decoder/http_decoder.h
@@ -0,0 +1,93 @@
+/*
+**********************************************************************************************
+* File: http_decoder.h
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_H_
+#define _HTTP_DECODER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+enum http_message_type {
+ HTTP_MESSAGE_REQ_LINE,
+ HTTP_MESSAGE_REQ_HEADER,
+ HTTP_MESSAGE_REQ_HEADER_END,
+ HTTP_MESSAGE_REQ_BODY,
+ HTTP_MESSAGE_RES_LINE,
+ HTTP_MESSAGE_RES_HEADER,
+ HTTP_MESSAGE_RES_HEADER_END,
+ HTTP_MESSAGE_RES_BODY,
+ HTTP_MESSAGE_MAX
+};
+
+//http string
+struct hstring {
+ char *str;
+ size_t str_len;
+};
+
+struct http_header {
+ struct hstring key;
+ struct hstring val;
+};
+
+struct http_request_line {
+ struct hstring method;
+ struct hstring uri;
+ struct hstring version;
+
+ int major_version;
+ int minor_version;
+};
+
+struct http_response_line {
+ struct hstring version;
+ struct hstring status;
+
+ int major_version;
+ int minor_version;
+ int status_code;
+};
+
+struct http_message;
+
+enum http_message_type http_message_type(struct http_message *msg);
+
+int http_message_get_request_line(struct http_message *msg, struct http_request_line *line);
+
+int http_message_get_response_line(struct http_message *msg, struct http_response_line *line);
+
+/* same key may has multiple kv */
+int http_message_get_request_header(struct http_message *msg, struct hstring *key,
+ struct http_header *header_array, size_t array_size);
+
+int http_message_get_response_header(struct http_message *msg, struct hstring *key,
+ struct http_header *header_array, size_t array_size);
+
+int http_message_request_header_next(struct http_message *msg, struct http_header *header);
+
+int http_message_response_header_next(struct http_message *msg, struct http_header *header);
+
+void http_message_get_request_raw_body(struct http_message *msg, struct hstring *body);
+
+void http_message_get_response_raw_body(struct http_message *msg, struct hstring *body);
+
+void http_message_get_request_decompress_body(struct http_message *msg, struct hstring *body);
+
+void http_message_get_response_decompress_body(struct http_message *msg, struct hstring *body);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/http_decoder.toml b/src/http_decoder/http_decoder.toml
new file mode 100644
index 0000000..f7717a7
--- /dev/null
+++ b/src/http_decoder/http_decoder.toml
@@ -0,0 +1,4 @@
+[[plugin]]
+path = "./src/http_decoder/http_decoder.so"
+init = "http_decoder_init"
+exit = "http_decoder_exit" \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_half.c b/src/http_decoder/http_decoder_half.c
new file mode 100644
index 0000000..c0b195b
--- /dev/null
+++ b/src/http_decoder/http_decoder_half.c
@@ -0,0 +1,729 @@
+/*
+**********************************************************************************************
+* File: http_decoder_half.c
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "stellar/utils.h"
+#include "llhttp.h"
+#include "http_decoder_utils.h"
+#include "http_decoder_half.h"
+#include "http_decoder_table.h"
+#include "http_content_decompress.h"
+
+struct http_decoder_half_data {
+ struct http_decoder_table *table;
+
+ int major_version;
+ int minor_version;
+ int status_code;
+
+ enum http_content_encoding content_encoding;
+ char *decompress_body;
+ size_t decompress_body_len;
+};
+
+struct http_decoder_half {
+ llhttp_t parser;
+ llhttp_settings_t settings;
+ enum llhttp_errno error;
+
+ int is_cache_line;
+ int is_cache_header;
+ int is_cache_body;
+
+ struct http_decoder_half_data *data;
+ enum http_event event;
+ http_event_cb *event_cb;
+ void *cb_args;
+};
+
+struct http_decoder_half *http_decoder_half_new()
+{
+ struct http_decoder_half *half = CALLOC(struct http_decoder_half, 1);
+ assert(half);
+
+ return half;
+}
+
+void http_decoder_half_free(struct http_decoder_half *half)
+{
+ if (NULL == half) {
+ return;
+ }
+
+ if (half->data != NULL) {
+ http_decoder_half_data_free(half->data);
+ half->data = NULL;
+ }
+
+ FREE(half);
+}
+
+#ifdef HTTP_DECODER_DEBUG
+static void printf_debug_info(const char *desc, const char *at, size_t length)
+{
+ if (at)
+ {
+ char *temp = safe_dup(at, length);
+ printf("HTTP PARSER STAGE: %s: %s\n", desc, temp);
+ safe_free(temp);
+ }
+ else
+ {
+ printf("HTTP PARSER STAGE: %s\n", desc);
+ }
+}
+#else
+#define printf_debug_info(desc, at, length)
+#endif
+
+static void http_decoder_half_data_decompress(struct http_decoder_half_data *data)
+{
+ assert(data);
+
+ FREE(data->decompress_body);
+ data->decompress_body_len = 0;
+
+ if (data->content_encoding == HTTP_CONTENT_ENCODING_NONE) {
+ return;
+ }
+
+ struct hstring raw_body = {0};
+ http_decoder_table_get_body(data->table, &raw_body);
+ if (raw_body.str == NULL || raw_body.str_len == 0) {
+ return;
+ }
+
+ struct http_content_decompress *decompress = http_content_decompress_create(data->content_encoding);
+ assert(decompress);
+ if (http_content_decompress_write(decompress, raw_body.str, raw_body.str_len,
+ &data->decompress_body,
+ &data->decompress_body_len) == -1) {
+ // log error
+ }
+ http_content_decompress_destroy(decompress);
+}
+
+/* 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+ assert(half->data == NULL);
+
+ half->event = HTTP_EVENT_INIT;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+
+ return 0;
+}
+
+static int on_message_complete(llhttp_t *http)
+{
+ printf_debug_info("on_message_complete", NULL, 0);
+
+ struct http_decoder_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ //trigger http_body end
+ if (half->parser.type == HTTP_REQUEST) {
+ if (half->event == HTTP_EVENT_REQ_BODY_DATA) {
+ half->event = HTTP_EVENT_REQ_BODY_END;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+ } else {
+ if (half->event == HTTP_EVENT_RES_BODY_DATA) {
+ half->event = HTTP_EVENT_RES_BODY_END;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+ }
+
+ //trigger req/res end
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_END;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ } else {
+ half->event = HTTP_EVENT_RES_END;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ return 0;
+}
+
+static int on_reset(llhttp_t *http)
+{
+ printf_debug_info("on_reset", NULL, 0);
+
+ return 0;
+}
+
+static int on_method(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_method", at, length);
+
+ struct http_decoder_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_METHOD, at, length);
+ }
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_method_complete(llhttp_t *http)
+{
+ printf_debug_info("on_method_complete", NULL, 0);
+
+ struct http_decoder_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ // if (http_decoder_table_state(half->data->table, HTTP_ITEM_METHOD) == STRING_STATE_REFER) {
+ // http_decoder_table_cache(half->data->table, HTTP_ITEM_METHOD);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_METHOD);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_URI, at, length);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ // if (half->is_cache_line && http_decoder_table_state(half->data->table, HTTP_ITEM_URI) == STRING_STATE_REFER)
+ // {
+ // http_decoder_table_cache(half->data->table, HTTP_ITEM_URI);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_URI);
+ }
+
+ return 0;
+}
+
+/* Possible return values 0, -1, HPE_USER */
+static int on_version(llhttp_t *http, const char *at, size_t length)
+{
+ printf_debug_info("on_version", at, length);
+
+ struct http_decoder_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_VERSION, at, length);
+ }
+
+ return 0;
+}
+
+/* Information-only callbacks, return value is ignored */
+static int on_version_complete(llhttp_t *http)
+{
+ printf_debug_info("on_version_complete", NULL, 0);
+
+ struct http_decoder_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data) {
+ // if (half->is_cache_line && http_decoder_table_state(half->data->table, HTTP_ITERM_VERSION) == STRING_STATE_REFER)
+ // {
+ // http_decoder_table_cache(half->data->table, HTTP_ITERM_VERSION);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_VERSION);
+
+ half->data->major_version = llhttp_get_http_major(&half->parser);
+ half->data->minor_version = llhttp_get_http_minor(&half->parser);
+ }
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = (enum http_event)HTTP_EVENT_REQ_LINE;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_STATUS, at, length);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ // if (half->is_cache_line && http_decoder_table_state(half->data->table, HTTP_ITERM_STATUS) == STRING_STATE_REFER)
+ // {
+ // http_decoder_table_cache(half->data->table, HTTP_ITERM_STATUS);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_STATUS);
+ half->data->status_code = llhttp_get_status_code(&half->parser);
+ }
+
+ half->event = (enum http_event)HTTP_EVENT_RES_LINE;
+ if (half->event_cb != NULL) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_HDRKEY, at, length);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ // if (half->is_cache_header && http_decoder_table_state(half->data->table, HTTP_ITERM_HDRKEY) == STRING_STATE_REFER)
+ // {
+ // http_decoder_table_cache(half->data->table, HTTP_ITERM_HDRKEY);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_HDRKEY);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_HDRVAL, at, length);
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->data != NULL) {
+ // if (half->is_cache_header &&
+ // http_decoder_table_state(half->data->table, HTTP_ITERM_HDRVAL) ==
+ // STRING_STATE_REFER) {
+ // http_decoder_table_cache(half->data->table, HTTP_ITERM_HDRVAL);
+ // }
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_HDRVAL);
+ }
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_HDR;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ if (half->parser.type == HTTP_RESPONSE) {
+ half->event = HTTP_EVENT_RES_HDR;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ if (half->data != NULL) {
+ if (half->data->content_encoding == HTTP_CONTENT_ENCODING_NONE) {
+ struct http_header http_hdr = {0};
+ struct hstring key = {.str = (char *)"Content-Encoding", .str_len = 16};
+ if (http_decoder_table_get_header(half->data->table, &key, &http_hdr, 1) == 1) {
+ char *str = safe_dup(http_hdr.val.str, http_hdr.val.str_len);
+ half->data->content_encoding = http_content_encoding_str2int(str);
+ FREE(str);
+ }
+ }
+ }
+
+ 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);
+
+ 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);
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_HDR_END;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ if (half->parser.type == HTTP_RESPONSE) {
+ half->event = HTTP_EVENT_RES_HDR_END;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+
+ 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_half *half = container_of(http, struct http_decoder_half, parser);
+ assert(half);
+
+ // trigger body_begin event
+ if (half->parser.type == HTTP_REQUEST) {
+ if (half->event == HTTP_EVENT_REQ_HDR_END) {
+ half->event = HTTP_EVENT_REQ_BODY_BEGIN;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+ } else {
+ if (half->event == HTTP_EVENT_RES_HDR_END) {
+ half->event = HTTP_EVENT_RES_BODY_BEGIN;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+ }
+
+ // if enable cache_body, trigger body_data event on_message_complete
+ // if disable cache_body, trigger body_data event on_body
+ // if (half->is_cache_body) {
+ // if (half->data) {
+ // http_decoder_table_refer(half->data->table, HTTP_ITERM_BODY, at,
+ // length);
+ // http_decoder_table_cache(half->data->table, HTTP_ITERM_BODY);
+ // }
+ // } else {
+ if (half->data != NULL) {
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_BODY) == STRING_STATE_COMMIT) {
+ http_decoder_table_reset(half->data->table, HTTP_ITEM_BODY);
+ }
+
+ http_decoder_table_refer(half->data->table, HTTP_ITEM_BODY, at, length);
+ http_decoder_table_commit(half->data->table, HTTP_ITEM_BODY);
+
+ http_decoder_half_data_decompress(half->data);
+ }
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_BODY_DATA;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ } else {
+ half->event = HTTP_EVENT_RES_BODY_DATA;
+ if (half->event_cb) {
+ half->event_cb(half->event, &half->data, half->cb_args);
+ }
+ }
+ // }
+
+ return 0;
+}
+
+void http_decoder_half_init(struct http_decoder_half *half, http_event_cb *event_cb,
+ void *cb_args, int is_cache_line, int is_cache_header,
+ int is_cache_body)
+{
+ if (NULL == half) {
+ return;
+ }
+
+ enum llhttp_type type = HTTP_BOTH; // HTTP_BOTH | HTTP_REQUEST | HTTP_RESPONSE
+ llhttp_settings_init(&half->settings);
+ llhttp_init(&half->parser, type, &half->settings);
+
+ half->settings.on_message_begin = on_message_begin;
+ half->settings.on_message_complete = on_message_complete;
+ half->settings.on_reset = on_reset;
+
+ half->settings.on_url = on_uri;
+ half->settings.on_url_complete = on_uri_complete;
+
+ half->settings.on_status = on_status;
+ half->settings.on_status_complete = on_status_complete;
+
+ half->settings.on_method = on_method;
+ half->settings.on_method_complete = on_method_complete;
+
+ half->settings.on_version = on_version;
+ half->settings.on_version_complete = on_version_complete;
+
+ half->settings.on_header_field = on_header_field;
+ half->settings.on_header_field_complete = on_header_field_complete;
+
+ half->settings.on_header_value = on_header_value;
+ half->settings.on_header_value_complete = on_header_value_complete;
+
+ half->settings.on_chunk_header = on_chunk_header;
+ half->settings.on_chunk_complete = on_chunk_header_complete;
+
+ half->settings.on_headers_complete = on_headers_complete;
+ half->settings.on_body = on_body;
+
+ half->error = HPE_OK;
+
+ half->event_cb = event_cb;
+ half->cb_args = cb_args; //result queue pointer
+ half->is_cache_line = is_cache_line;
+ half->is_cache_header = is_cache_header;
+ half->is_cache_body = is_cache_body;
+
+ half->data = NULL;
+}
+
+int http_decoder_half_parse(struct http_decoder_half *half, const char *data,
+ size_t data_len)
+{
+ if (NULL == half || NULL == data || 0 == data_len) {
+ return -1;
+ }
+
+ half->error = llhttp_execute(&half->parser, data, data_len);
+ if (half->error != HPE_OK) {
+ printf("llhttp_execute parse error: %s %s\n",
+ llhttp_errno_name(half->error), half->parser.reason);
+ return -1;
+ }
+
+ if (half->data != NULL) {
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_URI) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_URI);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_STATUS) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_STATUS);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_METHOD) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_METHOD);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_VERSION) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_VERSION);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_HDRKEY) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_HDRKEY);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_HDRVAL) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_HDRVAL);
+ }
+
+ if (http_decoder_table_state(half->data->table, HTTP_ITEM_BODY) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->data->table, HTTP_ITEM_BODY);
+ }
+ }
+
+ return 0;
+}
+
+struct http_decoder_half_data *http_decoder_half_data_new()
+{
+ struct http_decoder_half_data *data = CALLOC(struct http_decoder_half_data, 1);
+ assert(data);
+
+ data->table = http_decoder_table_new();
+ assert(data->table);
+
+ data->major_version = -1;
+ data->minor_version = -1;
+ data->status_code = -1;
+
+ data->content_encoding = HTTP_CONTENT_ENCODING_NONE;
+ data->decompress_body = NULL;
+ data->decompress_body_len = 0;
+
+ return data;
+}
+
+void http_decoder_half_data_free(struct http_decoder_half_data *data)
+{
+ if (NULL == data) {
+ return;
+ }
+
+ if (data->table != NULL) {
+ http_decoder_table_free(data->table);
+ data->table = NULL;
+ }
+
+ if (data->decompress_body != NULL) {
+ FREE(data->decompress_body);
+ }
+
+ FREE(data);
+}
+
+int http_decoder_half_data_get_request_line(struct http_decoder_half_data *data,
+ struct http_request_line *line)
+{
+ if (NULL == data || NULL == line) {
+ return -1;
+ }
+
+ http_decoder_table_get_method(data->table, &line->method);
+ http_decoder_table_get_uri(data->table, &line->uri);
+ http_decoder_table_get_version(data->table, &line->version);
+
+ line->major_version = data->major_version;
+ line->minor_version = data->minor_version;
+
+ return 0;
+}
+
+int http_decoder_half_data_get_response_line(struct http_decoder_half_data *data,
+ struct http_response_line *line)
+{
+ if (NULL == data || NULL == line) {
+ return -1;
+ }
+
+ http_decoder_table_get_version(data->table, &line->version);
+ http_decoder_table_get_status(data->table, &line->status);
+
+ line->major_version = data->major_version;
+ line->minor_version = data->minor_version;
+ line->status_code = data->status_code;
+
+ return 0;
+}
+
+int http_decoder_half_data_get_header(struct http_decoder_half_data *data,
+ struct hstring *key,
+ struct http_header *header_array,
+ size_t array_size)
+{
+ http_decoder_table_get_header(data->table, key, header_array, array_size);
+}
+
+int http_decoder_half_data_iter_header(struct http_decoder_half_data *data,
+ struct http_header *header)
+{
+ if (NULL == data || NULL == data->table) {
+ return -1;
+ }
+
+ http_decoder_table_iter_header(data->table, header);
+} \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_half.h b/src/http_decoder/http_decoder_half.h
new file mode 100644
index 0000000..5eede43
--- /dev/null
+++ b/src/http_decoder/http_decoder_half.h
@@ -0,0 +1,82 @@
+/*
+**********************************************************************************************
+* File: http_decoder_half.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_HALF_H_
+#define _HTTP_DECODER_HALF_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+#include "http_decoder.h"
+#include "http_content_decompress.h"
+
+// only one http event is fired at a time
+enum http_event {
+ HTTP_EVENT_INIT = 0,
+
+ HTTP_EVENT_REQ_LINE = 1 << 1,
+ HTTP_EVENT_REQ_HDR = 1 << 2,
+ HTTP_EVENT_REQ_HDR_END = 1 << 3,
+ HTTP_EVENT_REQ_BODY_BEGIN = 1 << 4,
+ HTTP_EVENT_REQ_BODY_DATA = 1 << 5,
+ HTTP_EVENT_REQ_BODY_END = 1 << 6,
+ HTTP_EVENT_REQ_END = 1 << 7,
+
+ HTTP_EVENT_RES_LINE = 1 << 8,
+ HTTP_EVENT_RES_HDR = 1 << 9,
+ HTTP_EVENT_RES_HDR_END = 1 << 10,
+ HTTP_EVENT_RES_BODY_BEGIN = 1 << 11,
+ HTTP_EVENT_RES_BODY_DATA = 1 << 12,
+ HTTP_EVENT_RES_BODY_END = 1 << 13,
+ HTTP_EVENT_RES_END = 1 << 14,
+};
+
+struct http_decoder_half;
+struct http_decoder_half_data;
+
+typedef void http_event_cb(enum http_event event, struct http_decoder_half_data **data, void *cb_args);
+
+struct http_decoder_half *http_decoder_half_new();
+
+void http_decoder_half_free(struct http_decoder_half *half);
+
+void http_decoder_half_init(struct http_decoder_half *half, http_event_cb *event_cb,
+ void *cb_args, int is_cache_line, int is_cache_header,
+ int is_cache_body);
+
+int http_decoder_half_parse(struct http_decoder_half *half, const char *data, size_t len);
+
+//http decoder half data API
+struct http_decoder_half_data *http_decoder_half_data_new();
+
+void http_decoder_half_data_free(struct http_decoder_half_data *data);
+
+int http_decoder_half_data_get_request_line(struct http_decoder_half_data *data,
+ struct http_request_line *line);
+
+int http_decoder_half_data_get_response_line(struct http_decoder_half_data *data,
+ struct http_response_line *line);
+
+int http_decoder_half_data_get_header(struct http_decoder_half_data *data,
+ struct hstring *key,
+ struct http_header *header_array,
+ size_t array_size);
+
+int http_decoder_half_data_iter_header(struct http_decoder_half_data *data,
+ struct http_header *header);
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_string.c b/src/http_decoder/http_decoder_string.c
new file mode 100644
index 0000000..15ff290
--- /dev/null
+++ b/src/http_decoder/http_decoder_string.c
@@ -0,0 +1,176 @@
+/*
+**********************************************************************************************
+* File: http_decoder_string.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <stdlib.h>
+
+#include "http_decoder_string.h"
+
+static const char *string_state_to_desc(enum string_state state)
+{
+ switch (state) {
+ case STRING_STATE_INIT:
+ return "init";
+ break;
+ case STRING_STATE_REFER:
+ return "refer";
+ break;
+ case STRING_STATE_CACHE:
+ return "cache";
+ break;
+ case STRING_STATE_COMMIT:
+ return "commit";
+ break;
+ default:
+ return "unknown";
+ break;
+ }
+}
+
+void http_decoder_string_refer(struct http_decoder_string *rstr,
+ const char *at, size_t length)
+{
+ if (NULL == rstr) {
+ return;
+ }
+
+ switch (rstr->state) {
+ case STRING_STATE_INIT:
+ case STRING_STATE_CACHE:
+ rstr->refer.str = (char *)at;
+ rstr->refer.str_len = length;
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->state = STRING_STATE_REFER;
+}
+
+void http_decoder_string_cache(struct http_decoder_string *rstr)
+{
+ if (NULL == rstr) {
+ return;
+ }
+
+ switch (rstr->state) {
+ case STRING_STATE_REFER:
+ if (rstr->refer.str_len > 0) {
+ rstr->cache.str = (char *)realloc(rstr->cache.str, rstr->cache.str_len + rstr->refer.str_len);
+ memcpy(rstr->cache.str + rstr->cache.str_len, rstr->refer.str, rstr->refer.str_len);
+ rstr->cache.str_len += rstr->refer.str_len;
+
+ rstr->refer.str = NULL;
+ rstr->refer.str_len = 0;
+ }
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->state = STRING_STATE_CACHE;
+}
+
+void http_decoder_string_commit(struct http_decoder_string *rstr)
+{
+ if (NULL == rstr) {
+ return;
+ }
+
+ switch (rstr->state) {
+ case STRING_STATE_REFER:
+ if (rstr->cache.str_len) {
+ http_decoder_string_cache(rstr);
+
+ rstr->commit.str = rstr->cache.str;
+ rstr->commit.str_len = rstr->cache.str_len;
+ // not overwrite rstr->cache.str
+ } else {
+ rstr->commit.str = rstr->refer.str;
+ rstr->commit.str_len = rstr->refer.str_len;
+
+ rstr->refer.str = NULL;
+ rstr->refer.str_len = 0;
+ }
+ break;
+ case STRING_STATE_CACHE:
+ rstr->commit.str = rstr->cache.str;
+ rstr->commit.str_len = rstr->cache.str_len;
+ // not overwrite rstr->cache.str
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->state = STRING_STATE_COMMIT;
+}
+
+void http_decoder_string_reset(struct http_decoder_string *rstr)
+{
+ assert(rstr);
+
+ switch (rstr->state) {
+ case STRING_STATE_INIT:
+ case STRING_STATE_REFER:
+ case STRING_STATE_CACHE:
+ case STRING_STATE_COMMIT:
+ safe_free(rstr->cache.str);
+ memset(rstr, 0, sizeof(struct http_decoder_string));
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->state = STRING_STATE_INIT;
+}
+
+enum string_state http_decoder_string_state(struct http_decoder_string *rstr)
+{
+ return rstr->state;
+}
+
+void http_decoder_string_get(struct http_decoder_string *rstr, struct hstring *out)
+{
+ if (NULL == rstr || NULL == out) {
+ return;
+ }
+
+ if (http_decoder_string_state(rstr) == STRING_STATE_COMMIT) {
+ out->str = rstr->commit.str;
+ out->str_len = rstr->commit.str_len;
+ } else {
+ out->str = NULL;
+ out->str_len = 0;
+ }
+}
+
+void http_decoder_string_dump(struct http_decoder_string *rstr, const char *desc)
+{
+ if (NULL == rstr) {
+ return;
+ }
+
+ char *refer_str = safe_dup(rstr->refer.str, rstr->refer.str_len);
+ char *cache_str = safe_dup(rstr->cache.str, rstr->cache.str_len);
+ char *commit_str = safe_dup(rstr->commit.str, rstr->commit.str_len);
+
+ printf("%s: state: %s, refer: {len: %02zu, str: %s}, cache: {len: %02zu, str: %s}, commit: {len: %02zu, str: %s}\n",
+ desc, string_state_to_desc(rstr->state),
+ rstr->refer.str_len, refer_str,
+ rstr->cache.str_len, cache_str,
+ rstr->commit.str_len, commit_str);
+
+ FREE(refer_str);
+ FREE(cache_str);
+ FREE(commit_str);
+} \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_string.h b/src/http_decoder/http_decoder_string.h
new file mode 100644
index 0000000..82a812b
--- /dev/null
+++ b/src/http_decoder/http_decoder_string.h
@@ -0,0 +1,87 @@
+/*
+**********************************************************************************************
+* File: http_decoder_string.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_STRING_H_
+#define _HTTP_DECODER_STRING_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "http_decoder.h"
+
+enum string_state {
+ STRING_STATE_INIT,
+ STRING_STATE_REFER,
+ STRING_STATE_CACHE,
+ STRING_STATE_COMMIT,
+};
+
+/* state transition diagram
+ * +----------+
+ * | |
+ * \|/ |
+ * +------+ |
+ * | init | |
+ * +------+ |
+ * | |
+ * +---->| |
+ * | \|/ |
+ * | +-------+ |
+ * | | refer |--+ |
+ * | +-------+ | |
+ * | | | |
+ * | \|/ | |
+ * | +-------+ | |
+ * +--| cache | | |
+ * +-------+ | |
+ * | | |
+ * |<------+ |
+ * \|/ |
+ * +--------+ |
+ * | commit | |
+ * +--------+ |
+ * | |
+ * \|/ |
+ * +--------+ |
+ * | reset |----+
+ * +--------+
+ */
+
+
+//http decoder string
+struct http_decoder_string {
+ struct hstring refer; // shallow copy
+ struct hstring cache; // deep copy
+ struct hstring commit;
+
+ enum string_state state;
+};
+
+void http_decoder_string_refer(struct http_decoder_string *rstr, const char *at, size_t length);
+
+void http_decoder_string_cache(struct http_decoder_string *rstr);
+
+void http_decoder_string_commit(struct http_decoder_string *rstr);
+
+void http_decoder_string_reset(struct http_decoder_string *rstr);
+
+enum string_state http_decoder_string_state(struct http_decoder_string *rstr);
+
+void http_decoder_string_get(struct http_decoder_string *rstr, struct hstring *out);
+
+void http_decoder_string_dump(struct http_decoder_string *rstr, const char *desc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_table.c b/src/http_decoder/http_decoder_table.c
new file mode 100644
index 0000000..292c80c
--- /dev/null
+++ b/src/http_decoder/http_decoder_table.c
@@ -0,0 +1,464 @@
+/*
+**********************************************************************************************
+* File: http_decoder_table.c
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "http_decoder_table.h"
+#include "http_decoder.h"
+#include "http_decoder_string.h"
+#include "stellar/utils.h"
+
+#define MAX_HEADER_SIZE 16
+
+struct http_decoder_header {
+ struct http_decoder_string key;
+ struct http_decoder_string val;
+};
+
+struct http_decoder_table {
+ struct http_decoder_string uri;
+ struct http_decoder_string status;
+ struct http_decoder_string method;
+ struct http_decoder_string version;
+ struct http_decoder_string body;
+
+ size_t header_size;
+ size_t header_index;
+ size_t header_iter;
+ struct http_decoder_header *headers;
+};
+
+struct http_decoder_table *http_decoder_table_new()
+{
+ struct http_decoder_table *table = CALLOC(struct http_decoder_table, 1);
+ assert(table);
+
+ table->header_index = 0;
+ table->header_size = MAX_HEADER_SIZE;
+ table->headers = CALLOC(struct http_decoder_header, table->header_size);
+
+ return table;
+}
+
+void http_decoder_table_free(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ if (table->uri.cache.str != NULL) {
+ FREE(table->uri.cache.str);
+ }
+
+ if (table->status.cache.str != NULL) {
+ FREE(table->status.cache.str);
+ }
+
+ if (table->method.cache.str != NULL) {
+ FREE(table->method.cache.str);
+ }
+
+ if (table->version.cache.str != NULL) {
+ FREE(table->version.cache.str);
+ }
+
+ if (table->body.cache.str != NULL) {
+ FREE(table->body.cache.str);
+ }
+
+ if (table->headers != NULL) {
+ FREE(table->headers);
+ }
+
+ FREE(table);
+}
+
+enum string_state http_decoder_table_state(struct http_decoder_table *table,
+ enum http_item type)
+{
+ if (NULL == table) {
+ return STRING_STATE_INIT;
+ }
+ struct http_decoder_header *header = NULL;
+ enum string_state state = STRING_STATE_INIT;
+ assert(table);
+
+ switch (type) {
+ case HTTP_ITEM_URI:
+ state = http_decoder_string_state(&table->uri);
+ break;
+ case HTTP_ITEM_STATUS:
+ state = http_decoder_string_state(&table->status);
+ break;
+ case HTTP_ITEM_METHOD:
+ state = http_decoder_string_state(&table->method);
+ break;
+ case HTTP_ITEM_VERSION:
+ state = http_decoder_string_state(&table->version);
+ break;
+ case HTTP_ITEM_HDRKEY:
+ header = &table->headers[table->header_index];
+ state = http_decoder_string_state(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ header = &table->headers[table->header_index];
+ state = http_decoder_string_state(&header->val);
+ break;
+ case HTTP_ITEM_BODY:
+ state = http_decoder_string_state(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ return state;
+}
+
+void http_decoder_table_refer(struct http_decoder_table *table, enum http_item type,
+ const char *at, size_t len)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type) {
+ case HTTP_ITEM_URI:
+ http_decoder_string_refer(&table->uri, at, len);
+ break;
+ case HTTP_ITEM_STATUS:
+ http_decoder_string_refer(&table->status, at, len);
+ break;
+ case HTTP_ITEM_METHOD:
+ http_decoder_string_refer(&table->method, at, len);
+ break;
+ case HTTP_ITEM_VERSION:
+ http_decoder_string_refer(&table->version, at, len);
+ break;
+ case HTTP_ITEM_HDRKEY:
+ if (table->header_index >= table->header_size) {
+ table->headers = (struct http_decoder_header *)realloc(table->headers,
+ table->header_size * 2);
+ assert(table->headers);
+
+ table->header_size *= 2;
+ for (size_t 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_string_refer(&header->key, at, len);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ header = &table->headers[table->header_index];
+ http_decoder_string_refer(&header->val, at, len);
+ break;
+ case HTTP_ITEM_BODY:
+ http_decoder_string_refer(&table->body, at, len);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_cache(struct http_decoder_table *table, enum http_item type)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type) {
+ case HTTP_ITEM_URI:
+ http_decoder_string_cache(&table->uri);
+ break;
+ case HTTP_ITEM_STATUS:
+ http_decoder_string_cache(&table->status);
+ break;
+ case HTTP_ITEM_METHOD:
+ http_decoder_string_cache(&table->method);
+ break;
+ case HTTP_ITEM_VERSION:
+ http_decoder_string_cache(&table->version);
+ break;
+ case HTTP_ITEM_HDRKEY:
+ header = &table->headers[table->header_index];
+ http_decoder_string_cache(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ header = &table->headers[table->header_index];
+ http_decoder_string_cache(&header->val);
+ break;
+ case HTTP_ITEM_BODY:
+ http_decoder_string_cache(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_commit(struct http_decoder_table *table, enum http_item type)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type) {
+ case HTTP_ITEM_URI:
+ http_decoder_string_commit(&table->uri);
+ break;
+ case HTTP_ITEM_STATUS:
+ http_decoder_string_commit(&table->status);
+ break;
+ case HTTP_ITEM_METHOD:
+ http_decoder_string_commit(&table->method);
+ break;
+ case HTTP_ITEM_VERSION:
+ http_decoder_string_commit(&table->version);
+ break;
+ case HTTP_ITEM_HDRKEY:
+ header = &table->headers[table->header_index];
+ http_decoder_string_commit(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ header = &table->headers[table->header_index];
+ http_decoder_string_commit(&header->val);
+ // inc index
+ table->header_index++;
+ break;
+ case HTTP_ITEM_BODY:
+ http_decoder_string_commit(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_reset(struct http_decoder_table *table, enum http_item type)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ switch (type) {
+ case HTTP_ITEM_URI:
+ http_decoder_string_reset(&table->uri);
+ break;
+ case HTTP_ITEM_STATUS:
+ http_decoder_string_reset(&table->status);
+ break;
+ case HTTP_ITEM_METHOD:
+ http_decoder_string_reset(&table->method);
+ break;
+ case HTTP_ITEM_VERSION:
+ http_decoder_string_reset(&table->version);
+ break;
+ case HTTP_ITEM_HDRKEY:
+ header = &table->headers[table->header_index];
+ http_decoder_string_reset(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ header = &table->headers[table->header_index];
+ http_decoder_string_reset(&header->val);
+ break;
+ case HTTP_ITEM_BODY:
+ http_decoder_string_reset(&table->body);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void http_decoder_table_remove(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ if (http_decoder_string_state(&table->uri) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&table->uri);
+ }
+
+ if (http_decoder_string_state(&table->status) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&table->status);
+ }
+
+ if (http_decoder_string_state(&table->method) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&table->method);
+ }
+
+ if (http_decoder_string_state(&table->version) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&table->version);
+ }
+
+ if (http_decoder_string_state(&table->body) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&table->body);
+ }
+
+ for (size_t i = 0; i <= table->header_index; i++) {
+ struct http_decoder_header *header = &table->headers[i];
+ if (http_decoder_string_state(&header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&header->val) == STRING_STATE_COMMIT) {
+ http_decoder_string_reset(&header->key);
+ http_decoder_string_reset(&header->val);
+ }
+ }
+
+ if (table->header_index != 0) {
+ struct http_decoder_header *last_header =
+ &table->headers[table->header_index];
+ if (http_decoder_string_state(&last_header->key) == STRING_STATE_CACHE ||
+ http_decoder_string_state(&last_header->val) == STRING_STATE_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)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ http_decoder_string_dump(&table->uri, "uri");
+ http_decoder_string_dump(&table->status, "status");
+ http_decoder_string_dump(&table->method, "method");
+ http_decoder_string_dump(&table->version, "version");
+ http_decoder_string_dump(&table->body, "body");
+
+ for (size_t i = 0; i <= table->header_index; i++) {
+ struct http_decoder_header *header = &table->headers[i];
+ http_decoder_string_dump(&header->key, "key");
+ http_decoder_string_dump(&header->val, "val");
+ }
+}
+
+void http_decoder_table_get_uri(struct http_decoder_table *table,
+ struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return;
+ }
+
+ http_decoder_string_get(&table->uri, out);
+}
+
+void http_decoder_table_get_method(struct http_decoder_table *table,
+ struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return;
+ }
+
+ http_decoder_string_get(&table->method, out);
+}
+
+void http_decoder_table_get_status(struct http_decoder_table *table,
+ struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return;
+ }
+
+ http_decoder_string_get(&table->status, out);
+}
+
+void http_decoder_table_get_version(struct http_decoder_table *table,
+ struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return;
+ }
+
+ http_decoder_string_get(&table->version, out);
+}
+
+void http_decoder_table_get_body(struct http_decoder_table *table,
+ struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return;
+ }
+
+ http_decoder_string_get(&table->body, out);
+}
+
+size_t http_decoder_table_get_header(struct http_decoder_table *table, struct hstring *key,
+ struct http_header *header_array, size_t array_size)
+{
+ if (NULL == table || NULL == key || NULL == header_array) {
+ return 0;
+ }
+
+ size_t header_cnt = 0;
+ for (size_t i = 0; i < table->header_index && header_cnt < array_size; i++) {
+ struct http_decoder_header *header = &table->headers[i];
+ if (http_decoder_string_state(&header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&header->val) == STRING_STATE_COMMIT) {
+ struct hstring tmp_key;
+ http_decoder_string_get(&header->key, &tmp_key);
+
+ if (tmp_key.str_len == key->str_len && strncasecmp(tmp_key.str, key->str, key->str_len) == 0) {
+ http_decoder_string_get(&header->val, &header_array[header_cnt++].val);
+ }
+ }
+ }
+
+ return header_cnt;
+}
+
+int http_decoder_table_iter_header(struct http_decoder_table *table,
+ struct http_header *header)
+{
+ if (NULL == table) {
+ return -1;
+ }
+
+ if (table->header_iter >= table->header_index) {
+ table->header_iter = 0;
+ }
+
+ struct http_decoder_header *tmp_decoder_header = &table->headers[table->header_iter++];
+ if (http_decoder_string_state(&tmp_decoder_header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&tmp_decoder_header->val) == STRING_STATE_COMMIT) {
+ http_decoder_string_get(&tmp_decoder_header->key, &header->key);
+ http_decoder_string_get(&tmp_decoder_header->val, &header->val);
+ return 1;
+ }
+
+ header->key.str = NULL;
+ header->key.str_len = 0;
+
+ header->val.str = NULL;
+ header->val.str_len = 0;
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_table.h b/src/http_decoder/http_decoder_table.h
new file mode 100644
index 0000000..53ff61d
--- /dev/null
+++ b/src/http_decoder/http_decoder_table.h
@@ -0,0 +1,82 @@
+/*
+**********************************************************************************************
+* File: http_decoder_table.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_TABLE_H_
+#define _HTTP_DECODER_TABLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+#include "http_decoder.h"
+#include "http_decoder_string.h"
+
+enum http_item {
+ HTTP_ITEM_URI = 0x01,
+ HTTP_ITEM_STATUS = 0x02,
+ HTTP_ITEM_METHOD = 0x03,
+ HTTP_ITEM_VERSION = 0x04,
+ HTTP_ITEM_HDRKEY = 0x05,
+ HTTP_ITEM_HDRVAL = 0x06,
+ HTTP_ITEM_BODY = 0x07,
+};
+
+struct http_decoder_table;
+
+struct http_decoder_table *http_decoder_table_new();
+
+void http_decoder_table_free(struct http_decoder_table *table);
+
+enum string_state http_decoder_table_state(struct http_decoder_table *table,
+ enum http_item type);
+
+void http_decoder_table_refer(struct http_decoder_table *table,
+ enum http_item type, const char *at, size_t len);
+
+void http_decoder_table_cache(struct http_decoder_table *table,
+ enum http_item type);
+
+void http_decoder_table_commit(struct http_decoder_table *table,
+ enum http_item type);
+
+void http_decoder_table_reset(struct http_decoder_table *table,
+ enum http_item type);
+
+void http_decoder_table_remove(struct http_decoder_table *table);
+
+void http_decoder_table_dump(struct http_decoder_table *table);
+
+void http_decoder_table_get_uri(struct http_decoder_table *table,
+ struct hstring *out);
+
+void http_decoder_table_get_method(struct http_decoder_table *table,
+ struct hstring *out);
+
+void http_decoder_table_get_status(struct http_decoder_table *table,
+ struct hstring *out);
+
+void http_decoder_table_get_version(struct http_decoder_table *table,
+ struct hstring *out);
+
+void http_decoder_table_get_body(struct http_decoder_table *table,
+ struct hstring *out);
+
+size_t http_decoder_table_get_header(struct http_decoder_table *table, struct hstring *key,
+ struct http_header *header_array, size_t array_size);
+
+int http_decoder_table_iter_header(struct http_decoder_table *table,
+ struct http_header *header);
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_utils.c b/src/http_decoder/http_decoder_utils.c
new file mode 100644
index 0000000..a5dfbe1
--- /dev/null
+++ b/src/http_decoder/http_decoder_utils.c
@@ -0,0 +1,25 @@
+/*
+**********************************************************************************************
+* File: http_decoder_utils.c
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <string.h>
+
+#include "stellar/utils.h"
+
+char *safe_dup(const char *str, size_t len)
+{
+ if (str == NULL || len == 0) {
+ return NULL;
+ }
+
+ char *dup = CALLOC(char, len + 1);
+ memcpy(dup, str, len);
+
+ return dup;
+} \ No newline at end of file
diff --git a/src/http_decoder/http_decoder_utils.h b/src/http_decoder/http_decoder_utils.h
new file mode 100644
index 0000000..0d981c5
--- /dev/null
+++ b/src/http_decoder/http_decoder_utils.h
@@ -0,0 +1,65 @@
+/*
+**********************************************************************************************
+* File: http_decoder_utils.h
+* Description:
+* Authors: LuWenPeng <[email protected]>
+* Date: 2022-10-31
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_UTILS_H_
+#define _HTTP_DECODER_UTILS_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+char *safe_dup(const char *str, size_t len);
+
+/******************************************************************************
+ * Logger
+ ******************************************************************************/
+
+enum http_decoder_log_level
+{
+ DEBUG = 0x11,
+ WARN = 0x12,
+ INFO = 0x13,
+ ERROR = 0x14,
+};
+
+#ifndef http_decoder_log
+#define http_decoder_log(level, format, ...) \
+ { \
+ switch (level) \
+ { \
+ case DEBUG: \
+ fprintf(stdout, "HTTP_DECODER [DEBUG] " format "\n", ##__VA_ARGS__); \
+ fflush(stdout); \
+ break; \
+ case WARN: \
+ fprintf(stdout, "HTTP_DECODER [WARN] " format "\n", ##__VA_ARGS__); \
+ fflush(stdout); \
+ break; \
+ case INFO: \
+ fprintf(stdout, "HTTP_DECODER [INFO] " format "\n", ##__VA_ARGS__); \
+ fflush(stdout); \
+ break; \
+ case ERROR: \
+ fprintf(stderr, "HTTP_DECODER [ERROR] " format "\n", ##__VA_ARGS__); \
+ fflush(stderr); \
+ break; \
+ } \
+ }
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder/version.map b/src/http_decoder/version.map
new file mode 100644
index 0000000..a0ba8a4
--- /dev/null
+++ b/src/http_decoder/version.map
@@ -0,0 +1,7 @@
+VERS_3.0{
+global:
+ extern "C" {
+ http_message_*;
+ };
+local: *;
+}; \ No newline at end of file
diff --git a/src/stellar_on_sapp/start_loader.inf b/src/stellar_on_sapp/start_loader.inf
index f966457..89b2f94 100644
--- a/src/stellar_on_sapp/start_loader.inf
+++ b/src/stellar_on_sapp/start_loader.inf
@@ -4,13 +4,13 @@ SO_PATH=./plug/stellar_on_sapp/stellar_on_sapp.so
INIT_FUNC=STELLAR_START_LOADER_INIT
DESTROY_FUNC=STELLAR_START_LOADER_EXIT
-[TCP_ALL]
-FUNC_FLAG=ALL
-FUNC_NAME=stellar_on_sapp_tcpall_entry
-
-#[TCP]
+#[TCP_ALL]
#FUNC_FLAG=ALL
-#FUNC_NAME=stellar_on_sapp_tcp_entry
+#FUNC_NAME=stellar_on_sapp_tcpall_entry
+
+[TCP]
+FUNC_FLAG=ALL
+FUNC_NAME=stellar_on_sapp_tcp_entry
[UDP]
FUNC_FLAG=ALL
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 56d7b60..b7beeaa 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -10,5 +10,7 @@ target_link_libraries(
gtest
)
+add_subdirectory(http_decoder)
+
include(GoogleTest)
gtest_discover_tests(gtest_stellar) \ No newline at end of file
diff --git a/test/http_decoder/CMakeLists.txt b/test/http_decoder/CMakeLists.txt
new file mode 100644
index 0000000..4ef959c
--- /dev/null
+++ b/test/http_decoder/CMakeLists.txt
@@ -0,0 +1,40 @@
+set(DECODER_NAME http_decoder)
+
+add_library(${DECODER_NAME}_test_plug SHARED http_decoder_gtest.cpp)
+add_dependencies(${DECODER_NAME}_test_plug ${DECODER_NAME})
+target_link_libraries(${DECODER_NAME}_test_plug MESA_prof_load cjson)
+set_target_properties(${DECODER_NAME}_test_plug PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_CURRENT_BINARY_DIR}/sapp)
+set(TEST_MAIN ${TEST_RUN_DIR}/plugin_test_main)
+
+# assemble test env
+add_test(NAME INSTALL_TEST_MAIN COMMAND sh -c "rpm -i ${CMAKE_CURRENT_SOURCE_DIR}/test_env/sapp4.el8.x86_64.rpm --prefix=${CMAKE_CURRENT_BINARY_DIR}/sapp --force --nodeps")
+
+add_test(NAME COPY_TEST_MAIN COMMAND sh -c "cp ${TEST_RUN_DIR}/tools/plugin_test_main ${TEST_RUN_DIR}/plugin_test_main")
+add_test(NAME COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/tsgconf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/tsg_l7_protocol.conf ${TEST_RUN_DIR}/tsgconf/tsg_l7_protocol.conf")
+add_test(NAME COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/stellar_plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/spec.toml ${TEST_RUN_DIR}/stellar_plugin/spec.toml")
+add_test(NAME COPY_CONFLIST COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plug/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/conflist.inf ${TEST_RUN_DIR}/plug/conflist.inf")
+add_test(NAME COPY_INF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plug/stellar_on_sapp && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/start_loader.inf ${TEST_RUN_DIR}/plug/stellar_on_sapp/start_loader.inf")
+
+# update config files
+add_test(NAME UPDATE_SAPP_LOG COMMAND bash -c "sed -i 's/sapp_log.fatal/sapp_log.info/' ${TEST_RUN_DIR}/etc/sapp_log.conf")
+add_test(NAME UPDATE_SAPP_SYN_MODE COMMAND bash -c "sed -i 's/syn_mandatory=1/syn_mandatory=0/' ${TEST_RUN_DIR}/etc/sapp.toml")
+add_test(NAME UPDATE_SAPP_REORDER COMMAND bash -c "sed -i 's/reorder_pkt_max=32/reorder_pkt_max=5/' ${TEST_RUN_DIR}/etc/sapp.toml")
+
+
+# update plugin to be tested
+add_test(NAME UPDATE_STELLAR_ON_SAPP_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/src/stellar_on_sapp/stellar_on_sapp.so ${TEST_RUN_DIR}/plug/stellar_on_sapp/stellar_on_sapp.so")
+add_test(NAME UPDATE_PLUG_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/src/${DECODER_NAME}/${DECODER_NAME}.so ${TEST_RUN_DIR}/stellar_plugin/${DECODER_NAME}.so")
+add_test(NAME UPDATE_TEST_SO COMMAND sh -c "cp ${CMAKE_CURRENT_BINARY_DIR}/${DECODER_NAME}_test_plug.so ${TEST_RUN_DIR}/stellar_plugin/${DECODER_NAME}_test_plug.so")
+
+set_tests_properties(INSTALL_TEST_MAIN COPY_TEST_MAIN COPY_CONF COPY_SPEC COPY_CONFLIST COPY_INF
+ UPDATE_SAPP_LOG UPDATE_SAPP_SYN_MODE UPDATE_SAPP_REORDER
+ UPDATE_STELLAR_ON_SAPP_SO UPDATE_PLUG_SO UPDATE_TEST_SO
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+# run tests
+# add_test(NAME RUN_HTTP_SIMPLE_TEST COMMAND ${TEST_MAIN} ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/proto_identify_http.json
+# -f "find ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/ -name *.pcap|sort -V" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
+# set_tests_properties(RUN_HTTP_SIMPLE_TEST PROPERTIES FIXTURES_REQUIRED TestFixture) \ No newline at end of file
diff --git a/test/http_decoder/http_decoder_gtest.cpp b/test/http_decoder/http_decoder_gtest.cpp
new file mode 100644
index 0000000..9ebaddd
--- /dev/null
+++ b/test/http_decoder/http_decoder_gtest.cpp
@@ -0,0 +1,185 @@
+/*
+ * author:yangwei
+ * create time:2021-8-21
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "../../src/http_decoder/http_decoder.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+
+
+#include "cJSON.h"
+#include "MESA_prof_load.h"
+
+#include "stellar/utils.h"
+#include "stellar/stellar.h"
+#include "stellar/session_exdata.h"
+#include "stellar/session_mq.h"
+
+int commit_test_result_json(cJSON *node, const char *name);
+}
+#endif
+
+#include "MESA/stream.h"
+
+static int g_result_count = 1;
+int g_test_exdata_idx = 0;
+int g_l7_exdata_idx = 0;
+int g_test_app_plugin_id = 0;
+int g_stellar_session_bridge_id = -1;
+
+#define MAX_PROTO_ID_NUM 10000
+static char *g_proto_id2name[MAX_PROTO_ID_NUM];
+
+struct http_decoder_test_context {
+ int topic_id;
+ int plugin_id;
+};
+
+// static int load_l7_protocol_mapper(const char *filename) {
+// int ret = 0, proto_id = 0;
+// char line[1024] = {0};
+// char type_name[32] = {0};
+// char proto_name[32] = {0};
+
+// memset(g_proto_id2name, 0, sizeof(g_proto_id2name));
+// FILE *fp = fopen(filename, "r");
+// if (NULL == fp) {
+// printf("Open %s failed ...", filename);
+// return -1;
+// }
+
+// memset(line, 0, sizeof(line));
+
+// while ((fgets(line, sizeof(line), fp)) != NULL) {
+// if (line[0] == '#' || line[0] == '\n' ||
+// line[0] == '\r' || line[0] == '\0') {
+// continue;
+// }
+
+// ret = sscanf(line, "%30s %30s %d", type_name, proto_name, &proto_id);
+// assert(ret == 3 && proto_id < MAX_PROTO_ID_NUM);
+
+// g_proto_id2name[proto_id] = (char *)calloc(strlen(proto_name)+1, 1);
+// strcpy(g_proto_id2name[proto_id], proto_name);
+// memset(line, 0, sizeof(line));
+// }
+
+// fclose(fp);
+// fp = NULL;
+
+// return ret;
+// }
+
+static void commit_test_result(cJSON *ctx, struct session *sess)
+{
+ assert(g_l7_exdata_idx >= 0 && ctx != NULL);
+ struct http_request_line *label = (struct http_request_line *)session_get_ex_data(sess, g_l7_exdata_idx);;
+ // if(label != NULL)
+ // {
+ // int proto_ids[8];
+ // const char* proto_names[8];
+ // for(int i = 0; i < label->protocol_id_num; i++)
+ // {
+ // proto_ids[i] = (int)(label->protocol_id[i]);
+ // proto_names[i] = g_proto_id2name[proto_ids[i]];
+
+ // }
+ // cJSON *label_ids = cJSON_CreateIntArray(proto_ids, label->protocol_id_num);
+ // cJSON_AddItemToObject(ctx, "l7_label_id", label_ids);
+ // cJSON *label_names = cJSON_CreateStringArray(proto_names, label->protocol_id_num);
+ // cJSON_AddItemToObject(ctx, "l7_label_name", label_names);
+ // }
+ // else
+ // {
+ // cJSON_AddStringToObject(ctx, "l7_label_id", "UNKNOWN");
+ // }
+ // unsigned char dir_flag;
+ // int is_symmetric=session_is_symmetric(sess, &dir_flag);
+ // if(is_symmetric)
+ // {
+ // cJSON_AddStringToObject(ctx, "STREAM_DIR", "DOUBLE");
+ // }
+ // else if(dir_flag == SESSION_SEEN_C2S_FLOW)
+ // {
+ // cJSON_AddStringToObject(ctx, "STREAM_DIR", "C2S");
+ // }
+ // else if(dir_flag == SESSION_SEEN_S2C_FLOW)
+ // {
+ // cJSON_AddStringToObject(ctx, "STREAM_DIR", "S2C");
+ // }
+ // else
+ // {
+ // assert(0);
+ // }
+ // if (ctx)
+ // {
+ // char result_name[128] = "";
+ // sprintf(result_name, "APP_PROTO_IDENTIFY_RESULT_%d", g_result_count);
+ // commit_test_result_json(ctx, result_name);
+ // g_result_count += 1;
+ // }
+ return;
+}
+
+static int http_decoder_test_entry(struct session *sess, int topic_id, const void *data, void *cb_arg)
+{
+ struct http_message *msg = (struct http_message *)data;
+
+ printf("................http_decoder_test_entry................\n");
+ struct http_request_line line;
+ memset(&line, 0, sizeof(line));
+
+ enum http_message_type msg_type = http_message_type(msg);
+ switch (msg_type) {
+ case HTTP_MESSAGE_REQ_LINE:
+ http_message_get_request_line(msg, &line);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+extern "C" void *http_decoder_test_init(struct stellar *st)
+{
+ char l7_label_name[256] = "";
+ char l7_bridge_name[256] = "";
+ char l7_proto_name[256] = "";
+
+ MESA_load_profile_string_def("./tsgconf/main.conf", "SYSTEM", "L7_LABEL_NAME", l7_label_name, sizeof(l7_label_name), "L7_PROTOCOL_LABEL");
+ MESA_load_profile_string_def("./tsgconf/main.conf", "SYSTEM", "APP_BRIDGE_NAME", l7_bridge_name, sizeof(l7_bridge_name), "APP_BRIDGE");
+ MESA_load_profile_string_def("./tsgconf/main.conf", "SYSTEM", "L7_PROTOCOL_FILE", l7_proto_name, sizeof(l7_proto_name), "./tsgconf/tsg_l7_protocol.conf");
+
+ int topic_id = session_mq_get_topic_id(st, "HTTP_DECODER_MESSAGE");
+ if (topic_id < 0) {
+ printf("http_decoder_test_init: can't get http_decoder topic id !!!\n");
+ exit(-1);
+ }
+
+ session_mq_subscribe_topic(st, topic_id, http_decoder_test_entry, NULL);
+ printf("http_decoder_test_init OK!\n");
+
+ return NULL;
+}
+
+extern "C" void http_decoder_test_exit(void *ctx)
+{
+ if (NULL == ctx) {
+ return;
+ }
+
+ FREE(ctx);
+ printf("http_decoder_test_exit OK!\n");
+} \ No newline at end of file
diff --git a/test/http_decoder/http_pcap/http_simple.pcap b/test/http_decoder/http_pcap/http_simple.pcap
new file mode 100644
index 0000000..a4b6bea
--- /dev/null
+++ b/test/http_decoder/http_pcap/http_simple.pcap
Binary files differ
diff --git a/test/http_decoder/http_pcap/simple_http.pcap b/test/http_decoder/http_pcap/simple_http.pcap
new file mode 100644
index 0000000..a4b6bea
--- /dev/null
+++ b/test/http_decoder/http_pcap/simple_http.pcap
Binary files differ
diff --git a/test/http_decoder/test_env/conflist.inf b/test/http_decoder/test_env/conflist.inf
new file mode 100644
index 0000000..2e8144d
--- /dev/null
+++ b/test/http_decoder/test_env/conflist.inf
@@ -0,0 +1,9 @@
+[platform]
+./plug/stellar_on_sapp/start_loader.inf
+
+
+[protocol]
+
+
+[business]
+#./plug/stellar_on_sapp/defer_loader.inf
diff --git a/test/http_decoder/test_env/sapp4.el8.x86_64.rpm b/test/http_decoder/test_env/sapp4.el8.x86_64.rpm
new file mode 100644
index 0000000..e43fe2f
--- /dev/null
+++ b/test/http_decoder/test_env/sapp4.el8.x86_64.rpm
Binary files differ
diff --git a/test/http_decoder/test_env/spec.toml b/test/http_decoder/test_env/spec.toml
new file mode 100644
index 0000000..0bfa214
--- /dev/null
+++ b/test/http_decoder/test_env/spec.toml
@@ -0,0 +1,11 @@
+# stellar_plugin.toml
+#
+[[plugin]]
+path = "./stellar_plugin/http_decoder.so"
+init = "http_decoder_init"
+exit = "http_decoder_exit"
+
+[[plugin]]
+path = "./stellar_plugin/http_decoder_test_plug.so"
+init = "http_decoder_test_init"
+exit = "http_decoder_test_exit"
diff --git a/test/http_decoder/test_env/start_loader.inf b/test/http_decoder/test_env/start_loader.inf
new file mode 100644
index 0000000..89b2f94
--- /dev/null
+++ b/test/http_decoder/test_env/start_loader.inf
@@ -0,0 +1,17 @@
+[PLUGINFO]
+PLUGNAME=stellar_start_loader
+SO_PATH=./plug/stellar_on_sapp/stellar_on_sapp.so
+INIT_FUNC=STELLAR_START_LOADER_INIT
+DESTROY_FUNC=STELLAR_START_LOADER_EXIT
+
+#[TCP_ALL]
+#FUNC_FLAG=ALL
+#FUNC_NAME=stellar_on_sapp_tcpall_entry
+
+[TCP]
+FUNC_FLAG=ALL
+FUNC_NAME=stellar_on_sapp_tcp_entry
+
+[UDP]
+FUNC_FLAG=ALL
+FUNC_NAME=stellar_on_sapp_udp_entry \ No newline at end of file
diff --git a/test/http_decoder/test_env/tsg_l7_protocol.conf b/test/http_decoder/test_env/tsg_l7_protocol.conf
new file mode 100644
index 0000000..1075a8f
--- /dev/null
+++ b/test/http_decoder/test_env/tsg_l7_protocol.conf
@@ -0,0 +1,57 @@
+#TYPE:1:UCHAR,2:USHORT,3:USTRING,4:ULOG,5:USTRING,6:FILE,7:UBASE64,8:PACKET
+#TYPE FIELD VALUE
+STRING UNCATEGORIZED 8000
+#STRING UNCATEGORIZED 8001
+#STRING UNKNOWN_OTHER 8002
+STRING DNS 32
+STRING FTP 45
+STRING FTPS 751
+STRING HTTP 67
+STRING HTTPS 68
+STRING ICMP 70
+STRING IKE 8003
+STRING MAIL 8004
+STRING IMAP 75
+STRING IMAPS 76
+STRING IPSEC 85
+STRING XMPP 94
+STRING L2TP 98
+STRING NTP 137
+STRING POP3 147
+STRING POP3S 148
+STRING PPTP 153
+STRING QUIC 2521
+STRING SIP 182
+STRING SMB 185
+STRING SMTP 186
+STRING SMTPS 187
+STRING SPDY 1469
+STRING SSH 198
+STRING SSL 199
+STRING SOCKS 8005
+STRING TELNET 209
+STRING DHCP 29
+STRING RADIUS 158
+STRING OPENVPN 336
+STRING STUN 201
+STRING TEREDO 555
+STRING DTLS 1291
+STRING DoH 8006
+STRING ISAKMP 92
+STRING MDNS 3835
+STRING NETBIOS 129
+STRING NETFLOW 130
+STRING RDP 150
+STRING RTCP 174
+STRING RTP 175
+STRING SLP 8007
+STRING SNMP 190
+STRING SSDP 197
+STRING TFTP 211
+STRING BJNP 2481
+STRING LDAP 100
+STRING RTMP 337
+STRING RTSP 176
+STRING ESNI 8008
+STRING QQ 156
+STRING WeChat 1296
diff --git a/test/http_decoder/test_result_json/empty_array.json b/test/http_decoder/test_result_json/empty_array.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/test/http_decoder/test_result_json/empty_array.json
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/test/http_decoder/test_result_json/http_simple_result.json b/test/http_decoder/test_result_json/http_simple_result.json
new file mode 100644
index 0000000..988f0b7
--- /dev/null
+++ b/test/http_decoder/test_result_json/http_simple_result.json
@@ -0,0 +1,28 @@
+[
+ {
+ "Tuple4": "192.168.38.73.50806>192.168.40.137.80",
+ "method": "GET",
+ "uri": "/index.html",
+ "req_version": "1.1",
+ "major_version": 1,
+ "minor_version": 1,
+ "Host": "192.168.40.137",
+ "User-Agent": "curl/7.79.1",
+ "Accept": "*/*",
+ "name": "HTTP_DECODER_RESULT_1"
+ },
+ {
+ "Tuple4": "192.168.38.73.50806>192.168.40.137.80",
+ "res_version": "1.0",
+ "res_status": "OK",
+ "major_version": 1,
+ "minor_version": 0,
+ "status_code": 200,
+ "Server": "SimpleHTTP/0.6 Python/2.7.5",
+ "Date": "Thu, 30 Nov 2023 08:42:24 GMT",
+ "Content-type": "text/html",
+ "Content-Length": "144",
+ "Last-Modified": "Thu, 30 Nov 2023 08:38:54 GMT",
+ "name": "HTTP_DECODER_RESULT_2"
+ }
+]
diff --git a/test/http_decoder/test_result_json/proto_identify_http.json b/test/http_decoder/test_result_json/proto_identify_http.json
new file mode 100644
index 0000000..757d9e2
--- /dev/null
+++ b/test/http_decoder/test_result_json/proto_identify_http.json
@@ -0,0 +1,10 @@
+[
+ {
+ "Tuple4": "192.168.38.73.50806>192.168.40.137.80",
+ "STREAM_TYPE": "TCP",
+ "l7_label_id": [32],
+ "l7_label_name": ["HTTP"],
+ "STREAM_DIR": "C2S",
+ "name": "APP_PROTO_IDENTIFY_RESULT_1"
+ }
+]
diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt
index e373899..045e36f 100644
--- a/vendor/CMakeLists.txt
+++ b/vendor/CMakeLists.txt
@@ -1,5 +1,7 @@
include(ExternalProject)
+set(VENDOR_BUILD ${CMAKE_BINARY_DIR}/vendor/vbuild)
+
# GoogleTest
ExternalProject_Add(googletest PREFIX googletest
URL ${CMAKE_CURRENT_SOURCE_DIR}/googletest-release-1.8.0.tar.gz
@@ -19,3 +21,16 @@ 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)
+
+#llhttp-9.1.2
+ExternalProject_Add(llhttp PREFIX llhttp
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/llhttp-release-v9.1.3.tar.gz
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${VENDOR_BUILD} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_FLAGS="-fPIC")
+
+file(MAKE_DIRECTORY ${VENDOR_BUILD}/include)
+
+add_library(llhttp-static STATIC IMPORTED GLOBAL)
+add_dependencies(llhttp-static llhttp)
+
+set_property(TARGET llhttp-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include)
+set_property(TARGET llhttp-static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libllhttp.a) \ No newline at end of file
diff --git a/vendor/llhttp-release-v9.1.3.tar.gz b/vendor/llhttp-release-v9.1.3.tar.gz
new file mode 100644
index 0000000..c83dbd0
--- /dev/null
+++ b/vendor/llhttp-release-v9.1.3.tar.gz
Binary files differ