diff options
| author | lijia <[email protected]> | 2024-04-08 09:48:13 +0800 |
|---|---|---|
| committer | lijia <[email protected]> | 2024-04-08 09:48:13 +0800 |
| commit | 215e383be1f47cd18c235855d0cee0485f6cb423 (patch) | |
| tree | 34668fd59622c37826c3a786ba0e196a7d65147b /src | |
| parent | ea795e9c6940281bf8557bfd79f13f319f947c58 (diff) | |
Separate from stellar-on-sapp project.
Diffstat (limited to 'src')
| -rw-r--r-- | src/.gitkeep | 0 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 18 | ||||
| -rw-r--r-- | src/http_content_decompress.c | 268 | ||||
| -rw-r--r-- | src/http_content_decompress.h | 52 | ||||
| -rw-r--r-- | src/http_decoder.c | 858 | ||||
| -rw-r--r-- | src/http_decoder_half.c | 1034 | ||||
| -rw-r--r-- | src/http_decoder_half.h | 119 | ||||
| -rw-r--r-- | src/http_decoder_inc.h | 53 | ||||
| -rw-r--r-- | src/http_decoder_result_queue.c | 172 | ||||
| -rw-r--r-- | src/http_decoder_result_queue.h | 73 | ||||
| -rw-r--r-- | src/http_decoder_string.c | 277 | ||||
| -rw-r--r-- | src/http_decoder_string.h | 95 | ||||
| -rw-r--r-- | src/http_decoder_table.c | 556 | ||||
| -rw-r--r-- | src/http_decoder_table.h | 99 | ||||
| -rw-r--r-- | src/http_decoder_utils.c | 25 | ||||
| -rw-r--r-- | src/http_decoder_utils.h | 66 | ||||
| -rw-r--r-- | src/version.map | 9 |
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 |
