summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlijia <[email protected]>2024-04-08 09:48:13 +0800
committerlijia <[email protected]>2024-04-08 09:48:13 +0800
commit215e383be1f47cd18c235855d0cee0485f6cb423 (patch)
tree34668fd59622c37826c3a786ba0e196a7d65147b /src
parentea795e9c6940281bf8557bfd79f13f319f947c58 (diff)
Separate from stellar-on-sapp project.
Diffstat (limited to 'src')
-rw-r--r--src/.gitkeep0
-rw-r--r--src/CMakeLists.txt18
-rw-r--r--src/http_content_decompress.c268
-rw-r--r--src/http_content_decompress.h52
-rw-r--r--src/http_decoder.c858
-rw-r--r--src/http_decoder_half.c1034
-rw-r--r--src/http_decoder_half.h119
-rw-r--r--src/http_decoder_inc.h53
-rw-r--r--src/http_decoder_result_queue.c172
-rw-r--r--src/http_decoder_result_queue.h73
-rw-r--r--src/http_decoder_string.c277
-rw-r--r--src/http_decoder_string.h95
-rw-r--r--src/http_decoder_table.c556
-rw-r--r--src/http_decoder_table.h99
-rw-r--r--src/http_decoder_utils.c25
-rw-r--r--src/http_decoder_utils.h66
-rw-r--r--src/version.map9
17 files changed, 3774 insertions, 0 deletions
diff --git a/src/.gitkeep b/src/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/.gitkeep
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..2312d62
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_definitions(-fPIC)
+
+include_directories(/opt/MESA/include/)
+include_directories(${PROJECT_SOURCE_DIR}/deps/)
+
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/mempool DEPS_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/toml DEPS_SRC)
+
+set(HTTP_SRC ${DEPS_SRC} http_decoder.c http_decoder_utils.c http_decoder_half.c
+ http_decoder_table.c http_decoder_string.c http_content_decompress.c
+ http_decoder_result_queue.c)
+
+add_library(http_decoder SHARED ${HTTP_SRC})
+set_target_properties(http_decoder PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map")
+target_link_libraries(http_decoder z brotlidec llhttp-static fieldstat4)
+set_target_properties(http_decoder PROPERTIES PREFIX "")
+
+install(TARGETS http_decoder LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/stellar_plugin/${lib_name} COMPONENT LIBRARIES) \ No newline at end of file
diff --git a/src/http_content_decompress.c b/src/http_content_decompress.c
new file mode 100644
index 0000000..5fe08a8
--- /dev/null
+++ b/src/http_content_decompress.c
@@ -0,0 +1,268 @@
+/*
+**********************************************************************************************
+* 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 (16 * 1024)
+
+struct http_content_decompress {
+ enum http_content_encoding encoding;
+
+ z_stream *z_stream_ptr;
+ BrotliDecoderState *br_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->br_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->br_state = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+ if (decompress->br_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->br_state) {
+ BrotliDecoderDestroyInstance(decompress->br_state);
+ decompress->br_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;
+ z_stream_ptr->avail_out = (unsigned int)decompress->buffer_size;
+ z_stream_ptr->next_out = (unsigned char *)decompress->buffer;
+
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ int ret = 0;
+ do {
+ 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 (0 == z_stream_ptr->avail_out) {
+ decompress->buffer_size += have;
+ decompress->buffer = REALLOC(char, decompress->buffer,
+ decompress->buffer_size);
+ *outdata = decompress->buffer;
+ *outdata_len = *outdata_len + have;
+ http_decoder_log(DEBUG, "%s realloc outbuffer %zu bytes",
+ http_content_encoding_int2str(decompress->encoding),
+ decompress->buffer_size);
+ z_stream_ptr->avail_out = have;
+ z_stream_ptr->next_out = (unsigned char *)decompress->buffer +
+ (decompress->buffer_size - have);
+ } else {
+ *outdata = decompress->buffer;
+ *outdata_len = have;
+ }
+ }
+ } while (z_stream_ptr->avail_in != 0);
+
+ 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_in = indata_len;
+ const unsigned char *next_in = (const unsigned char *)indata;
+ size_t available_out = decompress->buffer_size;
+ unsigned char *next_out = (unsigned char *)decompress->buffer;
+
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ int ret;
+ for (;;) {
+ ret = BrotliDecoderDecompressStream(decompress->br_state, &available_in,
+ &next_in, &available_out, &next_out, 0);
+ size_t have = decompress->buffer_size - available_out;
+ if (have > 0) {
+ if (0 == available_out) {
+ decompress->buffer_size += have;
+ decompress->buffer = REALLOC(char, decompress->buffer,
+ decompress->buffer_size);
+ *outdata = decompress->buffer;
+ *outdata_len = *outdata_len + have;
+ available_out = have;
+ next_out = (unsigned char *)decompress->buffer +
+ (decompress->buffer_size - have);
+ } else {
+ *outdata = decompress->buffer;
+ *outdata_len = have;
+ }
+ }
+
+ 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->br_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_content_decompress.h b/src/http_content_decompress.h
new file mode 100644
index 0000000..3c2ba48
--- /dev/null
+++ b/src/http_content_decompress.h
@@ -0,0 +1,52 @@
+/*
+**********************************************************************************************
+* 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>
+
+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.c b/src/http_decoder.c
new file mode 100644
index 0000000..68f7c58
--- /dev/null
+++ b/src/http_decoder.c
@@ -0,0 +1,858 @@
+/*
+**********************************************************************************************
+* File: http_decoder.c
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-01-10
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "toml/toml.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 "http_decoder_result_queue.h"
+#include "llhttp.h"
+#include "http_decoder_inc.h"
+#include "fieldstat/fieldstat_easy.h"
+
+#define HTTP_IDENTIFY_LEN 16
+#define HD_RESULT_QUEUE_LEN 16
+
+#define DEFAULT_STAT_OUTPUT_INTERVAL 1
+#define DEFAULT_STAT_INTERVAL_PKTS 1000
+#define DEFAULT_MEMPOOL_SIZE (32 * 1024)
+
+const char *g_hd_cfg_path = "./etc/http/http_decoder.toml";
+const char *http_decoder_topic = "HTTP_DECODER_MESSAGE";
+const char *fs_file_name = "http_decoder.fs";
+
+struct http_decoder_config {
+ int decompress_switch;
+ int stat_interval_pkts; //call fieldstat_incrby every stat_interval_pkts
+ int stat_output_interval;
+ size_t result_queue_len; // per session result queue length
+ size_t mempool_size; // per session mempool size
+};
+
+/**
+ * NOTE: http_message don't have the ownership of data
+ */
+struct http_message {
+ enum http_message_type type;
+ struct http_decoder_result_queue *ref_queue;
+ size_t queue_index;
+};
+
+struct http_decoder {
+ struct http_decoder_half *c2s_half;
+ struct http_decoder_half *s2c_half;
+};
+
+struct http_decoder_exdata {
+ struct http_decoder_result_queue *queue;
+ struct http_decoder *decoder;
+ nmx_pool_t *mempool;
+};
+
+struct http_decoder_stat {
+ long long incoming_bytes;
+ long long incoming_pkts;
+ long long incoming_trans;
+ long long err_pkts;
+ int counter;
+};
+
+struct http_decoder_context {
+ int plugin_id;
+ int topic_id;
+ int ex_data_idx;
+ int fs_incoming_bytes_id;
+ int fs_incoming_pkts_id;
+ int fs_incoming_trans_id;
+ int fs_err_pkts_id;
+ struct stellar *st;
+ struct fieldstat_easy *fse;
+ struct http_decoder_config hd_cfg;
+};
+
+__thread struct http_decoder_stat _th_stat;
+
+struct http_message *
+http_message_new(enum http_message_type type,
+ struct http_decoder_result_queue *queue,
+ int queue_index)
+{
+ struct http_message *msg = CALLOC(struct http_message, 1);
+
+ msg->type = type;
+ msg->ref_queue = queue;
+ msg->queue_index = queue_index;
+
+ return msg;
+}
+
+static void http_message_free(void *http_msg, void *cb_arg)
+{
+ if (NULL == http_msg) {
+ return;
+ }
+
+ FREE(http_msg);
+}
+
+static void http_event_handler(enum http_event event,
+ struct http_decoder_half_data **data,
+ struct http_event_context *ev_ctx)
+{
+ assert(ev_ctx);
+
+ size_t queue_idx = 0;
+ nmx_pool_t *mempool = ev_ctx->ref_mempool;
+ struct http_decoder_result_queue *queue = ev_ctx->ref_queue;
+ struct http_message *msg = NULL;
+ struct http_decoder_half_data *half_data = NULL;
+ int ret = 0;
+
+ switch (event) {
+ case HTTP_EVENT_REQ_INIT:
+ half_data = http_decoder_result_queue_peek_req(queue);
+ if (half_data != NULL) {
+ http_decoder_result_queue_inc_req_index(queue);
+ }
+
+ half_data = http_decoder_result_queue_peek_req(queue);
+ if (half_data != NULL) {
+ half_data = http_decoder_result_queue_pop_req(queue);
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+
+ half_data = http_decoder_half_data_new(mempool);
+ ret = http_decoder_result_queue_push_req(queue, half_data);
+ if (ret < 0) {
+ fprintf(stderr, "http_decoder_result_queue_push req failed.");
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+ *data = half_data;
+ break;
+ case HTTP_EVENT_REQ_LINE:
+ queue_idx = http_decoder_result_queue_req_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_REQ_LINE, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ break;
+ case HTTP_EVENT_REQ_HDR_END:
+ {
+ int build_url_final = http_decoder_join_url_finally(ev_ctx, http_decoder_result_queue_peek_req(queue),mempool);
+ ret = http_decoder_half_data_has_parsed_header(*data);
+ if (0 == ret && 0 == build_url_final) {
+ break;
+ }
+ queue_idx = http_decoder_result_queue_req_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_REQ_HEADER, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ }
+ break;
+ case HTTP_EVENT_REQ_BODY_BEGIN:
+ break;
+ case HTTP_EVENT_REQ_BODY_DATA:
+ queue_idx = http_decoder_result_queue_req_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_REQ_BODY, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ break;
+ case HTTP_EVENT_REQ_BODY_END:
+ break;
+ case HTTP_EVENT_REQ_END:
+ http_decoder_result_queue_inc_req_index(queue);
+ half_data = http_decoder_result_queue_pop_req(queue);
+ if (half_data != NULL) {
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+ break;
+ case HTTP_EVENT_RES_INIT:
+ half_data = http_decoder_result_queue_peek_res(queue);
+ if (half_data != NULL) {
+ http_decoder_result_queue_inc_res_index(queue);
+ }
+
+ half_data = http_decoder_result_queue_peek_res(queue);
+ if (half_data != NULL) {
+ half_data = http_decoder_result_queue_pop_res(queue);
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+
+ half_data = http_decoder_half_data_new(mempool);
+ ret = http_decoder_result_queue_push_res(queue, half_data);
+ if (ret < 0) {
+ fprintf(stderr, "http_decoder_result_queue_push res failed.");
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+ *data = half_data;
+ break;
+ case HTTP_EVENT_RES_LINE:
+ queue_idx = http_decoder_result_queue_res_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_RES_LINE, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ break;
+ case HTTP_EVENT_RES_HDR:
+ break;
+ case HTTP_EVENT_RES_HDR_END:
+ ret = http_decoder_half_data_has_parsed_header(*data);
+ if (0 == ret) {
+ break;
+ }
+
+ queue_idx = http_decoder_result_queue_res_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_RES_HEADER, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ break;
+ case HTTP_EVENT_RES_BODY_BEGIN:
+ break;
+ case HTTP_EVENT_RES_BODY_DATA:
+ queue_idx = http_decoder_result_queue_res_index(queue);
+ msg = http_message_new(HTTP_MESSAGE_RES_BODY, queue, queue_idx);
+ session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg);
+ break;
+ case HTTP_EVENT_RES_BODY_END:
+ break;
+ case HTTP_EVENT_RES_END:
+ http_decoder_result_queue_inc_res_index(queue);
+ half_data = http_decoder_result_queue_pop_res(queue);
+ if (half_data != NULL) {
+ http_decoder_half_data_free(mempool, half_data);
+ half_data = NULL;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static struct http_decoder *
+http_decoder_new(nmx_pool_t *mempool, http_event_cb *ev_cb,
+ int decompress_switch)
+{
+ struct http_decoder *decoder = MEMPOOL_CALLOC(mempool, struct http_decoder, 1);
+ assert(decoder);
+
+ decoder->c2s_half = http_decoder_half_new(mempool, ev_cb, HTTP_REQUEST,
+ decompress_switch);
+ decoder->s2c_half = http_decoder_half_new(mempool, ev_cb, HTTP_RESPONSE,
+ decompress_switch);
+
+ return decoder;
+}
+
+static void
+http_decoder_free(nmx_pool_t *mempool, struct http_decoder *decoder)
+{
+ if (NULL == decoder) {
+ return;
+ }
+
+ if (decoder->c2s_half != NULL) {
+ http_decoder_half_free(mempool, decoder->c2s_half);
+ decoder->c2s_half = NULL;
+ }
+
+ if (decoder->s2c_half != NULL) {
+ http_decoder_half_free(mempool, decoder->s2c_half);
+ decoder->s2c_half = NULL;
+ }
+
+ MEMPOOL_FREE(mempool, decoder);
+}
+
+static struct http_decoder_exdata *
+http_decoder_exdata_new(size_t mempool_size, size_t queue_size,
+ int decompress_switch)
+{
+ struct http_decoder_exdata *ex_data = CALLOC(struct http_decoder_exdata, 1);
+
+ ex_data->mempool = nmx_create_pool(mempool_size);
+ ex_data->decoder = http_decoder_new(ex_data->mempool, http_event_handler,
+ decompress_switch);
+ ex_data->queue = http_decoder_result_queue_new(ex_data->mempool, queue_size);
+
+ return ex_data;
+}
+
+static void http_decoder_exdata_free(struct http_decoder_exdata *ex_data)
+{
+ if (NULL == ex_data) {
+ return;
+ }
+
+ if (ex_data->decoder != NULL) {
+ http_decoder_free(ex_data->mempool, ex_data->decoder);
+ ex_data->decoder = NULL;
+ }
+
+ if (ex_data->queue != NULL) {
+ http_decoder_result_queue_free(ex_data->mempool, ex_data->queue);
+ ex_data->queue = NULL;
+ }
+
+ nmx_destroy_pool(ex_data->mempool);
+
+ FREE(ex_data);
+}
+
+static int http_protocol_identify(const char *data, size_t data_len)
+{
+ llhttp_t parser;
+ llhttp_settings_t settings;
+ enum llhttp_errno error;
+
+ llhttp_settings_init(&settings);
+ llhttp_init(&parser, HTTP_BOTH, &settings);
+
+ error = llhttp_execute(&parser, data, data_len);
+ if (error != HPE_OK) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+http_decoder_stat_init(struct http_decoder_context *ctx, int thread_num)
+{
+ ctx->fse =
+ fieldstat_easy_new(thread_num, "http_decoder_statistics", NULL, 0);
+ if (NULL == ctx->fse) {
+ fprintf(stderr, "fieldstat_easy_new failed.");
+ return -1;
+ }
+
+ ctx->fs_incoming_bytes_id =
+ fieldstat_easy_register_counter(ctx->fse, "incoming_bytes");
+ if (ctx->fs_incoming_bytes_id < 0) {
+ fprintf(stderr, "fieldstat_easy_register_counter incoming_bytes failed.");
+ return -1;
+ }
+
+ ctx->fs_incoming_trans_id =
+ fieldstat_easy_register_counter(ctx->fse, "incoming_trans");
+ if (ctx->fs_incoming_trans_id < 0) {
+ fprintf(stderr, "fieldstat_easy_register_counter incoming_trans failed.");
+ return -1;
+ }
+
+ ctx->fs_incoming_pkts_id =
+ fieldstat_easy_register_counter(ctx->fse, "incoming_pkts");
+ if (ctx->fs_incoming_pkts_id < 0) {
+ fprintf(stderr, "fieldstat_easy_register_counter incoming_pkts failed.");
+ return -1;
+ }
+
+ ctx->fs_err_pkts_id = fieldstat_easy_register_counter(ctx->fse, "err_pkts");
+ if (ctx->fs_err_pkts_id < 0) {
+ fprintf(stderr, "fieldstat_easy_register_counter err_pkts failed.");
+ return -1;
+ }
+
+ int stat_output_interval = DEFAULT_STAT_OUTPUT_INTERVAL;
+ if (ctx->hd_cfg.stat_output_interval > 0) {
+ stat_output_interval = ctx->hd_cfg.stat_output_interval;
+ }
+
+ int ret = fieldstat_easy_enable_auto_output(ctx->fse, fs_file_name,
+ stat_output_interval);
+ if (ret < 0) {
+ fprintf(stderr, "fieldstat_easy_enable_auto_output failed.");
+ return -1;
+ }
+
+ sleep(1);
+
+ return 0;
+}
+
+static void
+http_decoder_stat_output(struct http_decoder_context *ctx, int thread_id)
+{
+ if (NULL == ctx || thread_id < 0) {
+ return;
+ }
+
+ int stat_interval_pkts = DEFAULT_STAT_INTERVAL_PKTS;
+ if (ctx->hd_cfg.stat_interval_pkts > 0) {
+ stat_interval_pkts = ctx->hd_cfg.stat_interval_pkts;
+ }
+
+ if (_th_stat.counter >= stat_interval_pkts) {
+ fieldstat_easy_counter_incrby(ctx->fse, thread_id,
+ ctx->fs_incoming_bytes_id, NULL, 0,
+ _th_stat.incoming_bytes);
+
+ fieldstat_easy_counter_incrby(ctx->fse, thread_id,
+ ctx->fs_incoming_pkts_id, NULL, 0,
+ _th_stat.incoming_pkts);
+
+ fieldstat_easy_counter_incrby(ctx->fse, thread_id,
+ ctx->fs_incoming_trans_id, NULL, 0,
+ _th_stat.incoming_trans);
+
+ fieldstat_easy_counter_incrby(ctx->fse, thread_id,
+ ctx->fs_err_pkts_id, NULL, 0,
+ _th_stat.err_pkts);
+
+ _th_stat.counter = 0;
+ _th_stat.err_pkts = 0;
+ _th_stat.incoming_bytes = 0;
+ _th_stat.incoming_pkts = 0;
+ _th_stat.incoming_trans = 0;
+ }
+}
+
+int http_decoder_entry(struct session *sess, int events,
+ const struct packet *pkt, void *cb_arg)
+{
+ struct http_decoder_context *ctx = (struct http_decoder_context *)cb_arg;
+ size_t payload_len = 0;
+ uint64_t inner_flag = 0;
+
+ int ret = session_is_inner_most(sess, &inner_flag);
+ if (0 == ret) {
+ return 0;
+ }
+
+ struct http_decoder_exdata *ex_data =
+ session_get_ex_data(sess, ctx->ex_data_idx);
+
+ if (events & SESS_EV_CLOSING) {
+ if (ex_data != NULL) {
+ http_decoder_exdata_free(ex_data);
+ session_set_ex_data(sess, ctx->ex_data_idx, NULL);
+ }
+
+ return 0;
+ }
+
+ const char *payload = session_get0_current_payload(sess, &payload_len);
+
+ if (events & SESS_EV_OPENING) {
+ assert(ex_data == NULL);
+
+ //If not http, ignore this session
+ if (payload_len > 0) {
+ size_t http_identify_len = payload_len > HTTP_IDENTIFY_LEN
+ ? HTTP_IDENTIFY_LEN
+ : payload_len;
+
+ ret = http_protocol_identify(payload, http_identify_len);
+ if (ret < 0) {
+ // ignore this session's event
+ struct session_event *s_event =
+ session_get_intrinsic_event(sess, ctx->plugin_id);
+
+ session_event_assign(s_event, ctx->st, sess, 0,
+ http_decoder_entry, ctx);
+ return 0;
+ }
+ }
+
+ ex_data = http_decoder_exdata_new(ctx->hd_cfg.mempool_size,
+ ctx->hd_cfg.result_queue_len,
+ ctx->hd_cfg.decompress_switch);
+ session_set_ex_data(sess, ctx->ex_data_idx, ex_data);
+ }
+
+ if (0 == payload_len || NULL == ex_data) {
+ return 0;
+ }
+
+ int dir = packet_get_direction(pkt);
+ if (dir < 0) {
+ return -1;
+ }
+
+ int thread_id = session_get_current_thread_id(sess);
+ struct http_decoder_half *cur_half = NULL;
+
+ if (dir == PACKET_DIRECTION_C2S) {
+ cur_half = ex_data->decoder->c2s_half;
+ } else {
+ cur_half = ex_data->decoder->s2c_half;
+ }
+
+ http_decoder_half_reinit(cur_half, ctx->topic_id, ex_data->queue,
+ ex_data->mempool, sess);
+ ret = http_decoder_half_parse(cur_half, payload, payload_len);
+ if (ret < 0) {
+ _th_stat.err_pkts += 1;
+ }
+
+ _th_stat.incoming_bytes += payload_len;
+ _th_stat.incoming_pkts += 1;
+ _th_stat.incoming_trans += http_decoder_half_trans_count(cur_half);
+ _th_stat.counter++;
+
+ http_decoder_stat_output(ctx, thread_id);
+
+ return 0;
+}
+
+static void _http_decoder_context_free(struct http_decoder_context *ctx)
+{
+ if (NULL == ctx) {
+ return;
+ }
+
+ if (ctx->fse != NULL) {
+ fieldstat_easy_free(ctx->fse);
+ ctx->fse = NULL;
+ }
+
+ if (ctx->topic_id >= 0) {
+ session_mq_destroy_topic(ctx->st, ctx->topic_id);
+ ctx->topic_id = -1;
+ }
+
+ FREE(ctx);
+}
+
+static void http_decoder_ex_data_free(struct session *s, int idx,
+ void *ex_data, void *arg)
+{
+ if (NULL == ex_data) {
+ return;
+ }
+
+ struct http_decoder_exdata *exdata = (struct http_decoder_exdata *)ex_data;
+ http_decoder_exdata_free(exdata);
+}
+
+static int load_http_decoder_config(const char *cfg_path,
+ struct http_decoder_config *hd_cfg)
+{
+ 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;
+ }
+
+ int ret = 0;
+ 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, "basic");
+ if (NULL == basic_sec_tbl) {
+ fprintf(stderr, "[%s:%d]config file:%s has no key: [basic]",
+ __FUNCTION__, __LINE__, cfg_path);
+ ret = -1;
+ goto next;
+ }
+
+ toml_datum_t int_val = toml_int_in(basic_sec_tbl, "decompress");
+ if (int_val.ok != 0) {
+ hd_cfg->decompress_switch = int_val.u.b;
+ }
+
+ int_val = toml_int_in(basic_sec_tbl, "mempool_size");
+ if (int_val.ok != 0) {
+ hd_cfg->mempool_size = int_val.u.i;
+ } else {
+ hd_cfg->mempool_size = DEFAULT_MEMPOOL_SIZE;
+ }
+
+ int_val = toml_int_in(basic_sec_tbl, "result_queue_len");
+ if (int_val.ok != 0) {
+ hd_cfg->result_queue_len = int_val.u.i;
+ } else {
+ hd_cfg->result_queue_len = HD_RESULT_QUEUE_LEN;
+ }
+
+ int_val = toml_int_in(basic_sec_tbl, "stat_interval_pkts");
+ if (int_val.ok != 0) {
+ hd_cfg->stat_interval_pkts = int_val.u.i;
+ } else {
+ hd_cfg->stat_interval_pkts = DEFAULT_STAT_INTERVAL_PKTS;
+ }
+
+ int_val = toml_int_in(basic_sec_tbl, "stat_output_interval");
+ if (int_val.ok != 0) {
+ hd_cfg->stat_output_interval = int_val.u.i;
+ } else {
+ hd_cfg->stat_output_interval = DEFAULT_STAT_OUTPUT_INTERVAL;
+ }
+
+next:
+ toml_free(root);
+ return ret;
+}
+
+void *http_decoder_init(struct stellar *st)
+{
+ int plugin_id = -1;
+ int topic_id = -1;
+ int thread_num = 0;
+
+ struct http_decoder_context *ctx = CALLOC(struct http_decoder_context, 1);
+
+ int ret = load_http_decoder_config(g_hd_cfg_path, &ctx->hd_cfg);
+ if (ret < 0) {
+ goto failed;
+ }
+
+ ctx->st = st;
+ ctx->ex_data_idx = stellar_session_get_ex_new_index(st, "HTTP_DECODER",
+ http_decoder_ex_data_free,
+ NULL);
+
+ plugin_id = stellar_plugin_register(st, SESS_EV_TCP|SESS_EV_CLOSING,
+ http_decoder_entry, ctx);
+ if (plugin_id < 0) {
+ goto failed;
+ }
+
+ ctx->plugin_id = plugin_id;
+
+ 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;
+
+ thread_num = stellar_get_worker_thread_num(st);
+
+ if (http_decoder_stat_init(ctx, thread_num) < 0) {
+ goto failed;
+ }
+
+ 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;
+
+failed:
+ _http_decoder_context_free(ctx);
+ return NULL;
+}
+
+void http_decoder_exit(void *decoder_ctx)
+{
+ if (NULL == decoder_ctx) {
+ return;
+ }
+
+ struct http_decoder_context *ctx =
+ (struct http_decoder_context *)decoder_ctx;
+
+ _http_decoder_context_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;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_decoder_half_data_get_request_line(req_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;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *res_data =
+ msg->ref_queue->array[msg->queue_index].res_data;
+
+ return http_decoder_half_data_get_response_line(res_data, line);
+}
+
+int http_message_get_request_header(struct http_message *msg, struct hstring *key,
+ struct http_header *hdr_array, size_t array_size)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_HEADER ||
+ NULL == key || NULL == hdr_array || 0 == array_size) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_decoder_half_data_get_header(req_data, key, hdr_array, array_size);
+}
+
+int http_message_get_response_header(struct http_message *msg, struct hstring *key,
+ struct http_header *hdr_array, size_t array_size)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_RES_HEADER || NULL == key ||
+ NULL == hdr_array || 0 == array_size) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *res_data =
+ msg->ref_queue->array[msg->queue_index].res_data;
+
+ return http_decoder_half_data_get_header(res_data, key, hdr_array, array_size);
+}
+
+int http_message_request_header_next(struct http_message *msg,
+ struct http_header *hdr)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_HEADER
+ || NULL == hdr) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_decoder_half_data_iter_header(req_data, hdr);
+}
+
+int http_message_response_header_next(struct http_message *msg,
+ struct http_header *hdr)
+{
+ if (NULL == msg || msg->type != HTTP_MESSAGE_RES_HEADER ||
+ NULL == hdr) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *res_data =
+ msg->ref_queue->array[msg->queue_index].res_data;
+
+ return http_decoder_half_data_iter_header(res_data, hdr);
+}
+
+int http_message_get_request_raw_body(struct http_message *msg,
+ struct hstring *body)
+{
+ if (NULL == msg || (msg->type != HTTP_MESSAGE_REQ_BODY) ||
+ NULL == body) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_decoder_half_data_get_raw_body(req_data, body);
+}
+
+int http_message_get_response_raw_body(struct http_message *msg,
+ struct hstring *body)
+{
+ if (NULL == msg || (msg->type != HTTP_MESSAGE_RES_BODY) ||
+ NULL == body) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *res_data =
+ msg->ref_queue->array[msg->queue_index].res_data;
+
+ return http_decoder_half_data_get_raw_body(res_data, body);
+}
+
+int http_message_get_request_decompress_body(struct http_message *msg,
+ struct hstring *body)
+{
+ if (NULL == msg || (msg->type != HTTP_MESSAGE_REQ_BODY) ||
+ NULL == body) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_decoder_half_data_get_decompress_body(req_data, body);
+}
+
+int http_message_get_response_decompress_body(struct http_message *msg,
+ struct hstring *body)
+{
+ if (NULL == msg || (msg->type != HTTP_MESSAGE_RES_BODY) ||
+ NULL == body) {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *res_data =
+ msg->ref_queue->array[msg->queue_index].res_data;
+
+ return http_decoder_half_data_get_decompress_body(res_data, body);
+}
+
+int http_message_get_url(struct http_message *msg, struct hstring *url)
+{
+ if (NULL == msg || NULL == url)
+ {
+ return -1;
+ }
+
+ assert(msg->ref_queue);
+ assert(msg->queue_index < HD_RESULT_QUEUE_LEN);
+
+ struct http_decoder_half_data *req_data =
+ msg->ref_queue->array[msg->queue_index].req_data;
+
+ return http_half_data_get_url(req_data, url);
+} \ No newline at end of file
diff --git a/src/http_decoder_half.c b/src/http_decoder_half.c
new file mode 100644
index 0000000..56a7838
--- /dev/null
+++ b/src/http_decoder_half.c
@@ -0,0 +1,1034 @@
+/*
+**********************************************************************************************
+* File: http_decoder_half.c
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-01-10
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "stellar/utils.h"
+#include "stellar/session_mq.h"
+#include "llhttp.h"
+#include "http_decoder.h"
+#include "http_decoder_inc.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;
+ struct http_content_decompress *decompress;
+ char *ref_decompress_body;
+ size_t decompress_body_len;
+
+ int joint_url_complete;
+ struct hstring joint_url; // http://<host>[:<port>]/<path>?<searchpart>
+};
+
+struct http_decoder_half {
+ llhttp_t parser;
+ llhttp_settings_t settings;
+ enum llhttp_errno error;
+
+ int decompress_switch;
+
+ enum http_event event;
+ http_event_cb *http_ev_cb;
+ struct http_event_context *http_ev_ctx;
+
+ struct http_decoder_half_data *ref_data;
+
+ long long trans_counter;
+ long long err_counter;
+};
+
+// #define HTTP_DECODER_DEBUG
+#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);
+ 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);
+
+ 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;
+ }
+
+ if (NULL == data->decompress) {
+ data->decompress = http_content_decompress_create(data->content_encoding);
+ }
+
+ assert(data->decompress);
+ if (http_content_decompress_write(data->decompress, raw_body.str,
+ raw_body.str_len,
+ &data->ref_decompress_body,
+ &data->decompress_body_len) == -1) {
+ // log error
+ http_content_decompress_destroy(data->decompress);
+ data->decompress = NULL;
+ }
+}
+
+/* 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);
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_INIT;
+ } else {
+ half->event = HTTP_EVENT_RES_INIT;
+ }
+
+ half->ref_data = NULL;
+
+ assert(half->http_ev_cb != NULL);
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+
+ half->trans_counter++;
+
+ 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);
+
+ if (half->parser.type == HTTP_REQUEST) {
+ if (half->event == HTTP_EVENT_REQ_BODY_DATA) {
+ half->event = HTTP_EVENT_REQ_BODY_END;
+ if (half->http_ev_cb != NULL) {
+ half->http_ev_cb(half->event, &half->ref_data,
+ half->http_ev_ctx);
+ }
+ }
+ } else {
+ if (half->event == HTTP_EVENT_RES_BODY_DATA) {
+ half->event = HTTP_EVENT_RES_BODY_END;
+ if (half->http_ev_cb != NULL) {
+ half->http_ev_cb(half->event, &half->ref_data,
+ half->http_ev_ctx);
+ }
+ }
+ }
+
+ //trigger req_end/res_end
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_END;
+ if (half->http_ev_cb != NULL) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ } else {
+ half->event = HTTP_EVENT_RES_END;
+ if (half->http_ev_cb != NULL) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ 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);
+
+ http_decoder_table_refer(half->ref_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 (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_METHOD) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_METHOD);
+ }
+
+ http_decoder_table_commit(half->ref_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);
+
+ http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_URI,
+ at, length);
+ return 0;
+}
+
+static void http_decoder_cached_portion_url(struct http_decoder_half *half, const struct hstring *uri_result)
+{
+ struct http_decoder_half_data *ref_data = half->ref_data;
+
+ ref_data->joint_url.str_len = uri_result->str_len;
+ ref_data->joint_url.str = MEMPOOL_CALLOC(half->http_ev_ctx->ref_mempool, char, uri_result->str_len);
+ // ref_data->joint_url.str = (char *)malloc(uri_result->str_len);
+ memcpy(ref_data->joint_url.str, uri_result->str, uri_result->str_len);
+}
+
+/* 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 (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_URI) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_URI);
+ }
+
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_URI);
+
+ struct hstring uri_result = {};
+ http_decoder_table_get_uri(half->ref_data->table, &uri_result);
+ assert(uri_result.str);
+ http_decoder_cached_portion_url(half, &uri_result);
+
+ 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);
+
+ http_decoder_table_refer(half->ref_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 (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_VERSION) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_VERSION);
+ }
+
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_VERSION);
+
+ half->ref_data->major_version = llhttp_get_http_major(&half->parser);
+ half->ref_data->minor_version = llhttp_get_http_minor(&half->parser);
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_LINE;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ 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);
+
+ http_decoder_table_refer(half->ref_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 (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_STATUS) ==
+ STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_STATUS);
+ }
+
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_STATUS);
+ half->ref_data->status_code = llhttp_get_status_code(&half->parser);
+
+ if (half->parser.type == HTTP_RESPONSE) {
+ half->event = HTTP_EVENT_RES_LINE;
+ if (half->http_ev_cb != NULL) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ 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);
+
+ http_decoder_table_refer(half->ref_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);
+
+ http_decoder_table_commit(half->ref_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);
+
+ http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_HDRVAL,
+ at, length);
+ return 0;
+}
+
+#define MAX_ENCODING_STR_LEN 8
+/* 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 (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRKEY) ==
+ STRING_STATE_CACHE) {
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_HDRKEY);
+ }
+
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_HDRVAL);
+
+ if (half->ref_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->ref_data->table, &key,
+ &http_hdr, 1) == 1) {
+ char encoding_str[MAX_ENCODING_STR_LEN + 1] = {0};
+ size_t str_len = http_hdr.val.str_len;
+ if (str_len > MAX_ENCODING_STR_LEN) {
+ str_len = MAX_ENCODING_STR_LEN;
+ }
+ memcpy(encoding_str, http_hdr.val.str, str_len);
+ half->ref_data->content_encoding = http_content_encoding_str2int(encoding_str);
+ }
+ }
+
+ http_decoder_get_host_feed_url(half);
+
+ 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);
+ assert(half->ref_data);
+
+ http_decoder_table_set_header_complete(half->ref_data->table);
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_HDR_END;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ if (half->parser.type == HTTP_RESPONSE) {
+ half->event = HTTP_EVENT_RES_HDR_END;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ 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->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data,
+ half->http_ev_ctx);
+ }
+ }
+ } else {
+ if (half->event == HTTP_EVENT_RES_HDR_END) {
+ half->event = HTTP_EVENT_RES_BODY_BEGIN;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data,
+ half->http_ev_ctx);
+ }
+ }
+ }
+
+ if (half->ref_data != NULL) {
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_BODY) ==
+ STRING_STATE_COMMIT) {
+ http_decoder_table_reset(half->ref_data->table, HTTP_ITEM_BODY);
+ }
+
+ http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_BODY,
+ at, length);
+ http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_BODY);
+ }
+
+ if (1 == half->decompress_switch &&
+ half->ref_data->content_encoding != HTTP_CONTENT_ENCODING_NONE) {
+ http_decoder_half_data_decompress(half->ref_data);
+ }
+
+ if (half->parser.type == HTTP_REQUEST) {
+ half->event = HTTP_EVENT_REQ_BODY_DATA;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ } else {
+ half->event = HTTP_EVENT_RES_BODY_DATA;
+ if (half->http_ev_cb) {
+ half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx);
+ }
+ }
+
+ return 0;
+}
+
+static void
+http_decoder_half_init(struct http_decoder_half *half,
+ http_event_cb *http_ev_cb, int type)
+{
+ if (NULL == half) {
+ return;
+ }
+
+ 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->http_ev_cb = http_ev_cb;
+
+ half->ref_data = NULL;
+}
+
+struct http_decoder_half *
+http_decoder_half_new(nmx_pool_t *mempool, http_event_cb *ev_cb, int http_type,
+ int decompress_switch)
+{
+ struct http_decoder_half *half = MEMPOOL_CALLOC(mempool, struct http_decoder_half, 1);
+ assert(half);
+
+ half->decompress_switch = decompress_switch;
+ half->http_ev_ctx = MEMPOOL_CALLOC(mempool, struct http_event_context, 1);
+ http_decoder_half_init(half, ev_cb, http_type);
+
+ return half;
+}
+
+void http_decoder_half_free(nmx_pool_t *mempool, struct http_decoder_half *half)
+{
+ if (NULL == half) {
+ return;
+ }
+
+ if (half->http_ev_ctx != NULL) {
+ MEMPOOL_FREE(mempool, half->http_ev_ctx);
+ half->http_ev_ctx = NULL;
+ }
+
+ MEMPOOL_FREE(mempool, half);
+}
+
+void http_decoder_half_reinit(struct http_decoder_half *half, int topic_id,
+ struct http_decoder_result_queue *queue,
+ nmx_pool_t *mempool, struct session *sess)
+{
+ assert(half != NULL);
+
+ if (half->ref_data != NULL) {
+ http_decoder_table_reinit(half->ref_data->table);
+ }
+
+ half->http_ev_ctx->topic_id = topic_id;
+ half->http_ev_ctx->ref_mempool = mempool;
+ half->http_ev_ctx->ref_session = sess;
+ half->http_ev_ctx->ref_queue = queue;
+}
+
+static void publish_message_for_parsed_header(struct http_decoder_half *half)
+{
+ if (0 == http_decoder_table_has_parsed_header(half->ref_data->table)) {
+ return;
+ }
+
+ // publish complete kv-header message
+ struct http_message *msg = NULL;
+ size_t queue_idx = 0;
+ struct http_decoder_result_queue *queue = half->http_ev_ctx->ref_queue;
+
+ if (half->parser.type == HTTP_REQUEST) {
+ queue_idx = http_decoder_result_queue_req_index(queue);
+
+ msg = http_message_new(HTTP_MESSAGE_REQ_HEADER, queue, queue_idx);
+
+ session_mq_publish_message(half->http_ev_ctx->ref_session,
+ half->http_ev_ctx->topic_id, msg);
+ } else {
+ // http response
+ queue_idx = http_decoder_result_queue_res_index(queue);
+
+ msg = http_message_new(HTTP_MESSAGE_RES_HEADER, queue, queue_idx);
+
+ session_mq_publish_message(half->http_ev_ctx->ref_session,
+ half->http_ev_ctx->topic_id, msg);
+ }
+}
+
+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);
+
+ int ret = 0;
+ uint8_t type = 0;
+ struct http_decoder_half_data *half_data = NULL;
+
+ switch (half->error) {
+ case HPE_OK:
+ break;
+ case HPE_PAUSED:
+ llhttp_resume(&half->parser);
+ break;
+ case HPE_PAUSED_UPGRADE:
+ llhttp_resume_after_upgrade(&half->parser);
+ ret = 0;
+ break;
+ default:
+ type = half->parser.type;
+ llhttp_init(&half->parser, type, &half->settings);
+ ret = -1;
+ break;
+ }
+
+ if (ret < 0) {
+ // fprintf(stdout,
+ // "llhttp_execute parse error: %s err_reason:%s\n",
+ // llhttp_errno_name(half->error), half->parser.reason);
+ return half->error;
+ }
+
+ if (half->ref_data != NULL) {
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_URI)
+ == STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_URI);
+ }
+
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_STATUS)
+ == STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_STATUS);
+ }
+
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_METHOD)
+ == STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_METHOD);
+ }
+
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_VERSION)
+ == STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_VERSION);
+ }
+
+ if (http_decoder_table_header_complete(half->ref_data->table)) {
+ http_decoder_table_reset_header_complete(half->ref_data->table);
+ } else {
+ publish_message_for_parsed_header(half);
+ }
+
+ enum string_state hdr_key_state =
+ http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRKEY);
+ enum string_state hdr_val_state =
+ http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRVAL);
+
+ /* Truncated in http header key
+ For example http header k-v => User-Agent: Chrome
+ case1:
+ packet1: User- hdr_key_state == STRING_STATE_REFER
+ packet2: Agent: Chrome
+
+ case2:
+ packet1: User-Agent: hdr_key_state == STRING_STATE_COMMIT
+ hdr_val_state == STRING_STATE_INIT
+ packet2: Chrome
+ */
+ if (hdr_key_state == STRING_STATE_REFER ||
+ (hdr_key_state == STRING_STATE_COMMIT && hdr_val_state == STRING_STATE_INIT)) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRKEY);
+ }
+
+ /* Truncated in http header value
+ For example http header k-v => User-Agent: Chrome
+ packet1: User-Agent: Ch hdr_key_state == STRING_STATE_COMMIT
+ hdr_val_state == STRING_STATE_REFER
+
+ packet2: rome
+ */
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRVAL)
+ == STRING_STATE_REFER) {
+ /* Header key should have been committed
+ If it's not cached, cache it for next packet to use
+ */
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRKEY);
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRVAL);
+ }
+
+ if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_BODY)
+ == STRING_STATE_REFER) {
+ http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_BODY);
+ }
+ }
+
+ return 0;
+}
+
+long long http_decoder_half_trans_count(struct http_decoder_half *half)
+{
+ if (NULL == half) {
+ return 0;
+ }
+
+ long long trans_cnt = half->trans_counter;
+ half->trans_counter = 0;
+
+ return trans_cnt;
+}
+
+struct http_decoder_half_data *
+http_decoder_half_data_new(nmx_pool_t *mempool)
+{
+ struct http_decoder_half_data *data =
+ MEMPOOL_CALLOC(mempool, struct http_decoder_half_data, 1);
+ assert(data);
+
+ data->table = http_decoder_table_new(mempool);
+ assert(data->table);
+
+ data->major_version = -1;
+ data->minor_version = -1;
+ data->status_code = -1;
+
+ data->content_encoding = HTTP_CONTENT_ENCODING_NONE;
+ data->ref_decompress_body = NULL;
+ data->decompress_body_len = 0;
+
+ return data;
+}
+
+void http_decoder_half_data_free(nmx_pool_t *mempool,
+ 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 != NULL) {
+ http_content_decompress_destroy(data->decompress);
+ data->decompress = NULL;
+ }
+
+ if(data->joint_url.str)
+ {
+ MEMPOOL_FREE(mempool, data->joint_url.str);
+ data->joint_url.str = NULL;
+ data->joint_url_complete = 0;
+ }
+
+ MEMPOOL_FREE(mempool, 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 *hdr_array,
+ size_t array_size)
+{
+ if (NULL == data || NULL == key ||
+ NULL == hdr_array || 0 == array_size) {
+ return -1;
+ }
+
+ return http_decoder_table_get_header(data->table, key, hdr_array, array_size);
+}
+
+int http_decoder_half_data_iter_header(struct http_decoder_half_data *data,
+ struct http_header *header)
+{
+ if (NULL == data || NULL == header) {
+ return -1;
+ }
+
+ return http_decoder_table_iter_header(data->table, header);
+}
+
+int http_decoder_half_data_has_parsed_header(struct http_decoder_half_data *data)
+{
+ if (NULL == data) {
+ return 0;
+ }
+
+ return http_decoder_table_has_parsed_header(data->table);
+}
+
+int http_decoder_half_data_get_raw_body(struct http_decoder_half_data *data,
+ struct hstring *body)
+{
+ if (NULL == data || NULL == body) {
+ return -1;
+ }
+
+ return http_decoder_table_get_body(data->table, body);
+}
+
+int http_decoder_half_data_get_decompress_body(struct http_decoder_half_data *data,
+ struct hstring *body)
+{
+ if (NULL == data || NULL == body) {
+ return -1;
+ }
+
+ if (HTTP_CONTENT_ENCODING_NONE == data->content_encoding) {
+ return http_decoder_table_get_body(data->table, body);
+ }
+
+
+ body->str = data->ref_decompress_body;
+ body->str_len = data->decompress_body_len;
+ return 0;
+}
+
+void http_decoder_half_data_dump(struct http_decoder_half *half)
+{
+ if (NULL == half || NULL == half->ref_data) {
+ return;
+ }
+
+ http_decoder_table_dump(half->ref_data->table);
+}
+
+static void using_session_addr_as_host(struct session *ref_session,
+ struct http_header *host_result, nmx_pool_t *mempool)
+{
+ const struct session_addr *ssaddr;
+ enum session_addr_type ssaddr_type;
+ ssaddr = session_get0_addr(ref_session, &ssaddr_type);
+ if (!ssaddr)
+ {
+ assert(0);
+ }
+
+ char ip_string_buf[INET6_ADDRSTRLEN];
+ if (SESSION_ADDR_TYPE_IPV4_TCP == ssaddr_type || SESSION_ADDR_TYPE_IPV4_UDP == ssaddr_type)
+ {
+ host_result->val.str = MEMPOOL_CALLOC(mempool, char, (INET_ADDRSTRLEN + 7) /* "ip:port" max length */);
+ inet_ntop(AF_INET, &ssaddr->ipv4.daddr, ip_string_buf, INET_ADDRSTRLEN);
+ sprintf(host_result->val.str, "%s:%u", ip_string_buf, ntohs(ssaddr->ipv4.dport));
+ host_result->val.str_len = strlen(host_result->val.str);
+ }
+ else if (SESSION_ADDR_TYPE_IPV6_TCP == ssaddr_type || SESSION_ADDR_TYPE_IPV6_UDP == ssaddr_type)
+ {
+ host_result->val.str = MEMPOOL_CALLOC(mempool, char, (INET6_ADDRSTRLEN + 7) /* "ip:port" max length */);
+ inet_ntop(AF_INET6, &ssaddr->ipv6.daddr, ip_string_buf, INET6_ADDRSTRLEN);
+ sprintf(host_result->val.str, "%s:%u", ip_string_buf, ntohs(ssaddr->ipv6.dport));
+ host_result->val.str_len = strlen(host_result->val.str);
+ }
+ else
+ {
+ assert(0);
+ }
+}
+
+void http_decoder_join_url(struct http_decoder_half_data *hfdata, nmx_pool_t *mempool, const struct http_header *host_hdr)
+{
+ //int url_cache_str_len = strlen("http://") + host_hdr->val.str_len + hfdata->joint_url.str_len;
+ int url_cache_str_len = host_hdr->val.str_len + hfdata->joint_url.str_len;
+ char *url_cache_str = MEMPOOL_CALLOC(mempool, char, url_cache_str_len);
+
+ char *ptr = url_cache_str;
+ //memcpy(ptr, "http://", strlen("http://"));
+ //ptr += strlen("http://");
+ memcpy(ptr, host_hdr->val.str, host_hdr->val.str_len);
+ ptr += host_hdr->val.str_len;
+ memcpy(ptr, hfdata->joint_url.str, hfdata->joint_url.str_len);
+
+ MEMPOOL_FREE(mempool, hfdata->joint_url.str); // free the cached uri buffer
+ hfdata->joint_url.str = url_cache_str;
+ hfdata->joint_url.str_len = url_cache_str_len;
+
+ hfdata->joint_url_complete = 1;
+}
+
+int http_decoder_join_url_finally(struct http_event_context *ev_ctx,
+ struct http_decoder_half_data *hfdata,
+ nmx_pool_t *mempool)
+{
+ struct http_header addr_as_host = {};
+
+ if (hfdata->joint_url_complete)
+ {
+ return 0;
+ }
+
+ using_session_addr_as_host(ev_ctx->ref_session, &addr_as_host, mempool);
+ http_decoder_join_url(hfdata, mempool, &addr_as_host);
+ MEMPOOL_FREE(mempool, addr_as_host.val.str); // free session addr to host buffer
+ return 1;
+}
+
+void http_decoder_get_host_feed_url(struct http_decoder_half *half)
+{
+ struct http_header host_result = {};
+ struct hstring host_key = {"Host", 4};
+ const char *host_refer_str = NULL;
+ int host_refer_len = 0;
+
+ if (half->ref_data->joint_url_complete)
+ {
+ return;
+ }
+
+ int host_header_cnt = http_decoder_half_data_get_header(half->ref_data, &host_key,
+ &host_result, 1);
+ if (host_header_cnt <= 0)
+ {
+ return;
+ }
+
+ http_decoder_join_url(half->ref_data, half->http_ev_ctx->ref_mempool, &host_result);
+}
+
+int http_half_data_get_url(struct http_decoder_half_data *res_data, struct hstring *url)
+{
+ if (0 == res_data->joint_url_complete)
+ {
+ return -1;
+ }
+
+ url->str = res_data->joint_url.str;
+ url->str_len = res_data->joint_url.str_len;
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/http_decoder_half.h b/src/http_decoder_half.h
new file mode 100644
index 0000000..0fcd0b6
--- /dev/null
+++ b/src/http_decoder_half.h
@@ -0,0 +1,119 @@
+/*
+**********************************************************************************************
+* File: http_decoder_half.h
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-01-10
+* 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 "stellar/session.h"
+#include "http_decoder.h"
+#include "http_content_decompress.h"
+#include "http_decoder_result_queue.h"
+
+// only one http event is fired at a time
+enum http_event {
+ HTTP_EVENT_REQ_INIT = 1 << 1,
+ HTTP_EVENT_REQ_LINE = 1 << 2,
+ HTTP_EVENT_REQ_HDR = 1 << 3,
+ HTTP_EVENT_REQ_HDR_END = 1 << 4,
+ HTTP_EVENT_REQ_BODY_BEGIN = 1 << 5,
+ HTTP_EVENT_REQ_BODY_DATA = 1 << 6,
+ HTTP_EVENT_REQ_BODY_END = 1 << 7,
+ HTTP_EVENT_REQ_END = 1 << 8,
+
+ HTTP_EVENT_RES_INIT = 1 << 9,
+ HTTP_EVENT_RES_LINE = 1 << 10,
+ HTTP_EVENT_RES_HDR = 1 << 11,
+ HTTP_EVENT_RES_HDR_END = 1 << 12,
+ HTTP_EVENT_RES_BODY_BEGIN = 1 << 13,
+ HTTP_EVENT_RES_BODY_DATA = 1 << 14,
+ HTTP_EVENT_RES_BODY_END = 1 << 15,
+ HTTP_EVENT_RES_END = 1 << 16,
+};
+
+struct http_event_context {
+ int topic_id;
+ nmx_pool_t *ref_mempool;
+ struct session *ref_session;
+ struct http_decoder_result_queue *ref_queue;
+};
+
+struct http_decoder_half;
+struct http_decoder_half_data;
+
+typedef void http_event_cb(enum http_event event, struct http_decoder_half_data **data,
+ struct http_event_context *ev_ctx);
+
+struct http_decoder_half *
+http_decoder_half_new(nmx_pool_t *mempool, http_event_cb *event_cb, int http_type,
+ int decompress_switch);
+
+void http_decoder_half_free(nmx_pool_t *mempool, struct http_decoder_half *half);
+
+void http_decoder_half_reinit(struct http_decoder_half *half, int topic_id,
+ struct http_decoder_result_queue *queue,
+ nmx_pool_t *mempool, struct session *sess);
+
+int http_decoder_half_parse(struct http_decoder_half *half, const char *data,
+ size_t data_len);
+
+long long http_decoder_half_trans_count(struct http_decoder_half *half);
+
+//http decoder half data API
+struct http_decoder_half_data *
+http_decoder_half_data_new(nmx_pool_t *mempool);
+
+void http_decoder_half_data_free(nmx_pool_t *mempool,
+ 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 *hdr_array,
+ size_t array_size);
+
+int http_decoder_half_data_iter_header(struct http_decoder_half_data *data,
+ struct http_header *header);
+
+int http_decoder_half_data_has_parsed_header(struct http_decoder_half_data *data);
+
+int http_decoder_half_data_get_raw_body(struct http_decoder_half_data *data,
+ struct hstring *body);
+
+int http_decoder_half_data_get_decompress_body(struct http_decoder_half_data *data,
+ struct hstring *body);
+
+void http_decoder_half_data_dump(struct http_decoder_half *half);
+
+void http_decoder_get_host_feed_url(struct http_decoder_half *half);
+void http_decoder_join_url(struct http_decoder_half_data *hfdata,
+ nmx_pool_t *mempool,
+ const struct http_header *host_hdr);
+int http_decoder_join_url_finally(struct http_event_context *ev_ctx,
+ struct http_decoder_half_data *hfdata,
+ nmx_pool_t *mempool);
+int http_half_data_get_url(struct http_decoder_half_data *res_data, struct hstring *url);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder_inc.h b/src/http_decoder_inc.h
new file mode 100644
index 0000000..2d6a5c0
--- /dev/null
+++ b/src/http_decoder_inc.h
@@ -0,0 +1,53 @@
+/*
+**********************************************************************************************
+* File: http_decoder_inc.h
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-01-10
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _HTTP_DECODER_INC_H_
+#define _HTTP_DECODER_INC_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "mempool/nmx_palloc.h"
+#include "stellar/utils.h"
+#include "http_decoder.h"
+#include "http_decoder_result_queue.h"
+
+
+#define MEMPOOL_CALLOC(pool, type, number) ((type *)nmx_pcalloc(pool, sizeof(type) * number))
+#define MEMPOOL_REALLOC(pool)
+#define MEMPOOL_FREE(pool, p) nmx_pfree(pool, p)
+
+#ifdef ENABLE_MEMPOOL
+
+#define HD_CALLOC(pool, type, number) MEMPOOL_CALLOC(pool, number, type)
+#define HD_FREE(pool, p) MEMPOOL_FREE(pool, p)
+
+#else
+
+#define HD_CALLOC(pool, type, number) CALLOC(type, number)
+#define HD_FREE(pool, p) FREE(p)
+
+#endif
+
+
+struct http_message;
+
+struct http_message *
+http_message_new(enum http_message_type type,
+ struct http_decoder_result_queue *queue,
+ int queue_index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder_result_queue.c b/src/http_decoder_result_queue.c
new file mode 100644
index 0000000..9a60d15
--- /dev/null
+++ b/src/http_decoder_result_queue.c
@@ -0,0 +1,172 @@
+/*
+**********************************************************************************************
+* File: http_decoder_result_queue.c
+* Description:
+* Authors: Liuwentan <[email protected]>
+* Date: 2024-01-15
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <assert.h>
+
+#include "stellar/utils.h"
+#include "http_decoder_half.h"
+#include "http_decoder_inc.h"
+#include "http_decoder_result_queue.h"
+
+struct http_decoder_result_queue *
+http_decoder_result_queue_new(nmx_pool_t *mempool, size_t queue_size)
+{
+ struct http_decoder_result_queue *queue =
+ MEMPOOL_CALLOC(mempool, struct http_decoder_result_queue, 1);
+ assert(queue);
+
+ queue->req_index = 0;
+ queue->res_index = 0;
+ queue->queue_size = queue_size;
+
+ queue->array = MEMPOOL_CALLOC(mempool, struct http_decoder_result,
+ queue->queue_size);
+ assert(queue->array);
+
+ return queue;
+}
+
+void http_decoder_result_queue_free(nmx_pool_t *mempool,
+ 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].req_data != NULL) {
+ http_decoder_half_data_free(mempool, queue->array[i].req_data);
+ queue->array[i].req_data = NULL;
+ }
+
+ if (queue->array[i].res_data != NULL) {
+ http_decoder_half_data_free(mempool, queue->array[i].res_data);
+ queue->array[i].res_data = NULL;
+ }
+ }
+
+ MEMPOOL_FREE(mempool, queue->array);
+ }
+
+ MEMPOOL_FREE(mempool, queue);
+}
+
+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;
+}
+
+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;
+}
+
+size_t http_decoder_result_queue_req_index(struct http_decoder_result_queue *queue)
+{
+ assert(queue);
+
+ return queue->req_index;
+}
+
+size_t http_decoder_result_queue_res_index(struct http_decoder_result_queue *queue)
+{
+ assert(queue);
+
+ return queue->res_index;
+}
+
+int http_decoder_result_queue_push_req(struct http_decoder_result_queue *queue,
+ struct http_decoder_half_data *req_data)
+{
+ if (NULL == queue || NULL == req_data) {
+ return -1;
+ }
+
+ assert(queue->array[queue->req_index].req_data == NULL);
+ if (queue->array[queue->req_index].req_data != NULL) {
+ return -1;
+ }
+
+ queue->array[queue->req_index].req_data = req_data;
+ return 0;
+}
+
+int http_decoder_result_queue_push_res(struct http_decoder_result_queue *queue,
+ struct http_decoder_half_data *res_data)
+{
+ if (NULL == queue || NULL == res_data) {
+ return -1;
+ }
+
+ assert(queue->array[queue->res_index].res_data == NULL);
+ if (queue->array[queue->res_index].res_data != NULL) {
+ return -1;
+ }
+
+ queue->array[queue->res_index].res_data = res_data;
+ return 0;
+}
+
+struct http_decoder_half_data *
+http_decoder_result_queue_pop_req(struct http_decoder_result_queue *queue)
+{
+ if (NULL == queue) {
+ return NULL;
+ }
+
+ struct http_decoder_half_data *req_data =
+ queue->array[queue->req_index].req_data;
+ queue->array[queue->req_index].req_data = NULL;
+
+ return req_data;
+}
+
+struct http_decoder_half_data *
+http_decoder_result_queue_pop_res(struct http_decoder_result_queue *queue)
+{
+ if (NULL == queue) {
+ return NULL;
+ }
+
+ struct http_decoder_half_data *res_data =
+ queue->array[queue->res_index].res_data;
+ queue->array[queue->res_index].res_data = NULL;
+
+ return res_data;
+}
+
+struct http_decoder_half_data *
+http_decoder_result_queue_peek_req(struct http_decoder_result_queue *queue)
+{
+ if (NULL == queue) {
+ return NULL;
+ }
+
+ assert(queue->req_index < queue->queue_size);
+ return queue->array[queue->req_index].req_data;
+}
+
+struct http_decoder_half_data *
+http_decoder_result_queue_peek_res(struct http_decoder_result_queue *queue)
+{
+ if (NULL == queue) {
+ return NULL;
+ }
+
+ assert(queue->res_index < queue->queue_size);
+ return queue->array[queue->res_index].res_data;
+} \ No newline at end of file
diff --git a/src/http_decoder_result_queue.h b/src/http_decoder_result_queue.h
new file mode 100644
index 0000000..cd2163a
--- /dev/null
+++ b/src/http_decoder_result_queue.h
@@ -0,0 +1,73 @@
+/*
+**********************************************************************************************
+* File: http_decoder_result_queue.h
+* Description:
+* Authors: Liuwentan <[email protected]>
+* Date: 2024-01-15
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+
+#ifndef _HTTP_DECODER_RESULT_QUEUE_H_
+#define _HTTP_DECODER_RESULT_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+#include "mempool/nmx_palloc.h"
+#include "http_decoder_half.h"
+
+struct http_decoder_result {
+ 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 queue_size;
+ struct http_decoder_result *array;
+};
+
+struct http_decoder_result_queue *
+http_decoder_result_queue_new(nmx_pool_t *mempool, size_t queue_size);
+
+void http_decoder_result_queue_free(nmx_pool_t *mempool,
+ struct http_decoder_result_queue *queue);
+
+void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue);
+
+void http_decoder_result_queue_inc_res_index(struct http_decoder_result_queue *queue);
+
+size_t http_decoder_result_queue_req_index(struct http_decoder_result_queue *queue);
+
+size_t http_decoder_result_queue_res_index(struct http_decoder_result_queue *queue);
+
+struct http_decoder_half_data *
+http_decoder_result_queue_pop_req(struct http_decoder_result_queue *queue);
+
+struct http_decoder_half_data *
+http_decoder_result_queue_pop_res(struct http_decoder_result_queue *queue);
+
+int http_decoder_result_queue_push_req(struct http_decoder_result_queue *queue,
+ struct http_decoder_half_data *req_data);
+
+int http_decoder_result_queue_push_res(struct http_decoder_result_queue *queue,
+ struct http_decoder_half_data *res_data);
+
+struct http_decoder_half_data *
+http_decoder_result_queue_peek_req(struct http_decoder_result_queue *queue);
+
+struct http_decoder_half_data *
+http_decoder_result_queue_peek_res(struct http_decoder_result_queue *queue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder_string.c b/src/http_decoder_string.c
new file mode 100644
index 0000000..5414c5b
--- /dev/null
+++ b/src/http_decoder_string.c
@@ -0,0 +1,277 @@
+/*
+**********************************************************************************************
+* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "stellar/utils.h"
+#include "http_decoder_utils.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;
+}
+
+static void string_refer2cache(struct http_decoder_string *rstr)
+{
+ if (0 == rstr->refer.str_len) {
+ return;
+ }
+
+ if (rstr->cache.str_len >= rstr->max_cache_size) {
+ return;
+ }
+
+ size_t length = rstr->cache.str_len + rstr->refer.str_len;
+ if (length > rstr->max_cache_size) {
+ length = rstr->max_cache_size;
+ }
+
+ if (NULL == rstr->cache.str) {
+ rstr->cache.str = CALLOC(char, length + 1);
+ memcpy(rstr->cache.str, rstr->refer.str, length);
+ } else {
+ rstr->cache.str = REALLOC(char, rstr->cache.str, length + 1);
+ memcpy(rstr->cache.str + rstr->cache.str_len, rstr->refer.str,
+ (length - rstr->cache.str_len));
+ }
+
+ rstr->cache.str_len = length;
+
+ rstr->refer.str = NULL;
+ rstr->refer.str_len = 0;
+}
+
+static void string_commit2cache(struct http_decoder_string *rstr)
+{
+ if (rstr->cache.str_len == rstr->commit.str_len &&
+ rstr->cache.str == rstr->commit.str) {
+
+ rstr->commit.str = NULL;
+ rstr->commit.str_len = 0;
+ return;
+ }
+
+ //Only http header key need to backward to cache
+ size_t length = 0;
+ if (rstr->commit.str_len > rstr->max_cache_size) {
+ length = rstr->max_cache_size;
+ } else {
+ length = rstr->commit.str_len;
+ }
+
+ if (length > 0) {
+ if (NULL == rstr->cache.str) {
+ rstr->cache.str = CALLOC(char, length + 1);
+ } else {
+ abort();
+ }
+ memcpy(rstr->cache.str, rstr->commit.str, length);
+ rstr->cache.str_len = length;
+
+ rstr->commit.str = NULL;
+ rstr->commit.str_len = 0;
+ }
+}
+
+void http_decoder_string_cache(struct http_decoder_string *rstr)
+{
+ if (NULL == rstr) {
+ return;
+ }
+
+ switch (rstr->state) {
+ case STRING_STATE_REFER:
+ string_refer2cache(rstr);
+ break;
+ case STRING_STATE_CACHE:
+ break;
+ case STRING_STATE_COMMIT:
+ //commit backward to cache
+ string_commit2cache(rstr);
+ 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:
+ FREE(rstr->cache.str);
+ memset(rstr, 0, sizeof(struct http_decoder_string));
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ rstr->state = STRING_STATE_INIT;
+}
+
+
+void http_decoder_string_init(struct http_decoder_string *rstr,
+ size_t max_cache_size)
+{
+ rstr->max_cache_size = max_cache_size;
+}
+
+void http_decoder_string_reinit(struct http_decoder_string *rstr)
+{
+ if (rstr->state == STRING_STATE_CACHE) {
+ return;
+ }
+
+ if (rstr->state == STRING_STATE_COMMIT &&
+ rstr->cache.str == rstr->commit.str &&
+ rstr->cache.str_len == rstr->commit.str_len) {
+ return;
+ }
+
+ if (rstr->cache.str != NULL) {
+ FREE(rstr->cache.str);
+ rstr->cache.str_len = 0;
+ }
+
+ rstr->refer.str = NULL;
+ rstr->refer.str_len = 0;
+
+ rstr->commit.str = NULL;
+ rstr->commit.str_len = 0;
+
+ rstr->state = STRING_STATE_INIT;
+}
+
+enum string_state http_decoder_string_state(struct http_decoder_string *rstr)
+{
+ return rstr->state;
+}
+
+int http_decoder_string_get(struct http_decoder_string *rstr, struct hstring *out)
+{
+ if (NULL == rstr || NULL == out) {
+ return -1;
+ }
+
+ 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;
+ }
+
+ return 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_string.h b/src/http_decoder_string.h
new file mode 100644
index 0000000..f9d81dd
--- /dev/null
+++ b/src/http_decoder_string.h
@@ -0,0 +1,95 @@
+/*
+**********************************************************************************************
+* 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;
+ size_t max_cache_size;
+};
+
+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);
+
+void http_decoder_string_init(struct http_decoder_string *rstr,
+ size_t max_cache_size);
+
+void http_decoder_string_reinit(struct http_decoder_string *rstr);
+
+enum string_state http_decoder_string_state(struct http_decoder_string *rstr);
+
+int 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_table.c b/src/http_decoder_table.c
new file mode 100644
index 0000000..ede7989
--- /dev/null
+++ b/src/http_decoder_table.c
@@ -0,0 +1,556 @@
+/*
+**********************************************************************************************
+* 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 INIT_HEADER_CNT 16
+#define MAX_URI_CACHE_SIZE 2048
+#define MAX_STATUS_CACHE_SIZE 32
+#define MAX_METHOD_CACHE_SIZE 8
+#define MAX_VERSION_CACHE_SIZE 4
+#define MAX_HEADER_KEY_CACHE_SIZE 4096
+#define MAX_HEADER_VALUE_CACHE_SIZE 4096
+
+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;
+
+ nmx_pool_t *ref_mempool;
+ int header_complete; // flag for all headers parsed completely
+ size_t header_cnt;
+ size_t header_index;
+ size_t header_iter;
+ struct http_decoder_header *headers;
+};
+
+static void http_decoder_table_init(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ http_decoder_string_init(&table->uri, MAX_URI_CACHE_SIZE);
+ http_decoder_string_init(&table->status, MAX_STATUS_CACHE_SIZE);
+ http_decoder_string_init(&table->method, MAX_METHOD_CACHE_SIZE);
+ http_decoder_string_init(&table->version, MAX_METHOD_CACHE_SIZE);
+
+ for (size_t i = 0; i < table->header_cnt; i++) {
+ header = &table->headers[i];
+ http_decoder_string_init(&header->key, MAX_HEADER_KEY_CACHE_SIZE);
+ http_decoder_string_init(&header->val, MAX_HEADER_VALUE_CACHE_SIZE);
+ }
+
+ http_decoder_string_init(&table->body, 0);
+}
+
+struct http_decoder_table *http_decoder_table_new(nmx_pool_t *mempool)
+{
+ struct http_decoder_table *table =
+ MEMPOOL_CALLOC(mempool, struct http_decoder_table, 1);
+ assert(table);
+
+ table->ref_mempool = mempool;
+ table->header_cnt = INIT_HEADER_CNT;
+ table->headers = MEMPOOL_CALLOC(mempool, struct http_decoder_header,
+ table->header_cnt);
+
+ http_decoder_table_init(table);
+
+ 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) {
+ for (size_t i = 0; i < table->header_cnt; i++) {
+ if (table->headers[i].key.cache.str != NULL) {
+ FREE(table->headers[i].key.cache.str);
+ }
+
+ if (table->headers[i].val.cache.str != NULL) {
+ FREE(table->headers[i].val.cache.str);
+ }
+ }
+
+ MEMPOOL_FREE(table->ref_mempool, table->headers);
+ table->headers = NULL;
+ }
+
+ MEMPOOL_FREE(table->ref_mempool, 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:
+ assert(table->header_index < table->header_cnt);
+ header = &table->headers[table->header_index];
+ state = http_decoder_string_state(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ assert(table->header_index < table->header_cnt);
+ 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:
+ assert(table->header_index < table->header_cnt);
+ header = &table->headers[table->header_index];
+ http_decoder_string_refer(&header->key, at, len);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ assert(table->header_index < table->header_cnt);
+ 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:
+ assert(table->header_index < table->header_cnt);
+ header = &table->headers[table->header_index];
+ http_decoder_string_cache(&header->key);
+ break;
+ case HTTP_ITEM_HDRVAL:
+ assert(table->header_index < table->header_cnt);
+ 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;
+ }
+
+ size_t i = 0;
+ 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:
+ assert(table->header_index < table->header_cnt);
+ 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
+ if ((table->header_index + 1) >= table->header_cnt) {
+ struct http_decoder_header *old_headers = table->headers;
+ table->headers =
+ MEMPOOL_CALLOC(table->ref_mempool, struct http_decoder_header,
+ table->header_cnt * 2);
+ table->header_cnt *= 2;
+
+ for (i = 0; i <= table->header_index; i++) {
+ table->headers[i] = old_headers[i];
+ }
+
+ MEMPOOL_FREE(table->ref_mempool, old_headers);
+
+ for (i = table->header_index + 1; i < table->header_cnt; i++) {
+ header = &table->headers[i];
+ memset(header, 0, sizeof(struct http_decoder_header));
+ http_decoder_string_init(&header->key, MAX_HEADER_KEY_CACHE_SIZE);
+ http_decoder_string_init(&header->val, MAX_HEADER_VALUE_CACHE_SIZE);
+ }
+ }
+ 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_reinit(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ struct http_decoder_header *header = NULL;
+ assert(table);
+
+ http_decoder_string_reinit(&table->uri);
+ http_decoder_string_reinit(&table->status);
+ http_decoder_string_reinit(&table->method);
+ http_decoder_string_reinit(&table->version);
+ for (size_t i = 0; i < table->header_iter; i++) {
+ header = &table->headers[i];
+ http_decoder_string_reinit(&header->key);
+ http_decoder_string_reinit(&header->val);
+ }
+
+ http_decoder_string_reinit(&table->body);
+}
+
+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_cnt; i++) {
+ struct http_decoder_header *header = &table->headers[i];
+ if (NULL == header) {
+ continue;
+ }
+
+ http_decoder_string_dump(&header->key, "key");
+ http_decoder_string_dump(&header->val, "val");
+ }
+}
+
+int http_decoder_table_get_uri(struct http_decoder_table *table, struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return -1;
+ }
+
+ return http_decoder_string_get(&table->uri, out);
+}
+
+int http_decoder_table_get_method(struct http_decoder_table *table, struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return -1;
+ }
+
+ return http_decoder_string_get(&table->method, out);
+}
+
+int http_decoder_table_get_status(struct http_decoder_table *table, struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return -1;
+ }
+
+ return http_decoder_string_get(&table->status, out);
+}
+
+int http_decoder_table_get_version(struct http_decoder_table *table, struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return -1;
+ }
+
+ return http_decoder_string_get(&table->version, out);
+}
+
+int http_decoder_table_get_body(struct http_decoder_table *table, struct hstring *out)
+{
+ if (NULL == table || NULL == out) {
+ return -1;
+ }
+
+ return http_decoder_string_get(&table->body, out);
+}
+
+int http_decoder_table_get_header(struct http_decoder_table *table, struct hstring *key,
+ struct http_header *hdr_array, size_t array_size)
+{
+ if (NULL == table || NULL == key->str || 0 == key->str_len) {
+ return 0;
+ }
+
+ int header_cnt = 0;
+ for (size_t i = 0; i < table->header_cnt && header_cnt < array_size; i++) {
+ struct http_decoder_header *tmp_header = &table->headers[i];
+ if (tmp_header->key.commit.str_len != key->str_len) {
+ continue;
+ }
+
+ if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) {
+ struct hstring tmp_key;
+ http_decoder_string_get(&tmp_header->key, &tmp_key);
+
+ if (tmp_key.str_len == key->str_len &&
+ (0 == strncasecmp(tmp_key.str, key->str, key->str_len))) {
+ http_decoder_string_get(&tmp_header->key, &hdr_array[header_cnt].key);
+ http_decoder_string_get(&tmp_header->val, &hdr_array[header_cnt].val);
+ header_cnt++;
+ }
+ }
+ }
+
+ return header_cnt;
+}
+
+int http_decoder_table_iter_header(struct http_decoder_table *table,
+ struct http_header *hdr)
+{
+ if (NULL == table || NULL == hdr) {
+ return -1;
+ }
+
+ if (table->header_iter >= table->header_cnt) {
+ return 0;
+ }
+
+ struct http_decoder_header *tmp_header = &table->headers[table->header_iter];
+ if (tmp_header != NULL) {
+ if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) {
+
+ http_decoder_string_get(&tmp_header->key, &hdr->key);
+ http_decoder_string_get(&tmp_header->val, &hdr->val);
+ table->header_iter++;
+
+ return 1;
+ }
+ }
+
+ hdr->key.str = NULL;
+ hdr->key.str_len = 0;
+
+ hdr->val.str = NULL;
+ hdr->val.str_len = 0;
+
+ return 0;
+}
+
+int http_decoder_table_has_parsed_header(struct http_decoder_table *table)
+{
+ if (NULL == table || (table->header_iter == table->header_index)) {
+ return 0;
+ }
+
+ struct http_decoder_header *tmp_header = &table->headers[table->header_iter];
+ if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT &&
+ http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int http_decoder_table_header_complete(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return -1;
+ }
+
+ return table->header_complete;
+}
+
+void http_decoder_table_set_header_complete(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ table->header_complete = 1;
+}
+
+void http_decoder_table_reset_header_complete(struct http_decoder_table *table)
+{
+ if (NULL == table) {
+ return;
+ }
+
+ table->header_complete = 0;
+} \ No newline at end of file
diff --git a/src/http_decoder_table.h b/src/http_decoder_table.h
new file mode 100644
index 0000000..fe443bb
--- /dev/null
+++ b/src/http_decoder_table.h
@@ -0,0 +1,99 @@
+/*
+**********************************************************************************************
+* 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_inc.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(nmx_pool_t *mempool);
+
+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_reinit(struct http_decoder_table *table);
+
+void http_decoder_table_dump(struct http_decoder_table *table);
+
+int http_decoder_table_get_uri(struct http_decoder_table *table, struct hstring *out);
+
+int http_decoder_table_get_method(struct http_decoder_table *table, struct hstring *out);
+
+int http_decoder_table_get_status(struct http_decoder_table *table, struct hstring *out);
+
+int http_decoder_table_get_version(struct http_decoder_table *table, struct hstring *out);
+
+int http_decoder_table_get_body(struct http_decoder_table *table, struct hstring *out);
+
+int http_decoder_table_get_header(struct http_decoder_table *table,
+ struct hstring *key,
+ struct http_header *hdr_array,
+ size_t array_size);
+
+int http_decoder_table_iter_header(struct http_decoder_table *table,
+ struct http_header *hdr);
+
+/**
+ * @brief Is there a parsed header
+ *
+ * @retval yes(1) no(0)
+*/
+int http_decoder_table_has_parsed_header(struct http_decoder_table *table);
+
+/**
+ * @brief If headers have been parsed completely
+ *
+ * @retval yes(1) no(0)
+ */
+int http_decoder_table_header_complete(struct http_decoder_table *table);
+
+/**
+ * @brief set flag for headers parsed completely
+*/
+void http_decoder_table_set_header_complete(struct http_decoder_table *table);
+
+void http_decoder_table_reset_header_complete(struct http_decoder_table *table);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/http_decoder_utils.c b/src/http_decoder_utils.c
new file mode 100644
index 0000000..a5dfbe1
--- /dev/null
+++ b/src/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_utils.h b/src/http_decoder_utils.h
new file mode 100644
index 0000000..9c031a3
--- /dev/null
+++ b/src/http_decoder_utils.h
@@ -0,0 +1,66 @@
+/*
+**********************************************************************************************
+* 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/version.map b/src/version.map
new file mode 100644
index 0000000..d26b83a
--- /dev/null
+++ b/src/version.map
@@ -0,0 +1,9 @@
+VERS_3.0{
+global:
+ extern "C" {
+ http_message_*;
+ http_decoder_init;
+ http_decoder_entry;
+ };
+local: *;
+}; \ No newline at end of file