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