#include #include #include #include "zlib.h" #include "md5/md5.h" #include "stellar/http.h" #include "http_decoder.h" #include "http_decoder_utils.h" #include "http_decoder_half.h" #include "http_decoder_decompress.h" #ifdef __cplusplus extern "C" { #endif #include "http.h" #include "llhttp.h" #include "brotli/decode.h" #include "brotli/encode.h" #ifdef __cplusplus } #endif #define ZIP_UNZIP_TEST_DATA_LEN (1024 * 1024) void httpd_url_decode(const char *string, size_t length, char *ostring, size_t *olen); TEST(http_url_decoder, onebyte_ascii) { char decoded_url_buf[2048] = {}; size_t decode_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "h"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decode_url_buf_len); EXPECT_EQ(decoded_url_len, strlen("h")); EXPECT_STREQ("h", decoded_url_buf); } TEST(http_url_decoder, onebyte_hex) { char decoded_url_buf[2048] = {}; size_t decode_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "%FF"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decode_url_buf_len); EXPECT_EQ(decoded_url_len, 1); EXPECT_EQ((unsigned char)decoded_url_buf[0], 0xFF); } TEST(http_url_decoder, none_encode) { char decoded_url_buf[2048] = {}; size_t decode_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "https://docs.geedge.net/#all-updates"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decode_url_buf_len); EXPECT_EQ(decoded_url_len, strlen("https://docs.geedge.net/#all-updates")); EXPECT_STREQ("https://docs.geedge.net/#all-updates", decoded_url_buf); } TEST(http_url_decoder, simple) { char decoded_url_buf[2048] = {}; size_t decoded_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "http://a.b.cn/%A1%B2%C3%D4"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decoded_url_buf_len); const unsigned char expect_result[] = {0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x61, 0x2E, 0x62, 0x2E, 0x63, 0x6E, 0x2F, 0xA1, 0xB2, 0xC3, 0xD4, 0x00}; EXPECT_EQ(decoded_url_len, sizeof(expect_result) - 1); EXPECT_EQ(0, memcmp(expect_result, decoded_url_buf, decoded_url_len)); } TEST(http_url_decoder, chinese1) { char decoded_url_buf[2048] = {}; size_t decoded_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "http://www.baidu.com/%E6%B5%8B%E8%AF%95%E4%B8%AD%E6%96%87%E8%A7%A3%E7%A0%81"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decoded_url_buf_len); EXPECT_STREQ("http://www.baidu.com/\xE6\xB5\x8B\xE8\xAF\x95\xE4\xB8\xAD\xE6\x96\x87\xE8\xA7\xA3\xE7\xA0\x81", decoded_url_buf); EXPECT_EQ(decoded_url_len, strlen("http://www.baidu.com/\xE6\xB5\x8B\xE8\xAF\x95\xE4\xB8\xAD\xE6\x96\x87\xE8\xA7\xA3\xE7\xA0\x81")); } TEST(http_url_decoder, chinese2) { char decoded_url_buf[2048]; size_t decoded_url_buf_len = sizeof(decoded_url_buf); const char *encode_url = "http%3A%2F%2Fwww.baidu.com%2F%E7%BC%96%E8%A7%A3%E7%A0%81%E6%B5%8B%E8%AF%95%E5%93%88%E5%93%88"; size_t decoded_url_len = http_url_decode(encode_url, strlen(encode_url), decoded_url_buf, decoded_url_buf_len); EXPECT_EQ(0, memcmp("http://www.baidu.com/\xE7\xBC\x96\xE8\xA7\xA3\xE7\xA0\x81\xE6\xB5\x8B\xE8\xAF\x95\xE5\x93\x88\xE5\x93\x88", decoded_url_buf, decoded_url_len)); EXPECT_EQ(decoded_url_len, strlen("http://www.baidu.com/\xE7\xBC\x96\xE8\xA7\xA3\xE7\xA0\x81\xE6\xB5\x8B\xE8\xAF\x95\xE5\x93\x88\xE5\x93\x88")); } TEST(http, buffer) { struct http_buffer *hbuf = http_buffer_new(); http_buffer_add(hbuf, "hello", 5); char *data; size_t len; http_buffer_read(hbuf, &data, &len); EXPECT_EQ(len, 5); EXPECT_EQ(0, memcmp(data, "hello", 5)); http_buffer_add(hbuf, ",", 1); http_buffer_add(hbuf, "world", 5); http_buffer_read(hbuf, &data, &len); EXPECT_EQ(len, 11); EXPECT_EQ(0, memcmp(data, "hello,world", 11)); http_buffer_free(hbuf); } TEST(http, strtoll) { ASSERT_EQ(0, http_strtoll(NULL, 0)); ASSERT_EQ(0, http_strtoll("", 1)); ASSERT_EQ(0, http_strtoll("abcd", 4)); ASSERT_EQ(123, http_strtoll("123", 3)); ASSERT_EQ(123, http_strtoll(" 123", 4)); ASSERT_EQ(123, http_strtoll(" 123\r\n", 6)); ASSERT_EQ(123456789, http_strtoll("123456789", 9)); } TEST(http, line_header_is_completed) { ASSERT_EQ(0, http_line_header_completed(NULL, 0)); ASSERT_EQ(0, http_line_header_completed("GET ", 4)); ASSERT_EQ(0, http_line_header_completed("GET / HTTP/1.1\r\n", strlen("GET / HTTP/1.1\r\n"))); ASSERT_EQ(strlen("GET / HTTP/1.1\r\n\r\n"), http_line_header_completed("GET / HTTP/1.1\r\n\r\n", strlen("GET / HTTP/1.1\r\n\r\n"))); } TEST(http, identify) { ASSERT_EQ(-1, http_protocol_identify(NULL, 0)); ASSERT_EQ(-1, http_protocol_identify("123", 3)); ASSERT_EQ(1, http_protocol_identify("GET / HTTP/1.1\r\n", strlen("GET / HTTP/1.1\r\n"))); ASSERT_EQ(1, http_protocol_identify("POST / HTTP/1.1\r\n", strlen("POST / HTTP/1.1\r\n"))); ASSERT_EQ(1, http_protocol_identify("HTTP/1.1 200 OK\r\n", strlen("HTTP/1.1 200 OK\r\n"))); } static int http_compress_use_deflate(unsigned char *indata, size_t indata_len, unsigned char *zip_data, size_t *zip_data_len) { #define ZIP_CHUNK 4096 #define UZIP_CHUNK 16384 unsigned have; z_stream strm = {}; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY); /* no bad return value */ if (ret != Z_OK) { return -1; } strm.next_in = indata; strm.avail_in = indata_len; size_t read_raw_data_len = 0; size_t ziped_len = 0; int flush_mode; do { /* run deflate() on input until output buffer not full, finish compression if all of source has been read in */ strm.avail_in = MIN(indata_len - read_raw_data_len, ZIP_CHUNK); strm.next_in = indata + read_raw_data_len; flush_mode = (read_raw_data_len + ZIP_CHUNK < indata_len) ? Z_NO_FLUSH : Z_FINISH; do { unsigned char local_chunk_buf[UZIP_CHUNK]; strm.avail_out = UZIP_CHUNK; strm.next_out = local_chunk_buf; ret = deflate(&strm, flush_mode); assert(ret != Z_STREAM_ERROR); have = UZIP_CHUNK - strm.avail_out; if (have > 0) { memcpy(zip_data + ziped_len, local_chunk_buf, have); ziped_len += have; } } while (strm.avail_out == 0); read_raw_data_len += ZIP_CHUNK; } while (read_raw_data_len < indata_len); *zip_data_len = ziped_len; assert(strm.avail_in == 0); /* all input will be used */ /* done when last data in file processed */ /* clean up and return */ (void)deflateEnd(&strm); return Z_OK; } static int http_compress_use_brotli(unsigned char *input_data, size_t input_length, unsigned char *compressed_data, size_t *compressed_data_len) { // Allocate memory for the compressed data size_t compressed_length = BrotliEncoderMaxCompressedSize(input_length); // Create a Brotli encoder state BrotliEncoderState *encoder = BrotliEncoderCreateInstance(NULL, NULL, NULL); if (!encoder) { fprintf(stderr, "Failed to create Brotli encoder.\n"); return 1; } // Set up the Brotli encoder BrotliEncoderSetParameter(encoder, BROTLI_PARAM_QUALITY, 11); // Compress the data size_t available_in = input_length; const uint8_t *next_in = (const uint8_t *)input_data; size_t available_out = compressed_length; uint8_t *next_out = compressed_data; BrotliEncoderOperation op = BROTLI_OPERATION_FINISH; if (!BrotliEncoderCompressStream(encoder, op, &available_in, &next_in, &available_out, &next_out, NULL)) { fprintf(stderr, "Brotli compression failed.\n"); BrotliEncoderDestroyInstance(encoder); *compressed_data_len = 0; return -1; } // Calculate actual compressed size size_t actual_compressed_length = compressed_length - available_out; // printf("Original size: %zu\n", input_length); // printf("Compressed size: %zu\n", actual_compressed_length); // Clean up BrotliEncoderDestroyInstance(encoder); *compressed_data_len = actual_compressed_length; return 0; } /* High Compression Ratio */ static unsigned char *http_build_ascii_text(size_t data_len) { unsigned char *raw_data = (unsigned char *)malloc(data_len); for (int i = 0; i < (int)data_len; i++) { raw_data[i] = 'A' + i % 26; } return raw_data; } /* Low Compression Ratio */ static unsigned char *http_build_random_content(size_t data_len) { srand(12345678); unsigned char *raw_data = (unsigned char *)malloc(data_len); for (int i = 0; i < (int)data_len; i++) { raw_data[i] = (unsigned char)(rand() ^ i); } return raw_data; } static int http_zip_unzip_test(unsigned char *raw_data, size_t raw_data_len, enum http_content_encoding encoding, int input_zip_block_size) { unsigned char raw_data_md5sum[16], zip_unzip_data_md5sum[16]; MD5_CTX raw_data_md5_ctx = {}, zip_unzip_data_md5_ctx = {}; MD5Init(&raw_data_md5_ctx); MD5Init(&zip_unzip_data_md5_ctx); MD5Update(&raw_data_md5_ctx, raw_data, raw_data_len); MD5Final(raw_data_md5sum, &raw_data_md5_ctx); unsigned char *zip_data = (unsigned char *)malloc(raw_data_len * 2); size_t zip_data_len = raw_data_len * 2; if (HTTP_CONTENT_ENCODING_DEFLATE == encoding) { http_compress_use_deflate(raw_data, raw_data_len, zip_data, &zip_data_len); } else { http_compress_use_brotli(raw_data, raw_data_len, zip_data, &zip_data_len); } struct http_content_decompress *decompress_ins = http_content_decompress_create(encoding); unsigned char *unzip_data; size_t unzip_data_len; size_t total_read_zip_size = 0; size_t total_unziped_data_len = 0; while (total_read_zip_size < zip_data_len) { unzip_data = NULL; unzip_data_len = 0; int decompress_input_len = MIN((size_t)input_zip_block_size, zip_data_len - total_read_zip_size); int ret = http_content_decompress_write(decompress_ins, (char *)zip_data + total_read_zip_size, decompress_input_len, (char **)&unzip_data, &unzip_data_len); if (ret < 0) { goto fail; } if (unzip_data && unzip_data_len) { MD5Update(&zip_unzip_data_md5_ctx, unzip_data, unzip_data_len); total_unziped_data_len += unzip_data_len; } total_read_zip_size += decompress_input_len; } free(zip_data); http_content_decompress_destroy(decompress_ins); MD5Final(zip_unzip_data_md5sum, &zip_unzip_data_md5_ctx); if (total_unziped_data_len != raw_data_len) { // printf("ERROR: zip-unzip data len:%zu, raw data len:%zu\n", total_unziped_data_len, raw_data_len); return -1; } return memcmp(raw_data_md5sum, zip_unzip_data_md5sum, 16); fail: free(zip_data); http_content_decompress_destroy(decompress_ins); MD5Final(zip_unzip_data_md5sum, &zip_unzip_data_md5_ctx); return -1; } /* This test takes a long time! use gtest --gtest_also_run_disabled_tests to run this test if needs. */ TEST(http, DISABLED_deflate_ascii) { int ret; unsigned char *raw_data = http_build_ascii_text(ZIP_UNZIP_TEST_DATA_LEN); for (int block = 1; block <= 1460; block++) { if (block % 10 == 0) { printf("raw content: ascii text, encoding: deflate, block range: %d - %d\n", block, block + 9); } ret = http_zip_unzip_test(raw_data, ZIP_UNZIP_TEST_DATA_LEN, HTTP_CONTENT_ENCODING_DEFLATE, block); if (ret != 0) { printf("raw content: ascii text, encoding: deflate, block size:%d, result = failed!\n", block); } EXPECT_EQ(0, ret); } free(raw_data); } /* This test takes a long time! use gtest --gtest_also_run_disabled_tests to run this test if needs. */ TEST(http, DISABLED_deflate_random) { int ret; unsigned char *raw_data = http_build_random_content(ZIP_UNZIP_TEST_DATA_LEN); for (int block = 1; block <= 1460; block++) { if (block % 10 == 0) { printf("raw content: random binary, encoding: deflate, block range: %d - %d\n", block, block + 9); } ret = http_zip_unzip_test(raw_data, ZIP_UNZIP_TEST_DATA_LEN, HTTP_CONTENT_ENCODING_DEFLATE, block); if (ret != 0) { printf("raw content: random binary, encoding: deflate, block size:%d, result = failed!\n", block); } EXPECT_EQ(0, ret); } free(raw_data); } /* This test takes a long time! use gtest --gtest_also_run_disabled_tests to run this test if needs. */ TEST(http, DISABLED_brotli_random) { int ret; unsigned char *raw_data = http_build_random_content(ZIP_UNZIP_TEST_DATA_LEN); for (int block = 33; block <= 1460; block++) { if (block % 10 == 0) { printf("raw content: random binary, encoding: brotli, block range: %d - %d\n", block, block + 9); } ret = http_zip_unzip_test(raw_data, ZIP_UNZIP_TEST_DATA_LEN, HTTP_CONTENT_ENCODING_BR, block); if (ret != 0) { printf("raw content: random binary, encoding: brotli, block size:%d, result = failed!\n", block); } EXPECT_EQ(0, ret); } free(raw_data); } /* This test takes a long time! use gtest --gtest_also_run_disabled_tests to run this test if needs. */ TEST(http, DISABLED_brotli_ascii) { int ret; unsigned char *raw_data = http_build_ascii_text(ZIP_UNZIP_TEST_DATA_LEN); for (int block = 33; block <= 1460; block++) { if (block % 10 == 0) { printf("raw content: ascii text, encoding: brotli, block range: %d - %d\n", block, block + 9); } ret = http_zip_unzip_test(raw_data, ZIP_UNZIP_TEST_DATA_LEN, HTTP_CONTENT_ENCODING_BR, block); if (ret != 0) { printf("raw content: ascii text, encoding: brotli, block size:%d, result = failed!\n", block); } EXPECT_EQ(0, ret); } free(raw_data); } #include "http_decoder_half.c" #include "http_decoder_llhttp_wrap.c" struct gtest_llhttp_callback_flag { uint8_t on_message_begin; uint8_t on_message_complete; uint8_t on_uri; uint8_t on_uri_complete; uint8_t on_status; uint8_t on_status_complete; uint8_t on_method; uint8_t on_method_complete; uint8_t on_version; uint8_t on_version_complete; uint8_t on_header_field; uint8_t on_header_field_complete; uint8_t on_header_value; uint8_t on_header_value_complete; uint8_t on_chunk_header; uint8_t on_chunk_header_complete; uint8_t on_headers_complete; uint8_t on_body; }; static struct gtest_llhttp_callback_flag gtest_llhttp_cb_flags; static struct http_half_data gtest_flow_data; static int gtest_llhttp_on_message_begin(UNUSED llhttp_t *http) { struct http_half_parser *parser = container_of(http, struct http_half_parser, llhttp_parser); struct http_half *flow = parser->flow_ref; flow->event = HTTP_EVENT_REQ_INIT; gtest_llhttp_cb_flags.on_message_begin++; return 0; } static int gtest_llhttp_on_message_complete(llhttp_t *http) { struct http_half_parser *parser = container_of(http, struct http_half_parser, llhttp_parser); struct http_half *flow = parser->flow_ref; flow->event = __HTTP_EVENT_RESERVED; flow->shaper.headers_cache = NULL; gtest_llhttp_cb_flags.on_message_complete++; return 0; } static int gtest_llhttp_on_headers_complete(llhttp_t *http) { struct http_half_parser *parser = container_of(http, struct http_half_parser, llhttp_parser); struct http_half *flow = parser->flow_ref; flow->event = HTTP_EVENT_REQ_HDR_END; gtest_llhttp_cb_flags.on_headers_complete++; return 0; } static int gtest_llhttp_on_uri(UNUSED llhttp_t *http, UNUSED const char *at, UNUSED size_t length) { gtest_llhttp_cb_flags.on_uri++; return 0; } static int gtest_llhttp_on_method(UNUSED llhttp_t *http, UNUSED const char *at, UNUSED size_t length) { gtest_llhttp_cb_flags.on_method++; return 0; } static int gtest_llhttp_on_version(UNUSED llhttp_t *http, UNUSED const char *at, UNUSED size_t length) { gtest_llhttp_cb_flags.on_version++; return 0; } static int gtest_llhttp_on_body(UNUSED llhttp_t *http, UNUSED const char *at, UNUSED size_t length) { gtest_llhttp_cb_flags.on_body++; return 0; } static void gtest_llhttp_init(struct http_half *flow) { memset(flow, 0, sizeof(struct http_half)); flow->flow_data = >est_flow_data; flow->parser.flow_ref = flow; llhttp_settings_init(&flow->parser.settings); memset(>est_llhttp_cb_flags, 0, sizeof(gtest_llhttp_cb_flags)); flow->parser.settings.on_message_begin = gtest_llhttp_on_message_begin; flow->parser.settings.on_message_complete = gtest_llhttp_on_message_complete; flow->parser.settings.on_url = gtest_llhttp_on_uri; flow->parser.settings.on_method = gtest_llhttp_on_method; flow->parser.settings.on_version = gtest_llhttp_on_version; flow->parser.settings.on_body = gtest_llhttp_on_body; flow->parser.settings.on_headers_complete = gtest_llhttp_on_headers_complete; llhttp_init(&flow->parser.llhttp_parser, HTTP_BOTH, &flow->parser.settings); } TEST(http, stage_request_completed) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: keep-alive\r\n\r\n"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); http_flow_stage_shaping(&flow, newdata, new_datalen); llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_complete); } TEST(http, headers_extract) { const char *raw_data = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: keep-alive\r\n\r\n"; const char *headers_start, *headers_end; http_truncate_extract_headers(raw_data, strlen(raw_data), &headers_start, &headers_end); ASSERT_TRUE(headers_start != NULL); ASSERT_TRUE(headers_end != NULL); ASSERT_EQ(0, memcmp(headers_start, "Host: www.example.com\r\nConnection: keep-alive\r\n\r\n", headers_end - headers_start)); } TEST(http, stage_request_with_body) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,world"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); gtest_flow_data.header.content_length = 11; http_flow_stage_shaping(&flow, newdata, new_datalen); llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_body); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_complete); } TEST(http, stage_request_with_body_pipeline_many) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example1.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,worldGET / HTTP/1.1\r\nHost: www.example2.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,secndGET / HTTP/1.1\r\nHost: www.example3.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,third"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); gtest_flow_data.header.content_length = 11; http_flow_stage_shaping(&flow, newdata, new_datalen); llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_body); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_complete); } TEST(http, stage_request_with_body_pipeline_many_one_by_one) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example1.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,worldGET / HTTP/1.1\r\nHost: www.example2.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,secndGET / HTTP/1.1\r\nHost: www.example3.com\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\nhello,third"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); gtest_flow_data.header.content_length = 11; for (size_t i = 0; i < new_datalen; i++) { http_flow_stage_shaping(&flow, newdata + i, 1); } llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_complete); } TEST(http, stage_request_completed_pipeline) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example1.com\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: www.example2.com\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: www.example3.com\r\nConnection: keep-alive\r\n\r\n"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); http_flow_stage_shaping(&flow, newdata, new_datalen); llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(3, gtest_llhttp_cb_flags.on_message_complete); } TEST(http, stage_request_one_by_one) { const char *newdata = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: keep-alive\r\n\r\n"; size_t new_datalen = strlen(newdata); struct http_half flow = {}; gtest_llhttp_init(&flow); for (size_t i = 0; i < new_datalen; i++) { http_flow_stage_shaping(&flow, newdata + i, 1); } llhttp_finish(&flow.parser.llhttp_parser); http_buffer_free(flow.shaper.headers_cache); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_begin); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_method); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_uri); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_version); ASSERT_EQ(1, gtest_llhttp_cb_flags.on_message_complete); } int main(int argc, char const *argv[]) { ::testing::InitGoogleTest(&argc, (char **)argv); return RUN_ALL_TESTS(); }