summaryrefslogtreecommitdiff
path: root/test/decoders
diff options
context:
space:
mode:
Diffstat (limited to 'test/decoders')
-rw-r--r--test/decoders/http/.gitkeep0
-rw-r--r--test/decoders/http/CMakeLists.txt50
-rw-r--r--test/decoders/http/base64.c164
-rw-r--r--test/decoders/http/base64.h28
-rw-r--r--test/decoders/http/http_decoder_gtest.cpp64
-rw-r--r--test/decoders/http/http_decoder_gtest.h68
-rw-r--r--test/decoders/http/http_decoder_perf_main.cpp226
-rw-r--r--test/decoders/http/http_decoder_perf_plug.cpp143
-rw-r--r--test/decoders/http/http_decoder_test_plug.cpp675
-rw-r--r--test/decoders/http/md5.c356
-rw-r--r--test/decoders/http/md5.h74
-rw-r--r--test/decoders/http/plugin_test_main.cpp150
-rw-r--r--test/decoders/http/test_based_on_stellar/CMakeLists.txt128
-rw-r--r--test/decoders/http/test_based_on_stellar/env/gtest_entry.toml3
-rw-r--r--test/decoders/http/test_based_on_stellar/env/http_decoder.toml18
-rw-r--r--test/decoders/http/test_based_on_stellar/env/log.toml4
-rw-r--r--test/decoders/http/test_based_on_stellar/env/spec.toml9
-rw-r--r--test/decoders/http/test_based_on_stellar/env/stellar.toml57
18 files changed, 2217 insertions, 0 deletions
diff --git a/test/decoders/http/.gitkeep b/test/decoders/http/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/decoders/http/.gitkeep
diff --git a/test/decoders/http/CMakeLists.txt b/test/decoders/http/CMakeLists.txt
new file mode 100644
index 0000000..78467f5
--- /dev/null
+++ b/test/decoders/http/CMakeLists.txt
@@ -0,0 +1,50 @@
+set(DECODER_NAME http)
+
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/toml DEPS_SRC)
+
+add_library(${DECODER_NAME}_test SHARED http_decoder_test_plug.cpp md5.c ${DEPS_SRC})
+add_dependencies(${DECODER_NAME}_test ${DECODER_NAME})
+target_link_libraries(${DECODER_NAME}_test cjson-static)
+set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "")
+
+# add_library(${DECODER_NAME}_perf SHARED http_decoder_perf_plug.cpp)
+# add_dependencies(${DECODER_NAME}_perf ${DECODER_NAME})
+# set_target_properties(${DECODER_NAME}_perf PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/testing)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_SOURCE_DIR}/deps)
+include_directories(/opt/tsg/stellar/include/)
+include_directories(/opt/tsg/framework/include/)
+include_directories(${CMAKE_BINARY_DIR}/vendor/llhttp/include)
+include_directories(${CMAKE_BINARY_DIR}/vendor/cjson/src/cjson/include)
+include_directories(${PROJECT_SOURCE_DIR}/decoders/http)
+include_directories(${PROJECT_SOURCE_DIR}/include/stellar)
+
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/mempool PERF_TEST_DEP_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/toml PERF_TEST_DEP_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/src PERF_TEST_DEP_SRC)
+
+add_executable(gtest_http http_decoder_gtest.cpp ${PROJECT_SOURCE_DIR}/decoders/http/http_decoder_utils.cpp)
+target_link_libraries(gtest_http gtest stellar_devel)
+
+add_executable(http_test_main plugin_test_main.cpp)
+set_target_properties(http_test_main
+ PROPERTIES
+ LINK_OPTIONS
+ "-rdynamic"
+ )
+set_target_properties(http_test_main
+ PROPERTIES
+ LINK_FLAGS
+ "-rdynamic"
+ )
+set(LINK_FLAGS "-rdynamic")
+target_link_libraries(http_test_main gtest cjson-static stellar_devel)
+
+add_subdirectory(test_based_on_stellar)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_http)
diff --git a/test/decoders/http/base64.c b/test/decoders/http/base64.c
new file mode 100644
index 0000000..eeded25
--- /dev/null
+++ b/test/decoders/http/base64.c
@@ -0,0 +1,164 @@
+/* This is a public domain base64 implementation written by WEI Zhicheng. */
+
+#include "base64.h"
+
+#define BASE64_PAD '='
+#define BASE64DE_FIRST '+'
+#define BASE64DE_LAST 'z'
+
+/* BASE 64 encode table */
+static const char base64en[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+};
+
+/* ASCII order for BASE 64 decode, 255 in unused character */
+static const unsigned char base64de[] = {
+ /* nul, soh, stx, etx, eot, enq, ack, bel, */
+ 255, 255, 255, 255, 255, 255, 255, 255,
+
+ /* bs, ht, nl, vt, np, cr, so, si, */
+ 255, 255, 255, 255, 255, 255, 255, 255,
+
+ /* dle, dc1, dc2, dc3, dc4, nak, syn, etb, */
+ 255, 255, 255, 255, 255, 255, 255, 255,
+
+ /* can, em, sub, esc, fs, gs, rs, us, */
+ 255, 255, 255, 255, 255, 255, 255, 255,
+
+ /* sp, '!', '"', '#', '$', '%', '&', ''', */
+ 255, 255, 255, 255, 255, 255, 255, 255,
+
+ /* '(', ')', '*', '+', ',', '-', '.', '/', */
+ 255, 255, 255, 62, 255, 255, 255, 63,
+
+ /* '0', '1', '2', '3', '4', '5', '6', '7', */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+
+ /* '8', '9', ':', ';', '<', '=', '>', '?', */
+ 60, 61, 255, 255, 255, 255, 255, 255,
+
+ /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */
+ 255, 0, 1, 2, 3, 4, 5, 6,
+
+ /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */
+ 7, 8, 9, 10, 11, 12, 13, 14,
+
+ /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+
+ /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */
+ 23, 24, 25, 255, 255, 255, 255, 255,
+
+ /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */
+ 255, 26, 27, 28, 29, 30, 31, 32,
+
+ /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */
+ 33, 34, 35, 36, 37, 38, 39, 40,
+
+ /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+
+ /* 'x', 'y', 'z', '{', '|', '}', '~', del, */
+ 49, 50, 51, 255, 255, 255, 255, 255
+};
+
+unsigned int
+base64_encode(const unsigned char *in, unsigned int inlen, char *out)
+{
+ int s;
+ unsigned int i;
+ unsigned int j;
+ unsigned char c;
+ unsigned char l;
+
+ s = 0;
+ l = 0;
+ for (i = j = 0; i < inlen; i++) {
+ c = in[i];
+
+ switch (s) {
+ case 0:
+ s = 1;
+ out[j++] = base64en[(c >> 2) & 0x3F];
+ break;
+ case 1:
+ s = 2;
+ out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xF)];
+ break;
+ case 2:
+ s = 0;
+ out[j++] = base64en[((l & 0xF) << 2) | ((c >> 6) & 0x3)];
+ out[j++] = base64en[c & 0x3F];
+ break;
+ }
+ l = c;
+ }
+
+ switch (s) {
+ case 1:
+ out[j++] = base64en[(l & 0x3) << 4];
+ out[j++] = BASE64_PAD;
+ out[j++] = BASE64_PAD;
+ break;
+ case 2:
+ out[j++] = base64en[(l & 0xF) << 2];
+ out[j++] = BASE64_PAD;
+ break;
+ }
+
+ out[j] = 0;
+
+ return j;
+}
+
+unsigned int
+base64_decode(const char *in, unsigned int inlen, unsigned char *out)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned char c;
+
+ if (inlen & 0x3) {
+ return 0;
+ }
+
+ for (i = j = 0; i < inlen; i++) {
+ if (in[i] == BASE64_PAD) {
+ break;
+ }
+ if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST) {
+ return 0;
+ }
+
+ c = base64de[(unsigned char)in[i]];
+ if (c == 255) {
+ return 0;
+ }
+
+ switch (i & 0x3) {
+ case 0:
+ out[j] = (c << 2) & 0xFF;
+ break;
+ case 1:
+ out[j++] |= (c >> 4) & 0x3;
+ out[j] = (c & 0xF) << 4;
+ break;
+ case 2:
+ out[j++] |= (c >> 2) & 0xF;
+ out[j] = (c & 0x3) << 6;
+ break;
+ case 3:
+ out[j++] |= c;
+ break;
+ }
+ }
+
+ return j;
+}
diff --git a/test/decoders/http/base64.h b/test/decoders/http/base64.h
new file mode 100644
index 0000000..9bea95a
--- /dev/null
+++ b/test/decoders/http/base64.h
@@ -0,0 +1,28 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1))
+#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3))
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/*
+ * out is null-terminated encode string.
+ * return values is out length, exclusive terminating `\0'
+ */
+unsigned int
+base64_encode(const unsigned char *in, unsigned int inlen, char *out);
+
+/*
+ * return values is out length
+ */
+unsigned int
+base64_decode(const char *in, unsigned int inlen, unsigned char *out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BASE64_H */
diff --git a/test/decoders/http/http_decoder_gtest.cpp b/test/decoders/http/http_decoder_gtest.cpp
new file mode 100644
index 0000000..68f275f
--- /dev/null
+++ b/test/decoders/http/http_decoder_gtest.cpp
@@ -0,0 +1,64 @@
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "http.h"
+#include "http_decoder_private.h"
+
+void httpd_url_decode(const char *string, size_t length, char *ostring, size_t *olen);
+TEST(http_url_decoder, none)
+{
+ char decode_url_buf[2048];
+ size_t decode_url_len = sizeof(decode_url_buf);
+ const char *encode_url = "https://docs.geedge.net/#all-updates";
+ httpd_url_decode(encode_url, strlen(encode_url), decode_url_buf, &decode_url_len);
+
+ EXPECT_STREQ("https://docs.geedge.net/#all-updates", decode_url_buf);
+ EXPECT_EQ(decode_url_len, strlen("https://docs.geedge.net/#all-updates"));
+}
+
+TEST(http_url_decoder, simple)
+{
+ char decode_url_buf[2048];
+ size_t decode_url_len = sizeof(decode_url_buf);
+ const char *encode_url = "http://a.b.cn/%A1%B2%C3%D4";
+ httpd_url_decode(encode_url, strlen(encode_url), decode_url_buf, &decode_url_len);
+
+ const unsigned char expect_result[] =
+ {0x68, 0x74, 0x74, 0x70,
+ 0x3A, 0x2F, 0x2F, 0x61,
+ 0x2E, 0x62, 0x2E, 0x63,
+ 0x6E, 0x2F, 0xA1, 0xB2,
+ 0xC3, 0xD4, 0x00
+ };
+ EXPECT_STREQ((char *)expect_result, decode_url_buf);
+ EXPECT_EQ(decode_url_len, sizeof(expect_result)-1);
+}
+
+TEST(http_url_decoder, chinese1)
+{
+ char decode_url_buf[2048];
+ size_t decode_url_len = sizeof(decode_url_buf);
+ const char *encode_url = "http://www.baidu.com/%E6%B5%8B%E8%AF%95%E4%B8%AD%E6%96%87%E8%A7%A3%E7%A0%81";
+ httpd_url_decode(encode_url, strlen(encode_url), decode_url_buf, &decode_url_len);
+
+ EXPECT_STREQ("http://www.baidu.com/\xE6\xB5\x8B\xE8\xAF\x95\xE4\xB8\xAD\xE6\x96\x87\xE8\xA7\xA3\xE7\xA0\x81", decode_url_buf);
+ EXPECT_EQ(decode_url_len, strlen("http://www.baidu.com/\xE6\xB5\x8B\xE8\xAF\x95\xE4\xB8\xAD\xE6\x96\x87\xE8\xA7\xA3\xE7\xA0\x81"));
+}
+
+TEST(http_url_decoder, chinese2)
+{
+ char decode_url_buf[2048];
+ size_t decode_url_len = sizeof(decode_url_buf);
+ const char *encode_url = "http%3A%2F%2Fwww.baidu.com%2F%E7%BC%96%E8%A7%A3%E7%A0%81%E6%B5%8B%E8%AF%95%E5%93%88%E5%93%88";
+ httpd_url_decode(encode_url, strlen(encode_url), decode_url_buf, &decode_url_len);
+
+ EXPECT_STREQ("http://www.baidu.com/\xE7\xBC\x96\xE8\xA7\xA3\xE7\xA0\x81\xE6\xB5\x8B\xE8\xAF\x95\xE5\x93\x88\xE5\x93\x88", decode_url_buf);
+ EXPECT_EQ(decode_url_len, strlen("http://www.baidu.com/\xE7\xBC\x96\xE8\xA7\xA3\xE7\xA0\x81\xE6\xB5\x8B\xE8\xAF\x95\xE5\x93\x88\xE5\x93\x88"));
+}
+
+
+int main(int argc, char const *argv[])
+{
+ ::testing::InitGoogleTest(&argc, (char **)argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/decoders/http/http_decoder_gtest.h b/test/decoders/http/http_decoder_gtest.h
new file mode 100644
index 0000000..eabae77
--- /dev/null
+++ b/test/decoders/http/http_decoder_gtest.h
@@ -0,0 +1,68 @@
+#pragma once
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <stellar/session.h>
+#include <stellar/stellar_mq.h>
+#include <stellar/stellar_exdata.h>
+#include <stellar/stellar.h>
+#ifdef __cplusplus
+}
+#endif
+#include "http.h"
+#include "md5.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <pcap/pcap.h>
+#include "cJSON.h"
+
+#define TRUE 1
+#define FLASE 0
+
+#define JSON_KEY_VALUE_STRING_MAX_LEN (4096)
+
+#ifndef MAX
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) >= (b) ? (b) : (a))
+#endif
+
+#define MMALLOC(type, size) ((type *)calloc(1, size))
+#define MFREE(p) \
+ do \
+ { \
+ free(p); \
+ p = NULL; \
+ } while (0)
+
+#if 0
+#define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define DEBUG_PRINT(fmt, ...)
+#endif
+
+#define EX_DATA_MAX_SIZE 10
+#define PIPELINE_MAX_NUM 8
+
+
+#define GTEST_FIX_PAYLOAD_CSTR "<Hello http decoder World!!!>"
+#define GTEST_FIX_PAYLOAD_MD5 "e91e072f772737c7a45013cc3b1a916c"
+
+#define GTEST_HTTP_URL_NAME "__X_HTTP_URL"
+#define GTEST_HTTP_TRANS_NAME "__X_HTTP_TRANSACTION"
+#define GTEST_HTTP_TRANS_SEQ_NAME "__X_HTTP_TRANSACTION_SEQ"
+#define GTEST_HTTP_TUPLE4_NAME "__X_HTTP_TUPLE4"
+#define GTEST_HTTP_PAYLOAD_NAME "__X_HTTP_PAYLOAD"
+#define GTEST_HTTP_RAW_PAYLOAD_MD5_NAME "__X_HTTP_RAW_PAYLOAD_MD5"
+#define GTEST_HTTP_DECOMPRESS_PAYLOAD_MD5_NAME "__X_HTTP_DECOMPRESS_PAYLOAD_MD5"
+
+
+struct stellar *stellar_init(void);
+void stellar_destroy(struct stellar *st);
+int stellar_load_plugin(struct stellar *st, void *(plugin_init_cb)(struct stellar *st));
+struct session *stellar_session_new(struct stellar *st, int topic_id, const char *payload, size_t payload_len, u_int8_t dir);
+void stellar_session_active(struct stellar *st, struct session *sess, int topic_id, const char *payload, size_t payload_len, u_int8_t dir);
+void stellar_session_close(struct stellar *st, struct session *sess, int topic_id); \ No newline at end of file
diff --git a/test/decoders/http/http_decoder_perf_main.cpp b/test/decoders/http/http_decoder_perf_main.cpp
new file mode 100644
index 0000000..7d314e9
--- /dev/null
+++ b/test/decoders/http/http_decoder_perf_main.cpp
@@ -0,0 +1,226 @@
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <stellar/session.h>
+#include <stellar/stellar_mq.h>
+#include <stellar/stellar_exdata.h>
+#include <stellar/stellar.h>
+#ifdef __cplusplus
+}
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <fieldstat/fieldstat_easy.h>
+#include "http_decoder_gtest.h"
+
+#define TIME_MEASURE 1
+#if TIME_MEASURE
+#define TIME_START() struct timespec _start_time, _end_time; long long time_diff_ns; clock_gettime(CLOCK_REALTIME, &_start_time)
+#define TIME_UPDATE() clock_gettime(CLOCK_REALTIME, &_start_time)
+#define TIME_DIFF() \
+ do { \
+ clock_gettime(CLOCK_REALTIME, &_end_time); \
+ if (_end_time.tv_sec == _start_time.tv_sec)\
+ {\
+ time_diff_ns = (_end_time.tv_nsec - _start_time.tv_nsec);\
+ }else{\
+ time_diff_ns = (_end_time.tv_sec * 1000 * 1000 * 1000 + _end_time.tv_nsec) - (_start_time.tv_sec * 1000 * 1000 * 1000 + _start_time.tv_nsec);\
+ }\
+ }while (0)
+#else
+#define TIME_START()
+#define TIME_DIFF()
+#endif
+
+struct test_packet{
+ const char *payload;
+ size_t payload_len;
+ u_int8_t dir;
+};
+extern "C" void *http_decoder_perf_plug_init(struct stellar *st);
+extern "C" void *http_decoder_init(struct stellar *st);
+extern "C" void http_decoder_exit(void *plugin_env);
+extern "C" void *httpd_session_ctx_new_cb(struct session *sess, void *plugin_env);
+extern "C" void _httpd_ex_data_free_cb(struct session *s, int idx,void *ex_data, void *arg);
+extern "C" void http_decoder_tcp_stream_msg_cb(struct session *sess, int topic_id, const void *msg, void *no_use_ctx, void *plugin_env);
+extern "C" void http_decoder_perf_entry(struct session *sess, int topic_id, const void *raw_msg, void *per_session_ctx, void *plugin_env);
+
+static struct fieldstat_easy *fs4_instance;
+static int fs4_simple_id;
+static int fs4_long_long_url_id;
+static int fs4_frag_id;
+
+static void perf_test_loop(struct stellar *st, int tcp_stream_topic_id, struct test_packet *test_payload, int test_payload_index_max, int fs4_metric_id)
+{
+ int idx = 0;
+ struct session *sess;
+ TIME_START();
+ sess = stellar_session_new(st, tcp_stream_topic_id, test_payload[idx].payload, test_payload[idx].payload_len, test_payload[idx].dir);
+ idx++;
+ TIME_DIFF();
+ fieldstat_easy_histogram_record(fs4_instance, 0, fs4_metric_id, NULL, 0, time_diff_ns);
+
+ for(; idx < test_payload_index_max; idx++){
+ TIME_UPDATE();
+ stellar_session_active(st, sess, tcp_stream_topic_id, test_payload[idx].payload, test_payload[idx].payload_len, test_payload[idx].dir);
+ TIME_DIFF();
+ fieldstat_easy_histogram_record(fs4_instance, 0, fs4_metric_id, NULL, 0, time_diff_ns);
+ }
+
+ TIME_UPDATE();
+ stellar_session_close(st, sess, tcp_stream_topic_id);
+ TIME_DIFF();
+ fieldstat_easy_histogram_record(fs4_instance, 0, fs4_metric_id, NULL, 0, time_diff_ns);
+}
+
+#define SET_DATA_LENGTH_DIR(pkt_payload, cstr_in_heap, cur_dir, tmp_index) \
+ pkt_payload.payload = cstr_in_heap; \
+ pkt_payload.payload_len = strlen(pkt_payload.payload); \
+ pkt_payload.dir = cur_dir;\
+ tmp_index++;
+
+static void init_test_data_simple(struct test_packet *test_payload, int *index)
+{
+ int tmp_index = 0;
+ const char *c2s_payload = strdup("GET / HTTP/1.1\r\nHost: www.simple.com\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n");
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], c2s_payload, FLOW_DIRECTION_C2S , tmp_index);
+
+ const char *content = "Hello, http decoder perf test simple!!!";
+ int content_length = strlen(content);
+ char *s2c_payload = (char *)malloc(1024);
+ snprintf(s2c_payload, 1024, "HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\nConnection: keep-alive\r\nDate: Sat, 01 May 2024 01:36:57 GMT\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: %d\r\n\r\n%s", content_length, content);
+
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], s2c_payload, FLOW_DIRECTION_S2C, tmp_index);
+ *index = tmp_index;
+}
+
+static void init_test_data_frag(struct test_packet *test_payload, int *index)
+{
+ int tmp_index = 0;
+ /* c2s */
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("GET / HTTP/1.1\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Host: www.fragment.com\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Cache-Control: max-age=0\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Connection: keep-alive\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Referer: http://fragment.com/register.jsp?redirect:http://aa.bb.cc.dd.com/?\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Cookie: JSESSIONID=385C79E211D561C0CA13D90F150F603D34875GH87FSHG8S7RTHG74875GHS8R7THG87SRTH\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Accept: */*\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Accept-Encoding: gzip, deflate\r\n"), FLOW_DIRECTION_C2S , tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("\r\n"), FLOW_DIRECTION_C2S , tmp_index); //header EOF
+
+ /* s2c */
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("HTTP/1.1 200 OK\r\n"), FLOW_DIRECTION_S2C, tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Server: Apache-Coyote/1.1\r\n"), FLOW_DIRECTION_S2C, tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Connection: keep-alive\r\n"), FLOW_DIRECTION_S2C, tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Content-Type: text/html;charset=UTF-8\r\n"), FLOW_DIRECTION_S2C, tmp_index);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("Date: Sat, 01 May 2024 01:36:57 GMT\r\n"), FLOW_DIRECTION_S2C, tmp_index);
+
+ char *cont_len_buf = (char *)malloc(1024);
+ const char *s2c_payload = strdup("Hello, http decoder perf test fragment!!!");
+ int content_length = strlen(s2c_payload);
+ snprintf(cont_len_buf, 1024, "Content-Length: %d\r\n", content_length);
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], cont_len_buf, FLOW_DIRECTION_S2C, tmp_index);
+
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], strdup("\r\n"), FLOW_DIRECTION_S2C, tmp_index); //header EOF
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], s2c_payload, FLOW_DIRECTION_S2C, tmp_index);
+ *index = tmp_index;
+}
+
+static void init_test_data_long_long_url(struct test_packet *test_payload, int *index)
+{
+ int tmp_index = 0;
+
+ const char *request_line_frag1 = strdup("GET /long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/");
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], request_line_frag1, FLOW_DIRECTION_C2S , tmp_index);
+
+ const char *request_line_frag2 = strdup("long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/\
+/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long.index.html HTTP/1.1\r\n");
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], request_line_frag2, FLOW_DIRECTION_C2S , tmp_index);
+
+ const char *request_line_frag3 = strdup("Host: www.long-long-url.com\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n");
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], request_line_frag3, FLOW_DIRECTION_C2S , tmp_index);
+
+ const char *content = "Hello, http decoder perf test long long url!!!";
+ int content_length = strlen(content);
+ char *s2c_payload = (char *)malloc(1024);
+ snprintf(s2c_payload, 1024, "HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\nConnection: keep-alive\r\nDate: Sat, 01 May 2024 01:36:57 GMT\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: %d\r\n\r\n%s", content_length, content);
+
+ SET_DATA_LENGTH_DIR(test_payload[tmp_index], s2c_payload, FLOW_DIRECTION_S2C, tmp_index);
+ *index = tmp_index;
+}
+
+static void test_payload_free(struct test_packet *test_payload, int test_payload_index_max)
+{
+ for(int i = 0; i < test_payload_index_max; i++){
+ if(test_payload[i].payload){
+ free((void *)test_payload[i].payload);
+ }
+ }
+}
+
+static void perf_stat_init(void)
+{
+ fs4_instance = fieldstat_easy_new(1, "http_decoder_test", NULL, 0);
+ fieldstat_easy_enable_auto_output(fs4_instance, "./httpd_hisgram.json", 1);
+
+ fs4_simple_id = fieldstat_easy_register_histogram(fs4_instance, "simple", 1, 99999999, 5);
+ fs4_frag_id = fieldstat_easy_register_histogram(fs4_instance, "frag", 1, 99999999, 5);
+ fs4_long_long_url_id = fieldstat_easy_register_histogram(fs4_instance, "long-url", 1, 99999999, 5);
+}
+
+int main(int argc, char const *argv[])
+{
+ struct test_packet test_payload_simple [4] = {};
+ int payload_index_simple = 0;
+ struct test_packet test_payload_long_long_url [8] = {};
+ int payload_index_long_long_url = 0;
+ struct test_packet test_payload_frag [32] = {};
+ int payload_index_frag = 0;
+
+ struct stellar *st = stellar_init();
+
+ int tcp_stream_topic_id = stellar_mq_get_topic_id(st, TOPIC_TCP_STREAM);
+
+ if(stellar_load_plugin(st, http_decoder_init) < 0){
+ fprintf(stderr, "load plugin 'http_decoder_init' failed\n");
+ return -1;
+ }
+
+ if(stellar_load_plugin(st, http_decoder_perf_plug_init) < 0){
+ fprintf(stderr, "load plugin 'http_decoder_perf_plug_init' failed\n");
+ return -1;
+ }
+
+ perf_stat_init();
+ init_test_data_simple(test_payload_simple, &payload_index_simple);
+ init_test_data_long_long_url(test_payload_long_long_url, &payload_index_long_long_url);
+ init_test_data_frag(test_payload_frag, &payload_index_frag);
+
+ // while(1){
+ for(int i = 0; i < 1000; i++){
+ perf_test_loop(st, tcp_stream_topic_id,test_payload_simple, payload_index_simple, fs4_simple_id);
+ perf_test_loop(st, tcp_stream_topic_id,test_payload_long_long_url, payload_index_long_long_url, fs4_long_long_url_id);
+ perf_test_loop(st, tcp_stream_topic_id,test_payload_frag, payload_index_frag, fs4_frag_id);
+ }
+
+ test_payload_free(test_payload_simple, sizeof(test_payload_simple)/sizeof(struct test_packet)) ;
+ test_payload_free(test_payload_long_long_url, sizeof(test_payload_long_long_url)/sizeof(struct test_packet)) ;
+ test_payload_free(test_payload_frag, sizeof(test_payload_frag)/sizeof(struct test_packet)) ;
+ stellar_destroy(st);
+ sleep(1);
+ fieldstat_easy_free(fs4_instance);
+ return 0;
+}
diff --git a/test/decoders/http/http_decoder_perf_plug.cpp b/test/decoders/http/http_decoder_perf_plug.cpp
new file mode 100644
index 0000000..338acaa
--- /dev/null
+++ b/test/decoders/http/http_decoder_perf_plug.cpp
@@ -0,0 +1,143 @@
+#include "http.h"
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#include "http_decoder_private.h"
+#include "cJSON.h"
+}
+#endif
+
+#define MAX_KEY_STR_LEN 2048
+
+static int g_result_count = 0;
+static int g_header_count = 1;
+static int g_exdata_idx = -1;
+static int g_topic_id = -1;
+static int g_plugin_id = -1;
+
+#define DEBUG_PRINT(fmt, ...) //printf(fmt, ##__VA_ARGS__)
+
+extern "C" void http_decoder_perf_entry(struct session *sess, int topic_id, const void *raw_msg, void *per_session_ctx, void *plugin_env)
+{
+ struct http_request_line req_line = {0};
+ struct http_response_line res_line = {0};
+ struct http_header header = {0};
+ hstring url = {};
+ hstring body = {};
+ struct http_message *msg = (struct http_message *)raw_msg;
+ enum http_message_type msg_type = http_message_type_get(msg);
+ void *ret1, *ret2;
+
+ switch (msg_type)
+ {
+ case HTTP_MESSAGE_REQ_LINE:
+ DEBUG_PRINT("---------------------------------------------------------------\n");
+ http_message_request_line_get0(msg, &req_line);
+ if (req_line.uri.iov_base)
+ {
+ DEBUG_PRINT("req_line.method.iov_base: %.*s\n", req_line.method.iov_len, req_line.method.iov_base);
+ ret1 = memmem(req_line.method.iov_base, req_line.method.iov_len, "PUT", 3);
+ DEBUG_PRINT("req_line.version.iov_base: %.*s\n", req_line.version.iov_len, req_line.version.iov_base);
+ }
+ break;
+ case HTTP_MESSAGE_REQ_HEADER:
+ while (http_message_header_next(msg, &header) >= 0)
+ {
+ ret1 = memmem(header.key.iov_base, header.key.iov_len, "key", 3);
+ ret2 = memmem(header.val.iov_base, header.val.iov_len, "val", 3);
+ DEBUG_PRINT("REQ header: %.*s : %.*s\n", (int)header.key.iov_len, header.key.iov_base, (int)header.val.iov_len, header.val.iov_base);
+ }
+ http_message_raw_url_get0(msg, &url);
+ if(url.iov_base && url.iov_len){
+ DEBUG_PRINT("URL: %.*s\n", url.iov_len, url.iov_base);
+ }
+ break;
+ case HTTP_MESSAGE_REQ_BODY:
+ // http_message_get_request_raw_body(msg, &body);
+ // output_http_body(&body, 0);
+ http_message_decompress_body_get0(msg, &body);
+ // output_http_body(&body, 1);
+ ret1 = memmem(body.iov_base, body.iov_len, "</html>", 7);
+ break;
+ case HTTP_MESSAGE_RES_LINE:
+ http_message_response_line_get0(msg, &res_line);
+ ret1 = memmem(res_line.status.iov_base, res_line.status.iov_len, "OK", 2);
+ DEBUG_PRINT("res_line.status.iov_base: %.*s\n", (int)res_line.status.iov_len, res_line.status.iov_base);
+ break;
+ case HTTP_MESSAGE_RES_HEADER:
+ while (http_message_header_next(msg, &header) >= 0)
+ {
+ ret1 = memmem(header.key.iov_base, header.key.iov_len, "key", 3);
+ ret2 = memmem(header.val.iov_base, header.val.iov_len, "val", 3);
+ DEBUG_PRINT("RES header: %.*s : %.*s\n", (int)header.key.iov_len, header.key.iov_base, (int)header.val.iov_len, header.val.iov_base);
+ }
+ break;
+ case HTTP_MESSAGE_RES_BODY_START:
+ case HTTP_MESSAGE_RES_BODY:
+ case HTTP_MESSAGE_RES_BODY_END:
+ http_message_raw_body_get0(msg, &body);
+ if(body.iov_base!=NULL && body.iov_len > 0){
+ DEBUG_PRINT("res raw body: %.*s\n", body.iov_len, body.iov_base);
+ }
+ // output_http_body(&body, 0);
+ http_message_decompress_body_get0(msg, &body);
+ if(body.iov_base!=NULL && body.iov_len > 0){
+ // output_http_body(&body, 1);
+ ret1 = memmem(body.iov_base, body.iov_len, "</html>", 7);
+ DEBUG_PRINT("res unzip body: %.*s\n", body.iov_len, body.iov_base);
+ DEBUG_PRINT("---------------------------------------------------------------\n");
+ }
+ break;
+ // to do: check payload
+ default:
+ break;
+ }
+ return;
+}
+
+static on_session_msg_cb_func*g_entry_fun = &http_decoder_perf_entry;
+
+static void http_decoder_test_exdata_free(int idx, void *ex_ptr, void *arg)
+{
+ return;
+}
+
+extern "C" void *http_decoder_perf_plug_init(struct stellar *st)
+{
+ g_plugin_id = stellar_session_plugin_register(st, NULL, NULL, NULL);
+ g_exdata_idx = stellar_exdata_new_index(st, "HTTP_DECODER_REQ_TEST",
+ http_decoder_test_exdata_free,
+ NULL);
+ if (g_exdata_idx < 0)
+ {
+ printf("[%s:%d]: can't get http_decoder exdata index !!!\n", __FUNCTION__, __LINE__);
+ exit(-1);
+ }
+
+ g_topic_id = stellar_mq_get_topic_id(st, "HTTP_DECODER_MESSAGE");
+ if (g_topic_id < 0)
+ {
+ printf("[%s:%d]: can't get http_decoder topic id !!!\n", __FUNCTION__, __LINE__);
+ exit(-1);
+ }
+
+ stellar_session_mq_subscribe(st, g_topic_id, g_entry_fun, g_plugin_id);
+ // printf("http_decoder_test_init OK!\n");
+
+ return NULL;
+}
+
+extern "C" void http_decoder_perf_plug_exit(void *test_ctx)
+{
+ if (test_ctx != NULL)
+ {
+ FREE(test_ctx);
+ }
+ printf("http_decoder_perf plug exit OK!\n");
+}
diff --git a/test/decoders/http/http_decoder_test_plug.cpp b/test/decoders/http/http_decoder_test_plug.cpp
new file mode 100644
index 0000000..86be09f
--- /dev/null
+++ b/test/decoders/http/http_decoder_test_plug.cpp
@@ -0,0 +1,675 @@
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include "http.h"
+#include "http_decoder_private.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#include "cJSON.h"
+#include "http_decoder_gtest.h"
+#include "md5.h"
+#include "toml/toml.h"
+
+ int commit_test_result_json(cJSON *node, const char *name);
+ extern void http_decoder_test_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env);
+ extern void http_decoder_test_state_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env);
+ extern void http_decoder_tunnel_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env);
+ static on_session_msg_cb_func *g_entry_fun = http_decoder_test_entry;
+}
+#endif
+
+#define MAX_KEY_STR_LEN 2048
+#define GTEST_PLUG_ENTRY_CFG "./etc/http/gtest_entry.toml"
+
+struct plug_entry_t
+{
+ const char *name;
+ on_session_msg_cb_func *entry;
+};
+
+static struct plug_entry_t g_entry_tbl[] = {
+ {"http_decoder_test_entry", http_decoder_test_entry},
+ {"http_decoder_test_state_entry", http_decoder_test_state_entry},
+ {"http_decoder_tunnel_entry", http_decoder_tunnel_entry},
+ {NULL, NULL}};
+
+enum http_transaction_type
+{
+ HTTP_TRANSACTION_REQ = 0,
+ HTTP_TRANSACTION_RES,
+ HTTP_TRANSACTION_SESSION, // global session info
+ HTTP_TRANSACTION_MAX
+};
+
+enum payload_compress_mode
+{
+ PAYLOAD_RAW = 0,
+ PAYLOAD_DECOMPRESS = 1,
+ PAYLOAD_MAX = 2,
+};
+
+struct gtest_plug_exdata_t
+{
+ cJSON *result_jnode[HTTP_TRANSACTION_MAX];
+ MD5_CTX *payload_md5ctx[PAYLOAD_MAX][HTTP_TRANSACTION_MAX];
+};
+
+static int g_result_count = 0;
+static int g_header_count = 1;
+static int g_http_gtest_plugin_id = -1;
+static int g_exdata_idx = -1;
+static int g_topic_id = -1;
+
+#if 0
+void output_http_req_line(struct http_request_line *req_line)
+{
+ char tmp_str[MAX_KEY_STR_LEN] = {0};
+ memcpy(tmp_str, req_line->method.iov_base, req_line->method.iov_len);
+ printf("req_method:%s\n", tmp_str);
+
+ memset(tmp_str, 0, sizeof(tmp_str));
+ memcpy(tmp_str, req_line->uri.iov_base, req_line->uri.iov_len);
+ printf("req_uri:%s\n", tmp_str);
+
+ memset(tmp_str, 0, sizeof(tmp_str));
+ memcpy(tmp_str, req_line->version.iov_base, req_line->version.iov_len);
+ printf("req_version:%s\n", tmp_str);
+}
+
+void output_http_res_line(struct http_response_line *res_line)
+{
+ char tmp_str[MAX_KEY_STR_LEN] = {0};
+ memcpy(tmp_str, res_line->version.iov_base, res_line->version.iov_len);
+ printf("res_version:%s\n", tmp_str);
+
+ memset(tmp_str, 0, sizeof(tmp_str));
+ memcpy(tmp_str, res_line->status.iov_base, res_line->status.iov_len);
+ printf("res_status:%s\n", tmp_str);
+}
+
+void output_http_header(struct http_header *header)
+{
+ char tmp_key[MAX_KEY_STR_LEN] = {0};
+ char tmp_val[MAX_KEY_STR_LEN] = {0};
+
+ memcpy(tmp_key, header->key.iov_base, header->key.iov_len);
+ memcpy(tmp_val, header->val.iov_base, header->val.iov_len);
+ printf("<%s:%s>\n", tmp_key, tmp_val);
+}
+
+void output_http_body(hstring *body, int decompress_flag)
+{
+ int counter = 0;
+
+ if (1 == decompress_flag)
+ {
+ printf("\n\n----------------decompress body len:%zu---------------\n",
+ body->iov_len);
+ }
+ else
+ {
+ printf("\n\n----------------raw body len:%zu---------------\n",
+ body->iov_len);
+ }
+
+ for (size_t i = 0; i < body->iov_len; i++)
+ {
+ if (counter % 16 == 0)
+ {
+ printf("\n");
+ }
+ printf("%02x ", (unsigned char)body->iov_base[i]);
+ counter++;
+ }
+ printf("\n");
+}
+#endif
+
+static void append_http_payload(struct session *sess, struct gtest_plug_exdata_t *gtest_plug_exdata, enum payload_compress_mode mode,
+ const hstring *body, enum http_transaction_type type)
+{
+ (void)sess;
+ if (NULL == body->iov_base || 0 == body->iov_len)
+ {
+ return;
+ }
+ if (NULL == gtest_plug_exdata->payload_md5ctx[mode][type])
+ {
+ gtest_plug_exdata->payload_md5ctx[mode][type] = MMALLOC(MD5_CTX, sizeof(MD5_CTX));
+ MD5Init(gtest_plug_exdata->payload_md5ctx[mode][type]);
+ }
+ MD5Update(gtest_plug_exdata->payload_md5ctx[mode][type], (unsigned char *)body->iov_base, body->iov_len);
+}
+
+int http_field_to_json(cJSON *object, const char *key, char *val, size_t val_len)
+{
+ if (NULL == object || NULL == key || NULL == val || 0 == val_len)
+ {
+ return -1;
+ }
+
+ char *tmp = CALLOC(char, val_len + 1);
+ memcpy(tmp, val, val_len);
+ cJSON_AddStringToObject(object, key, tmp);
+ FREE(tmp);
+
+ return 0;
+}
+
+void transaction_index_to_json(cJSON *ctx, int transaction_index)
+{
+ cJSON_AddNumberToObject(ctx, GTEST_HTTP_TRANS_SEQ_NAME, transaction_index);
+}
+
+void req_line_to_json(cJSON *ctx, struct http_request_line *req_line)
+{
+ http_field_to_json(ctx, "method", (char *)req_line->method.iov_base,
+ req_line->method.iov_len);
+ http_field_to_json(ctx, "uri", (char *)req_line->uri.iov_base, req_line->uri.iov_len);
+ http_field_to_json(ctx, "req_version", (char *)req_line->version.iov_base,
+ req_line->version.iov_len);
+
+ cJSON_AddNumberToObject(ctx, "major_version", req_line->major_version);
+ cJSON_AddNumberToObject(ctx, "minor_version", req_line->minor_version);
+}
+
+void res_line_to_json(cJSON *ctx, struct http_response_line *res_line)
+{
+ http_field_to_json(ctx, "res_version", (char *)res_line->version.iov_base,
+ res_line->version.iov_len);
+ http_field_to_json(ctx, "res_status", (char *)res_line->status.iov_base,
+ res_line->status.iov_len);
+
+ cJSON_AddNumberToObject(ctx, "major_version", res_line->major_version);
+ cJSON_AddNumberToObject(ctx, "minor_version", res_line->minor_version);
+ cJSON_AddNumberToObject(ctx, "status_code", res_line->status_code);
+}
+
+void http_header_to_json(cJSON *ctx, struct http_header *header)
+{
+ char key[MAX_KEY_STR_LEN] = {0};
+ if ((header->key.iov_base == NULL) || (header->val.iov_base == NULL))
+ {
+ return;
+ }
+
+ memcpy(key, header->key.iov_base, header->key.iov_len);
+
+ if (cJSON_HasObjectItem(ctx, key) == FALSE)
+ {
+ http_field_to_json(ctx, key, (char *)header->val.iov_base, header->val.iov_len);
+ }
+ else
+ {
+ // ctx already has the key, so rename key by key%d
+ char new_key[header->val.iov_len + 64] = {0};
+ snprintf(new_key,sizeof(new_key), "%s%d", key, g_header_count++);
+ http_field_to_json(ctx, new_key, (char *)header->val.iov_base, header->val.iov_len);
+ }
+}
+
+void http_url_add_to_json(cJSON *ctx, struct http_message *msg)
+{
+ hstring raw_url_result = {};
+
+ if (cJSON_GetObjectItem(ctx, GTEST_HTTP_URL_NAME))
+ {
+ return;
+ }
+
+ http_message_raw_url_get0(msg, &raw_url_result);
+ if (raw_url_result.iov_base == NULL)
+ {
+ return;
+ }
+
+ struct http_header raw_url_header_result = {};
+
+ raw_url_header_result.key.iov_base = (char *)GTEST_HTTP_URL_NAME;
+ raw_url_header_result.key.iov_len = strlen(GTEST_HTTP_URL_NAME);
+ raw_url_header_result.val = raw_url_result;
+
+ http_header_to_json(ctx, &raw_url_header_result);
+
+ hstring decode_url_result = {};
+ struct http_header decode_url_header_result = {};
+ http_message_decoded_url_get0(msg, &decode_url_result);
+ if (decode_url_result.iov_len != raw_url_result.iov_len)
+ {
+ decode_url_header_result.key.iov_base = (char *)"__X_HTTP_DECODED_URL";
+ decode_url_header_result.key.iov_len = strlen("__X_HTTP_DECODED_URL");
+ decode_url_header_result.val = decode_url_result;
+ http_header_to_json(ctx, &decode_url_header_result);
+ }
+}
+
+static void commit_payload_md5sum(cJSON *last_jnode, struct gtest_plug_exdata_t *gtest_plug_exdata, enum http_transaction_type type)
+{
+ // finish md5 streming hash
+ for (int mode = 0; mode < PAYLOAD_MAX; mode++)
+ {
+ if (gtest_plug_exdata->payload_md5ctx[mode][type])
+ {
+ unsigned char md5_result_bin[16] = {0};
+ unsigned char md5_result_cstr[33] = {0};
+ MD5Final(md5_result_bin, gtest_plug_exdata->payload_md5ctx[mode][type]);
+
+ for (int i = 0; i < 16; i++)
+ sprintf((char *)md5_result_cstr + 2 * i, "%02x", md5_result_bin[i]);
+ md5_result_cstr[32] = '\0';
+ if (mode == PAYLOAD_RAW)
+ {
+ cJSON_AddStringToObject(last_jnode, GTEST_HTTP_RAW_PAYLOAD_MD5_NAME, (char *)md5_result_cstr);
+ }
+ else
+ {
+ cJSON_AddStringToObject(last_jnode, GTEST_HTTP_DECOMPRESS_PAYLOAD_MD5_NAME, (char *)md5_result_cstr);
+ }
+ FREE(gtest_plug_exdata->payload_md5ctx[mode][type]);
+ gtest_plug_exdata->payload_md5ctx[mode][type] = NULL;
+ }
+ }
+}
+
+// Full duplex
+static void commit_last_half_flow_data(struct gtest_plug_exdata_t *gtest_plug_exdata,
+ struct http_message *msg, enum http_transaction_type type, int final)
+{
+ char result_name[MAX_KEY_STR_LEN] = {0};
+
+ cJSON *last_jnode = gtest_plug_exdata->result_jnode[type];
+ if (last_jnode)
+ {
+ commit_payload_md5sum(last_jnode, gtest_plug_exdata, type);
+ sprintf(result_name, "%d", g_result_count);
+ commit_test_result_json(last_jnode, result_name);
+ gtest_plug_exdata->result_jnode[type] = NULL;
+ g_result_count++;
+ }
+
+ if (0 == final)
+ {
+ gtest_plug_exdata->result_jnode[type] = cJSON_CreateObject();
+ if (HTTP_TRANSACTION_REQ == type)
+ {
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[type], GTEST_HTTP_TRANS_NAME, "request");
+ }
+ else if (HTTP_TRANSACTION_RES == type)
+ {
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[type], GTEST_HTTP_TRANS_NAME, "response");
+ }
+ if (msg)
+ {
+ transaction_index_to_json(gtest_plug_exdata->result_jnode[type], http_message_get_transaction_seq(msg));
+ }
+ }
+}
+
+static void http_decoder_test_update_session_tuple4(struct session *sess, struct gtest_plug_exdata_t *gtest_plug_exdata)
+{
+ if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION] == NULL)
+ {
+ const char *human_addr_cstr = session_get0_readable_addr(sess);
+ if (NULL == human_addr_cstr)
+ {
+ fprintf(stderr, "can't get readable_addr, to use session_get0_readable_addr() the sapp_log.conf level must <= INFO\n");
+ return;
+ }
+ char result_name[MAX_KEY_STR_LEN] = {0};
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION] = cJSON_CreateObject();
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], GTEST_HTTP_TUPLE4_NAME, human_addr_cstr);
+ sprintf(result_name, "%d", g_result_count++);
+ commit_test_result_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], result_name);
+ }
+}
+
+static int get_gtest_plug_entry(const char *cfg_path, char *entry_name, int max_len, char *topic_name, int topic_max_len)
+{
+ FILE *fp = fopen(cfg_path, "r");
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[%s:%d]Can't open config file:%s",
+ __FUNCTION__, __LINE__, cfg_path);
+ return -1;
+ }
+
+ char errbuf[256] = {0};
+
+ toml_table_t *root = toml_parse_file(fp, errbuf, sizeof(errbuf));
+ fclose(fp);
+
+ toml_table_t *basic_sec_tbl = toml_table_in(root, "entry");
+ if (NULL == basic_sec_tbl)
+ {
+ fprintf(stderr, "[%s:%d]config file:%s has no key: [entry]",
+ __FUNCTION__, __LINE__, cfg_path);
+ toml_free(root);
+ return -1;
+ }
+
+ toml_datum_t str_val = toml_string_in(basic_sec_tbl, "name");
+ if (str_val.ok != 0)
+ {
+ strncpy(entry_name, str_val.u.s, max_len);
+ free(str_val.u.s);
+ }
+
+ toml_datum_t topic_str_val = toml_string_in(basic_sec_tbl, "topic");
+ if (str_val.ok != 0)
+ {
+ strncpy(topic_name, topic_str_val.u.s, topic_max_len);
+ free(topic_str_val.u.s);
+ }
+
+ toml_free(root);
+ return 0;
+}
+
+static void set_gtest_plug_entry(const char *entry_name)
+{
+ if (NULL == entry_name)
+ {
+ g_entry_fun = http_decoder_test_entry; // set default
+ return;
+ }
+ for (size_t i = 0; g_entry_tbl[i].name != NULL; i++)
+ {
+ if (strcmp(entry_name, g_entry_tbl[i].name) == 0)
+ {
+ g_entry_fun = g_entry_tbl[i].entry;
+ return;
+ }
+ }
+ g_entry_fun = http_decoder_test_entry; // set default
+}
+
+extern "C" void http_decoder_test_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env)
+{
+ (void)topic_id;
+ (void)no_use_ctx;
+ (void)plugin_env;
+ struct http_request_line req_line = {};
+ struct http_response_line res_line = {};
+ struct http_header header = {};
+ hstring body = {};
+ struct http_message *msg = (struct http_message *)raw_msg;
+ enum http_message_type msg_type = http_message_type_get(msg);
+
+ struct gtest_plug_exdata_t *gtest_plug_exdata = (struct gtest_plug_exdata_t *)session_exdata_get(sess, g_exdata_idx);
+ if (NULL == gtest_plug_exdata)
+ {
+ gtest_plug_exdata = (struct gtest_plug_exdata_t *)calloc(1, sizeof(struct gtest_plug_exdata_t));
+ session_exdata_set(sess, g_exdata_idx, gtest_plug_exdata);
+ }
+
+ // if (http_message_type_is_req(sess, msg_type))
+ // {
+ // cJSON *json = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ];
+ // }
+ // else
+ // {
+ // cJSON *json = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES];
+ // }
+
+ http_decoder_test_update_session_tuple4(sess, gtest_plug_exdata);
+
+ switch (msg_type)
+ {
+ case HTTP_MESSAGE_REQ_LINE:
+ commit_last_half_flow_data(gtest_plug_exdata, msg, HTTP_TRANSACTION_REQ, 0);
+ http_message_request_line_get0(msg, &req_line);
+ req_line_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], &req_line);
+ break;
+ case HTTP_MESSAGE_REQ_HEADER:
+ while (http_message_header_next(msg, &header) >= 0)
+ {
+ http_header_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], &header);
+ }
+ g_header_count = 1;
+ break;
+ case HTTP_MESSAGE_REQ_HEADER_END:
+ http_url_add_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], msg);
+ break;
+ case HTTP_MESSAGE_REQ_BODY_START:
+ case HTTP_MESSAGE_REQ_BODY:
+ memset(&body, 0, sizeof(hstring));
+ http_message_raw_body_get0(msg, &body);
+ if (body.iov_base && body.iov_len)
+ {
+ append_http_payload(sess, gtest_plug_exdata, PAYLOAD_RAW, &body, HTTP_TRANSACTION_REQ);
+ }
+ // output_http_body(&body, 0);
+ http_message_decompress_body_get0(msg, &body);
+ // output_http_body(&body, 1);
+ if (body.iov_base && body.iov_len)
+ {
+ append_http_payload(sess, gtest_plug_exdata, PAYLOAD_DECOMPRESS, &body, HTTP_TRANSACTION_REQ);
+ }
+ break;
+ case HTTP_MESSAGE_RES_LINE:
+ commit_last_half_flow_data(gtest_plug_exdata, msg, HTTP_TRANSACTION_RES, 0);
+ http_message_response_line_get0(msg, &res_line);
+ res_line_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], &res_line);
+ break;
+ case HTTP_MESSAGE_RES_HEADER:
+ while (http_message_header_next(msg, &header) >= 0)
+ {
+ http_header_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], &header);
+ }
+ g_header_count = 1;
+ break;
+ case HTTP_MESSAGE_RES_BODY_START:
+ case HTTP_MESSAGE_RES_BODY:
+ memset(&body, 0, sizeof(hstring));
+ http_message_raw_body_get0(msg, &body);
+ if (body.iov_base && body.iov_len)
+ {
+ append_http_payload(sess, gtest_plug_exdata, PAYLOAD_RAW, &body, HTTP_TRANSACTION_RES);
+ }
+ // output_http_body(&body, 0);
+ memset(&body, 0, sizeof(hstring));
+ http_message_decompress_body_get0(msg, &body);
+ // output_http_body(&body, 1);
+ if (body.iov_base && body.iov_len)
+ {
+ append_http_payload(sess, gtest_plug_exdata, PAYLOAD_DECOMPRESS, &body, HTTP_TRANSACTION_RES);
+ }
+ break;
+
+ // to do: check payload
+ default:
+ break;
+ }
+
+ return;
+}
+
+void http_decoder_test_exdata_free(int idx, void *ex_ptr, void *arg)
+{
+ (void)idx;
+ (void)arg;
+ if (ex_ptr != NULL)
+ {
+ struct gtest_plug_exdata_t *gtest_plug_exdata = (struct gtest_plug_exdata_t *)ex_ptr;
+ if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ])
+ {
+ commit_last_half_flow_data(gtest_plug_exdata, NULL, HTTP_TRANSACTION_REQ, 1);
+ }
+ if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES])
+ {
+ commit_last_half_flow_data(gtest_plug_exdata, NULL, HTTP_TRANSACTION_RES, 1);
+ }
+ free(ex_ptr);
+ }
+}
+
+// static int update_config_file(const char *filename, const char *key, const char *value)
+// {
+// char cmd_buf[1024] = {};
+// snprintf(cmd_buf, 1024, "sed 's/^[ \t]*%s=.*/%s=%s/g' -i %s", key, key, value, filename);
+// int ret = system(cmd_buf);
+// return ret;
+// }
+
+extern "C" void http_decoder_test_state_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env)
+{
+ (void)topic_id;
+ (void)no_use_ctx;
+ (void)plugin_env;
+ static int msg_index = 0;
+ char msg_index_name[64] = {};
+ char msg_index_value[64] = {};
+ cJSON *json_object = NULL;
+ enum http_message_type msg_type = http_message_type_get((struct http_message *)raw_msg);
+
+ struct gtest_plug_exdata_t *gtest_plug_exdata = (struct gtest_plug_exdata_t *)session_exdata_get(sess, g_exdata_idx);
+ if (NULL == gtest_plug_exdata)
+ {
+ gtest_plug_exdata = (struct gtest_plug_exdata_t *)calloc(1, sizeof(struct gtest_plug_exdata_t));
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ] = cJSON_CreateObject();
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES] = cJSON_CreateObject();
+ session_exdata_set(sess, g_exdata_idx, gtest_plug_exdata);
+ }
+
+ if (http_message_type_is_req(sess, msg_type))
+ {
+ json_object = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ];
+ }
+ else
+ {
+ json_object = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES];
+ }
+ if (HTTP_TRANSACTION_END == msg_type)
+ {
+ unsigned char flow_flag;
+ session_is_symmetric(sess, &flow_flag);
+ if (SESSION_SEEN_C2S_FLOW == flow_flag)
+ {
+ json_object = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ];
+ }
+ else if (SESSION_SEEN_S2C_FLOW == flow_flag)
+ {
+ json_object = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES];
+ }
+ else
+ { // is symmetric
+ json_object = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES];
+ }
+ }
+ int cur_transaction_id = http_message_get_transaction_seq((const http_message *)raw_msg);
+
+ if (HTTP_TRANSACTION_START == msg_type || HTTP_TRANSACTION_END == msg_type)
+ {
+ snprintf(msg_index_name, sizeof(msg_index_name), "msg_%d", msg_index++);
+ snprintf(msg_index_value, sizeof(msg_index_value), "%s_transaction_%d", http_message_type_to_string(msg_type), cur_transaction_id);
+ cJSON_AddStringToObject(json_object, msg_index_name, msg_index_value);
+ }
+ else
+ {
+ snprintf(msg_index_name, sizeof(msg_index_name), "msg_%d", msg_index++);
+ cJSON_AddStringToObject(json_object, msg_index_name, http_message_type_to_string(msg_type));
+ }
+ return;
+}
+
+extern "C" void http_decoder_tunnel_entry(struct session *sess, int topic_id, const void *raw_msg, void *no_use_ctx, void *plugin_env)
+{
+ (void)topic_id;
+ (void)no_use_ctx;
+ (void)plugin_env;
+ struct gtest_plug_exdata_t *gtest_plug_exdata;
+ enum http_tunnel_message_type tmsg_type = http_tunnel_message_type_get((const struct http_tunnel_message *)raw_msg);
+ static size_t req_payload_block = 0, req_payload_size = 0;
+ static size_t res_payload_block = 0, res_payload_size = 0;
+ gtest_plug_exdata = (struct gtest_plug_exdata_t *)session_exdata_get(sess, g_exdata_idx);
+
+ switch (tmsg_type)
+ {
+ case HTTP_TUNNEL_OPENING:
+ {
+ if (NULL == gtest_plug_exdata)
+ {
+ gtest_plug_exdata = (struct gtest_plug_exdata_t *)calloc(1, sizeof(struct gtest_plug_exdata_t));
+ session_exdata_set(sess, g_exdata_idx, gtest_plug_exdata);
+ }
+ const char *human_addr_cstr = session_get0_readable_addr(sess);
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ] = cJSON_CreateObject();
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES] = cJSON_CreateObject();
+ gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION] = cJSON_CreateObject();
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], GTEST_HTTP_TUPLE4_NAME, human_addr_cstr);
+ commit_test_result_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], "TUNNEL_NEW");
+ }
+ break;
+
+ case HTTP_TUNNEL_ACTIVE:
+ {
+ enum flow_direction curdir = session_get_current_flow_direction(sess);
+ hstring tunnel_payload = {};
+ http_tunnel_message_get_payload((const struct http_tunnel_message *)raw_msg, &tunnel_payload);
+ if (FLOW_DIRECTION_C2S == curdir)
+ {
+ req_payload_block++;
+ req_payload_size += tunnel_payload.iov_len;
+ }
+ else
+ {
+ res_payload_block++;
+ res_payload_size += tunnel_payload.iov_len;
+ }
+ }
+ break;
+ case HTTP_TUNNEL_CLOSING:
+ {
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], "flow", "C2S");
+ cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], "flow", "S2C");
+ cJSON_AddNumberToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], "payload_block", req_payload_block);
+ cJSON_AddNumberToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], "payload_size", req_payload_size);
+ cJSON_AddNumberToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], "payload_block", res_payload_block);
+ cJSON_AddNumberToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], "payload_size", res_payload_size);
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+extern "C" void *http_decoder_test_init(struct stellar *st)
+{
+ g_http_gtest_plugin_id = stellar_session_plugin_register(st, NULL, NULL, NULL);
+ g_exdata_idx = stellar_exdata_new_index(st, "HTTP_DECODER_GTEST_EXDATA", http_decoder_test_exdata_free, NULL);
+ if (g_exdata_idx < 0)
+ {
+ printf("[%s:%d]: can't get http_decoder exdata index !!!\n", __FUNCTION__, __LINE__);
+ return NULL;
+ }
+
+ char entry_name[64] = "";
+ char topic_name[64] = "";
+ get_gtest_plug_entry(GTEST_PLUG_ENTRY_CFG, entry_name, sizeof(entry_name) - 1, topic_name, sizeof(topic_name) - 1);
+ set_gtest_plug_entry(entry_name);
+ g_topic_id = stellar_mq_get_topic_id(st, topic_name);
+ if (g_topic_id < 0)
+ {
+ printf("[%s:%d]: can't get http_decoder topic:%s id !!!\n", __FUNCTION__, __LINE__, topic_name);
+ return NULL;
+ }
+ stellar_session_mq_subscribe(st, g_topic_id, g_entry_fun, g_http_gtest_plugin_id);
+ printf("http_decoder_gtest_init succ, plugin_id:%d, sub_topic_id:%d\n", g_http_gtest_plugin_id, g_topic_id);
+
+ return (void *)strdup("http_decoder_test_ctx");
+}
+
+extern "C" void http_decoder_test_exit(void *test_ctx)
+{
+ if (test_ctx != NULL)
+ {
+ FREE(test_ctx);
+ }
+ printf("http_decoder_test_exit OK!\n");
+} \ No newline at end of file
diff --git a/test/decoders/http/md5.c b/test/decoders/http/md5.c
new file mode 100644
index 0000000..3df1eb5
--- /dev/null
+++ b/test/decoders/http/md5.c
@@ -0,0 +1,356 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software. */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include "md5.h"
+
+ /* Constants for MD5Transform routine. */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+ static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64]));
+ static void Encode PROTO_LIST((unsigned char *, UINT4 *, unsigned int));
+ static void Decode PROTO_LIST((UINT4 *, unsigned char *, unsigned int));
+ static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int));
+ static void MD5_memset PROTO_LIST((POINTER, int, unsigned int));
+
+ static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation. */
+#define FF(a, b, c, d, x, s, ac) \
+ { \
+ (a) += F((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ { \
+ (a) += G((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ { \
+ (a) += H((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ { \
+ (a) += I((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT((a), (s)); \
+ (a) += (b); \
+ }
+
+ /* MD5 initialization. Begins an MD5 operation, writing a new context. */
+ void MD5Init(MD5_CTX *context)
+ {
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+ }
+
+ /* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context. */
+ void MD5Update(
+ MD5_CTX *context, /* context */
+ unsigned char *input, /* input block */
+ unsigned int inputLen) /* length of input block */
+ {
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.*/
+ if (inputLen >= partLen)
+ {
+ MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen - i);
+ }
+
+ /* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context. */
+ void MD5Final(
+ unsigned char digest[16], /* message digest */
+ MD5_CTX *context) /* context */
+ {
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information.*/
+ MD5_memset((POINTER)context, 0, sizeof(*context));
+ }
+
+ /* MD5 basic transformation. Transforms state based on block. */
+ static void MD5Transform(
+ UINT4 state[4],
+ unsigned char block[64])
+ {
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ MD5_memset((POINTER)x, 0, sizeof(x));
+ }
+
+ /* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4. */
+ static void Encode(
+ unsigned char *output,
+ const UINT4 *input,
+ unsigned int len)
+ {
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+ }
+
+ /* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4. */
+ static void Decode(
+ UINT4 *output,
+ unsigned char *input,
+ unsigned int len)
+ {
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j + 1]) << 8) |
+ (((UINT4)input[j + 2]) << 16) | (((UINT4)input[j + 3]) << 24);
+ }
+
+ /* Note: Replace "for loop" with standard memcpy if possible. */
+
+ static void MD5_memcpy(
+ POINTER output,
+ const POINTER input,
+ unsigned int len)
+ {
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+
+ output[i] = input[i];
+ }
+
+ /* Note: Replace "for loop" with standard memset if possible. */
+ static void MD5_memset(
+ POINTER output,
+ int value,
+ unsigned int len)
+ {
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+ }
+
+ char *MESA_MD5_sum_str(unsigned char *raw_data, unsigned int raw_data_len, char result[33])
+ {
+ int i;
+ MD5_CTX context;
+ unsigned char digest[16];
+
+ MD5Init(&context);
+ MD5Update(&context, raw_data, raw_data_len);
+ MD5Final(digest, &context);
+
+ for (i = 0; i < 16; i++)
+ sprintf(result + 2 * i, "%02x", digest[i]);
+ result[32] = 0;
+
+ return result;
+ }
+
+ char *MESA_MD5_sum_bin(unsigned char *raw_data, unsigned int raw_data_len, char result[16])
+ {
+ MD5_CTX context;
+
+ MD5Init(&context);
+ MD5Update(&context, raw_data, raw_data_len);
+ MD5Final((unsigned char *)result, &context);
+
+ return result;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/test/decoders/http/md5.h b/test/decoders/http/md5.h
new file mode 100644
index 0000000..adf5457
--- /dev/null
+++ b/test/decoders/http/md5.h
@@ -0,0 +1,74 @@
+/* MD5.H - header file for MD5C.C */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software. */
+#ifndef __MD5_H
+#define __MD5_H
+
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+// typedef unsigned long int UINT4;
+typedef unsigned int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+ If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+ returns an empty list. */
+
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+
+/* MD5 context. */
+typedef struct
+{
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+#ifdef __cplusplus
+extern "C"
+{
+ void MD5Init(MD5_CTX *context);
+ void MD5Update(
+ MD5_CTX *context, /* context */
+ unsigned char *input, /* input block */
+ unsigned int inputLen); /* length of input block */
+ void MD5Final(
+ unsigned char digest[16], /* message digest */
+ MD5_CTX *context); /* context */
+ char *MESA_MD5_sum_str(unsigned char *raw_data, unsigned int raw_data_len, char result[33]);
+ char *MESA_MD5_sum_bin(unsigned char *raw_data, unsigned int raw_data_len, char result[16]);
+}
+#endif
+
+#endif
diff --git a/test/decoders/http/plugin_test_main.cpp b/test/decoders/http/plugin_test_main.cpp
new file mode 100644
index 0000000..a94c692
--- /dev/null
+++ b/test/decoders/http/plugin_test_main.cpp
@@ -0,0 +1,150 @@
+#include "cJSON.h"
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+#include "http_decoder_gtest.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/stellar.h"
+#ifdef __cplusplus
+}
+#endif
+
+// #define IGNORE_PRINTF
+#ifdef IGNORE_PRINTF
+#define printf(fmt, ...) (0)
+#endif
+static cJSON *g_test_result_root = NULL;
+static cJSON *g_load_result_root = NULL;
+static const char *result_json_path = NULL;
+
+extern "C" int commit_test_result_json(cJSON *node, const char *name)
+{
+ (void)name;
+ if (g_test_result_root)
+ {
+ // cJSON_AddItemToObject(g_test_result_root, name, node);
+ // cJSON_AddStringToObject(node, "name", name);
+ cJSON_AddItemToArray(g_test_result_root, node);
+ return 0;
+ }
+ return -1;
+}
+
+static void hdgt_prune_non_result_item(cJSON *benchmark_json_root)
+{
+ int array_size = cJSON_GetArraySize(benchmark_json_root);
+
+ for (int i = 0; i < array_size; i++)
+ {
+ cJSON *object_root = cJSON_GetArrayItem(benchmark_json_root, i);
+ cJSON_DeleteItemFromObject(object_root, GTEST_HTTP_PAYLOAD_NAME);
+ }
+}
+
+static cJSON *load_result_from_jsonfile(const char *json_path)
+{
+ if (json_path == NULL)
+ return NULL;
+
+ long file_len = 0;
+ char *file_content = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen(json_path, "r+");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_END);
+ file_len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if (file_len == 0)
+ {
+ fclose(fp);
+ return NULL;
+ }
+ file_content = (char *)malloc(file_len + 1);
+ fread(file_content, file_len, 1, fp);
+ file_content[file_len] = '\0';
+ cJSON *load = cJSON_Parse(file_content);
+ free(file_content);
+ fclose(fp);
+
+ hdgt_prune_non_result_item(load);
+
+ return load;
+}
+
+TEST(PROTOCOL, compare_result_json)
+{
+ EXPECT_EQ(cJSON_GetArraySize(g_test_result_root), cJSON_GetArraySize(g_load_result_root));
+ int ret = cJSON_Compare(g_test_result_root, g_load_result_root, 0);
+ EXPECT_EQ(1, ret);
+
+ if (ret != 1)
+ {
+ char *load_json_str = cJSON_Print(g_load_result_root);
+ printf("LOAD Raw:\n%s\n", load_json_str);
+ free(load_json_str);
+ char *result_json_str = cJSON_Print(g_test_result_root);
+ printf("TEST Raw:\n%s\n", result_json_str);
+ free(result_json_str);
+
+ cJSON *t_load = g_load_result_root->child, *t_test = g_test_result_root->child;
+ while (t_load != NULL)
+ {
+ ret = cJSON_Compare(t_load, t_test, 0);
+ if (ret != 1)
+ {
+ load_json_str = cJSON_Print(t_load);
+ printf("LOAD Diff:\n%s\n", load_json_str);
+ free(load_json_str);
+ result_json_str = cJSON_Print(t_test);
+ printf("TEST Diff:\n%s\n", result_json_str);
+ free(result_json_str);
+ goto fail;
+ }
+ t_load = t_load->next;
+ t_test = t_test->next;
+ }
+ }
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+fail:
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ if (argc < 2)
+ {
+ printf("Usage: %s <result_json_path>\n", argv[0]);
+ result_json_path = NULL;
+ }
+ else
+ {
+ result_json_path = argv[1];
+ g_test_result_root = cJSON_CreateArray();
+ g_load_result_root = load_result_from_jsonfile(result_json_path);
+ assert(g_load_result_root != NULL && g_test_result_root != NULL);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ if (stellar_run(argc - 1, argv + 1) < 0)
+ {
+ return -1;
+ }
+ if (result_json_path != NULL)
+ {
+ ret = RUN_ALL_TESTS();
+ }
+ return ret;
+}
diff --git a/test/decoders/http/test_based_on_stellar/CMakeLists.txt b/test/decoders/http/test_based_on_stellar/CMakeLists.txt
new file mode 100644
index 0000000..acc4bda
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/CMakeLists.txt
@@ -0,0 +1,128 @@
+set(DECODER_NAME http)
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/test/decoders/http)
+set(SAPP_DEVEL_DIR ${TEST_RUN_DIR}/lib)
+set(TEST_MAIN http_test_main)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/test)
+include_directories(/usr/local/include/cjson)
+include_directories(/opt/tsg/framework/include/stellar)
+include_directories(/opt/MESA/include/MESA)
+include_directories(/opt/tsg/stellar/include/)
+
+#various ways to add -rdynamic for centos7, centos8, and different cmake version
+add_definitions(-rdynamic)
+link_directories(${SAPP_DEVEL_DIR})
+
+# assemble test env
+add_test(NAME HTTP_MKDIR_METRIC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/metrics; mkdir -p ${TEST_RUN_DIR}/plugin; mkdir -p ${TEST_RUN_DIR}/log; mkdir -p ${TEST_RUN_DIR}/pcap")
+add_test(NAME HTTP_COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/spec.toml ${TEST_RUN_DIR}/plugin/spec.toml")
+add_test(NAME HTTP_COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/stellar.toml ${TEST_RUN_DIR}/conf/stellar.toml")
+add_test(NAME HTTP_COPY_LOG_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/log.toml ${TEST_RUN_DIR}/conf/log.toml")
+add_test(NAME HTTP_COPY_HTTP_DECODER_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/etc/http && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/http_decoder.toml ${TEST_RUN_DIR}/etc/http/")
+add_test(NAME HTTP_COPY_HTTP_GTEST_ENTRY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/etc/http && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/gtest_entry.toml ${TEST_RUN_DIR}/etc/http/")
+
+# update config files
+add_test(NAME HTTP_UPDATE_GTEST_PLUG_ENTRY COMMAND bash -c "sed -i 's/name=.*/name=\\x22http_decoder_test_entry\\x22/' ${TEST_RUN_DIR}/etc/http/gtest_entry.toml")
+add_test(NAME HTTP_UPDATE_GTEST_PLUG_TOPIC COMMAND bash -c "sed -i 's/topic=.*/topic=\\x22HTTP_DECODER_MESSAGE\\x22/' ${TEST_RUN_DIR}/etc/http/gtest_entry.toml")
+
+# update plugin to be tested
+add_test(NAME HTTP_CP_DECODER_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/decoders/http/http_dyn.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}.so")
+add_test(NAME HTTP_CP_DECODER_GTEST_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/test/decoders/http/${DECODER_NAME}_test.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}_test.so")
+
+set_tests_properties(HTTP_MKDIR_METRIC HTTP_COPY_SPEC HTTP_COPY_HTTP_DECODER_CONF HTTP_COPY_HTTP_GTEST_ENTRY_CONF
+ HTTP_COPY_CONF HTTP_COPY_LOG_CONF
+ HTTP_CP_DECODER_SO HTTP_CP_DECODER_GTEST_SO
+ HTTP_UPDATE_GTEST_PLUG_ENTRY HTTP_UPDATE_GTEST_PLUG_TOPIC
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+set(TEST_JSON_DIR ${PROJECT_SOURCE_DIR}/benchmarks/json/http)
+set(TEST_PCAP_DIR ${PROJECT_SOURCE_DIR}/benchmarks/pcap/http)
+
+# run tests
+add_test(NAME HTTP_GET_SINGLE_TRANS_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_single_trans.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_single_trans.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GET_MULTI_TRANS_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_multi_trans.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_multi_trans.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GET_LONG_COOKIE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_long_cookie.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_long_cookie.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GET_ENCODED_URI_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_encoded_uri.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_encoded_uri.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_RES_GZIP_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_res_gzip.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_res_gzip.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_CHUNKED_RES_GZIP_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_chunked_res_gzip.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_chunked_res_gzip.json"WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_OVER_TCP_KEEPALIVE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_over_tcp_keepalive.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_over_tcp_keepalive.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_OVER_PPPOE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_over_pppoe.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_over_pppoe.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_OVER_TLS_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_over_tls.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_over_tls.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME NON_HTTP_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/non_http.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/non_http.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_REQ_1BYTE_SLIDING_WINDOW_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_req_1byte_sliding_window.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_req_1byte_sliding_window.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_RES_1BYTE_SLIDING_WINDOW_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_res_1byte_sliding_window.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_res_1byte_sliding_window.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_NO_CONTENT_LENGTH_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_no_content_length.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_no_content_length.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_POST_MULTIPART_FORM_DATA_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_post_multipart_form_data.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_post_multipart_form_data.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_HEADERS_EXCEED_MAXIMUM_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_hdrs_exceed_maximum.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_hdrs_exceed_maximum.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GET_MALFORMED_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_malformed.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_malformed.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_URL_WITHOUT_HOST_V4_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_url_test_without_host.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_url_test_without_host.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_URL_WITHOUT_HOST_V6_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_url_test_without_host_v6.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_url_test_without_host_v6.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+#no SYN, steallar not support !
+# add_test(NAME HTTP_HEADER_VALUE_EMPTY_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_hdr_value_empty.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_hdr_value_empty.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_UPGRADE_WEBSOCKET_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_upgrade_websocket.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_upgrade_websocket.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MULTI_PARSE_ERROR_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_multi_parse_error.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_multi_parse_error.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GET_REQ_PIPELINE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_req_pipeline.pcap ${TEST_RUN_DIR}/pcap/test.pcap;./${TEST_MAIN} ${TEST_JSON_DIR}/http_get_req_pipeline.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_TRANS_PIPELINE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_trans_pipeline.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_trans_pipeline.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+#no SYN, steallar not support !
+# add_test(NAME HTTP_HEADER_TRUNCATED_IN_KV_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_hdr_truncated_in_kv.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_hdr_truncated_in_kv.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+# add_test(NAME HTTP_HEADER_TRUNCATED_AFTER_KV_TEST COMMAND ./${TEST_MAIN} ${TEST_JSON_DIR}/http_hdr_truncated_after_kv.json -r ${TEST_PCAP_DIR}/http_hdr_truncated_after_kv.pcap WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_FIN_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_fin.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/non_http.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+# add_test(NAME HTTP_TUNNEL_ONLY_HDR_TEST COMMAND ./${TEST_MAIN} ${TEST_JSON_DIR}/http_tunnel_s2c_only_hdr.json
+# -r ${TEST_PCAP_DIR}/http_tunnel_s2c_only_hdr.pcap WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_CHN_ENCODE_URL COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_chn_encode_url.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_chn_encode_url.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_ZLIB_DEADLOCK COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_zlib_deadlock.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_zlib_deadlock.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_OUT_OF_ORDER COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_out_of_order.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_out_of_order.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_GZIP_OUT_OF_ORDER COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_gzip_out_of_order.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_gzip_out_of_order.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
+set_tests_properties(HTTP_GET_SINGLE_TRANS_TEST
+ HTTP_GET_MULTI_TRANS_TEST
+ HTTP_GET_LONG_COOKIE_TEST
+ HTTP_GET_ENCODED_URI_TEST
+ HTTP_RES_GZIP_TEST
+ HTTP_CHUNKED_RES_GZIP_TEST
+ HTTP_OVER_TCP_KEEPALIVE_TEST
+ HTTP_OVER_PPPOE_TEST
+ HTTP_OVER_TLS_TEST
+ NON_HTTP_TEST
+ HTTP_REQ_1BYTE_SLIDING_WINDOW_TEST
+ HTTP_RES_1BYTE_SLIDING_WINDOW_TEST
+ HTTP_NO_CONTENT_LENGTH_TEST
+ HTTP_POST_MULTIPART_FORM_DATA_TEST
+ HTTP_HEADERS_EXCEED_MAXIMUM_TEST
+ HTTP_GET_MALFORMED_TEST
+ HTTP_URL_WITHOUT_HOST_V4_TEST
+ HTTP_URL_WITHOUT_HOST_V6_TEST
+ HTTP_MULTI_PARSE_ERROR_TEST
+ HTTP_UPGRADE_WEBSOCKET_TEST
+ HTTP_GET_REQ_PIPELINE_TEST
+ HTTP_TRANS_PIPELINE_TEST
+ HTTP_FIN_TEST
+ HTTP_CHN_ENCODE_URL
+ HTTP_ZLIB_DEADLOCK
+ HTTP_OUT_OF_ORDER
+ HTTP_GZIP_OUT_OF_ORDER
+ PROPERTIES FIXTURES_REQUIRED TestFixture)
+
+add_test(NAME HTTP_UPDATE_STATE_PLUG_ENTRY COMMAND bash -c "sed -i 's/name=.*/name=\\x22http_decoder_test_state_entry\\x22/' ${TEST_RUN_DIR}/etc/http/gtest_entry.toml")
+add_test(NAME HTTP_UPDATE_STATE_PLUG_TOPIC COMMAND bash -c "sed -i 's/topic=.*/topic=\\x22HTTP_DECODER_MESSAGE\\x22/' ${TEST_RUN_DIR}/etc/http/gtest_entry.toml")
+
+set_tests_properties(HTTP_UPDATE_STATE_PLUG_ENTRY HTTP_UPDATE_STATE_PLUG_TOPIC HTTP_CP_DECODER_SO HTTP_CP_DECODER_GTEST_SO HTTP_MKDIR_METRIC
+ PROPERTIES FIXTURES_SETUP TestState)
+
+add_test(NAME HTTP_MSG_TYPE_STATE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_post.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MSG_TYPE_STATE_C2S_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_post_c2s.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state_c2s.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MSG_TYPE_STATE_S2C_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_post_s2c.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state_s2c.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MSG_TYPE_STATE_PIPELINE_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_get_multi_trans.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state_pipeline.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MSG_TYPE_STATE_SES_EXCEPTION_C2S_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_session_exception_c2s.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state_exception_c2s.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+add_test(NAME HTTP_MSG_TYPE_STATE_SES_EXCEPTION_S2C_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/http_session_exception_s2c.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_JSON_DIR}/http_msg_type_state_exception_s2c.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
+set_tests_properties(HTTP_MSG_TYPE_STATE_TEST
+ HTTP_MSG_TYPE_STATE_C2S_TEST
+ HTTP_MSG_TYPE_STATE_S2C_TEST
+ HTTP_MSG_TYPE_STATE_PIPELINE_TEST
+ HTTP_MSG_TYPE_STATE_SES_EXCEPTION_C2S_TEST
+ HTTP_MSG_TYPE_STATE_SES_EXCEPTION_S2C_TEST
+ PROPERTIES FIXTURES_REQUIRED TestState)
+
diff --git a/test/decoders/http/test_based_on_stellar/env/gtest_entry.toml b/test/decoders/http/test_based_on_stellar/env/gtest_entry.toml
new file mode 100644
index 0000000..35e32ca
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/env/gtest_entry.toml
@@ -0,0 +1,3 @@
+[entry]
+name="http_decoder_test_state_entry"
+topic="HTTP_DECODER_MESSAGE"
diff --git a/test/decoders/http/test_based_on_stellar/env/http_decoder.toml b/test/decoders/http/test_based_on_stellar/env/http_decoder.toml
new file mode 100644
index 0000000..a2bb060
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/env/http_decoder.toml
@@ -0,0 +1,18 @@
+[basic]
+# Switch for decompress body, open(1) closed(0), default(0)
+decompress=1
+
+# per session mempool size, default(32Ki)
+mempool_size=32768
+
+# per session http parsed result queue length
+result_queue_len=16
+
+# call fieldstat every stat_interval_pkts
+stat_interval_pkts=1000
+
+# fieldstat output interval
+stat_output_interval=1
+
+# connect tunnel
+proxy_enable=0 \ No newline at end of file
diff --git a/test/decoders/http/test_based_on_stellar/env/log.toml b/test/decoders/http/test_based_on_stellar/env/log.toml
new file mode 100644
index 0000000..0246ed1
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/env/log.toml
@@ -0,0 +1,4 @@
+[log]
+output = file # stderr, file
+file = "log/stellar.log"
+level = DEBUG # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/test/decoders/http/test_based_on_stellar/env/spec.toml b/test/decoders/http/test_based_on_stellar/env/spec.toml
new file mode 100644
index 0000000..cce2c71
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/env/spec.toml
@@ -0,0 +1,9 @@
+[[plugin]]
+path = "./plugin/http.so"
+init = "http_decoder_init"
+exit = "http_decoder_exit"
+
+[[plugin]]
+path = "./plugin/http_test.so"
+init = "http_decoder_test_init"
+exit = "http_decoder_test_exit"
diff --git a/test/decoders/http/test_based_on_stellar/env/stellar.toml b/test/decoders/http/test_based_on_stellar/env/stellar.toml
new file mode 100644
index 0000000..9e11af5
--- /dev/null
+++ b/test/decoders/http/test_based_on_stellar/env/stellar.toml
@@ -0,0 +1,57 @@
+[id_generator]
+snowflake_worker_id_base = 1 # [0, 31]
+snowflake_worker_id_offset = 2 # [0, 127]
+
+[packet_io]
+mode = dumpfile # dumpfile, marsio
+app_symbol = stellar
+dev_symbol = nf_0_fw
+
+dumpfile_dir = "./pcap"
+nr_threads = 1 # [1, 256]
+#cpu_mask = [5, 6, 7, 8, 9, 10, 11, 12]
+cpu_mask = [5]
+
+[ip_reassembly]
+enable = 1
+timeout = 10000 # range: [1, 60000] (ms)
+bucket_entries = 256 # range: [1, 4294967295] (must be power of 2)
+bucket_num = 4096 # range: [1, 4294967295]
+
+[session_manager]
+# max session number
+max_tcp_session_num = 1000
+max_udp_session_num = 1000
+
+# session overload evict
+tcp_overload_evict_old_sess = 1 # 1: evict old session, 0: bypass new session
+udp_overload_evict_old_sess = 1 # 1: evict old session, 0: bypass new session
+
+# TCP timeout
+tcp_init_timeout = 10 # range: [1, 60000] (ms)
+tcp_handshake_timeout = 10 # range: [1, 60000] (ms)
+tcp_data_timeout = 10 # range: [1, 15999999000] (ms)
+tcp_half_closed_timeout = 10 # range: [1, 604800000] (ms)
+tcp_time_wait_timeout = 10 # range: [1, 600000] (ms)
+tcp_discard_timeout = 10 # range: [1, 15999999000] (ms)
+tcp_unverified_rst_timeout = 10 # range: [1, 600000] (ms)
+# UDP timeout
+udp_data_timeout = 10 # range: [1, 15999999000] (ms)
+udp_discard_timeout = 10 # range: [1, 15999999000] (ms)
+
+# duplicate packet filter
+duplicated_packet_filter_enable = 0
+duplicated_packet_filter_capacity = 1000000 # range: [1, 4294967295]
+duplicated_packet_filter_timeout = 10 # range: [1, 60000] (ms)
+duplicated_packet_filter_error_rate = 0.00001 # range: [0.0, 1.0]
+
+# evicted session filter
+evicted_session_filter_enable = 0
+evicted_session_filter_capacity = 1000000 # range: [1, 4294967295]
+evicted_session_filter_timeout = 10 # range: [1, 60000] (ms)
+evicted_session_filter_error_rate = 0.00001 # range: [0.0, 1.0]
+
+# TCP reassembly (Per direction)
+tcp_reassembly_enable = 1
+tcp_reassembly_max_timeout = 10 # range: [1, 60000] (ms)
+tcp_reassembly_max_segments = 256 # range: [2, 4096]