feature: http decoder

* support parser uncompress request/response
    * define public API interface
This commit is contained in:
luwenpeng
2022-08-17 18:08:33 +08:00
parent b9d93e042b
commit 9cfa120ae7
19 changed files with 2958 additions and 13 deletions

View File

@@ -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";

View File

@@ -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

View File

@@ -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_include_directories(http PUBLIC ${CMAKE_SOURCE_DIR})
target_link_libraries(http
llhttp-static
)
target_include_directories(http PUBLIC ${CMAKE_SOURCE_DIR})
add_subdirectory(test)

View File

@@ -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)
{
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}