This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/test/decoders/http/http_gtest.cpp

359 lines
12 KiB
C++

#include <gtest/gtest.h>
#include <unistd.h>
#include <stdio.h>
#include "zlib.h"
#include "md5/md5.h"
#include "http.h"
#include "http_decoder_private.h"
#include "brotli/decode.h"
#include "brotli/encode.h"
#include "event2/buffer.h"
#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, 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, event_buffer)
{
struct evbuffer *evbuf = evbuffer_new();
evbuffer_add(evbuf, "hello", 5);
size_t len = evbuffer_get_length(evbuf);
EXPECT_EQ(len, 5);
char outbuf[16];
len = evbuffer_copyout(evbuf, outbuf, sizeof(outbuf));
EXPECT_EQ(len, 5);
EXPECT_EQ(0, memcmp(outbuf, "hello", 5));
evbuffer_add(evbuf, ",", 1);
evbuffer_add(evbuf, "world", 5);
len = evbuffer_copyout(evbuf, outbuf, sizeof(outbuf));
EXPECT_EQ(len, 11);
EXPECT_EQ(0, memcmp(outbuf, "hello,world", 11));
evbuffer_free(evbuf);
}
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);
}
int main(int argc, char const *argv[])
{
::testing::InitGoogleTest(&argc, (char **)argv);
return RUN_ALL_TESTS();
}