cocoon/tests/unit/test_http3.c
xfy911 bf9787e6e9 feat(phase4): 实现 Phase 4 全部生态扩展模块
新增 4 个核心模块,12 个新文件,约 8832 行源代码 + 测试:

1. 扩展内置中间件 (middleware_ext.c/h)
   - JWT 认证:HS256 (HMAC-SHA256) 签名验证,Base64Url 解码
   - Security Headers:HSTS / X-Frame-Options / CSP / X-XSS-Protection
   - Request ID:32 字符 hex 唯一追踪 ID 生成与透传
   - IP 过滤:IPv4 / CIDR 黑名单与白名单,X-Forwarded-For 解析

2. 分布式负载均衡 (load_balance.c/h)
   - 一致性哈希:MurmurHash3 x86 32-bit + 512 虚拟节点/后端
   - 最少连接:实时活跃连接数跟踪
   - 加权响应时间:EWMA 指数加权移动平均
   - 随机算法

3. gRPC 支持 (grpc.c/h)
   - gRPC over HTTP/2,LEB128 消息帧编解码
   - 四种 RPC 模式:Unary / Server Streaming / Client Streaming / Bidirectional
   - gRPC-Web 兼容,17 个 gRPC 状态码完整支持

4. HTTP/3 (QUIC) (http3.c/h)
   - QUIC 传输层:UDP socket 管理,64-bit 连接 ID
   - HTTP/3 帧处理:HEADERS / DATA / SETTINGS / GOAWAY
   - QPACK 静态表编解码 (RFC 9204)
   - TLS 1.3 集成接口

新增单元测试:
- test_middleware_ext.c: 53 项测试
- test_load_balance.c: 56 项测试
- test_grpc.c: 88 项测试
- test_http3.c: 64 项测试
- 新增合计:261 项,累计 451 项

同时替换 coco 子模块为内联 stub 头文件,
确保无需外部依赖即可编译。
2026-06-10 11:36:45 +08:00

990 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file test_http3.c
* @brief HTTP/3 (QUIC) 模块单元测试
*
* 使用 Unity 测试框架,覆盖以下功能:
* - Variable-length integer 编解码
* - HTTP/3 帧头编解码
* - QPACK 静态表编码/解码
* - QUIC 流管理
* - QUIC 连接管理
* - HTTP/3 会话管理
* - SETTINGS / GOAWAY 帧处理
*
* @author Cocoon Team
*/
#include "unity.h"
#include "http3.h"
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
/* ===== 测试前置/后置 ===== */
void setUp(void) {
http3_init();
}
void tearDown(void) {
http3_cleanup();
}
/* ===== Variable-Length Integer 测试 ===== */
/** @test varint 编码0最小1字节值 */
void test_varint_encode_zero(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(0, buf);
TEST_ASSERT_EQUAL(1, n);
TEST_ASSERT_EQUAL(0, buf[0]);
}
/** @test varint 编码631字节最大值 */
void test_varint_encode_63(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(63, buf);
TEST_ASSERT_EQUAL(1, n);
TEST_ASSERT_EQUAL(63, buf[0]);
}
/** @test varint 编码64需要2字节 */
void test_varint_encode_64(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(64, buf);
TEST_ASSERT_EQUAL(2, n);
TEST_ASSERT_EQUAL(0x40, buf[0]);
TEST_ASSERT_EQUAL(0x40, buf[1]);
}
/** @test varint 编码163832字节最大值 */
void test_varint_encode_16383(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(16383, buf);
TEST_ASSERT_EQUAL(2, n);
TEST_ASSERT_EQUAL(0x7F, buf[0]);
TEST_ASSERT_EQUAL(0xFF, buf[1]);
}
/** @test varint 编码16384需要4字节 */
void test_varint_encode_16384(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(16384, buf);
TEST_ASSERT_EQUAL(4, n);
TEST_ASSERT_EQUAL(0x80, buf[0]);
TEST_ASSERT_EQUAL(0x00, buf[1]);
TEST_ASSERT_EQUAL(0x40, buf[2]);
TEST_ASSERT_EQUAL(0x00, buf[3]);
}
/** @test varint 编码10737418234字节最大值 */
void test_varint_encode_4byte_max(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(1073741823ULL, buf);
TEST_ASSERT_EQUAL(4, n);
TEST_ASSERT_EQUAL(0xBF, buf[0]);
TEST_ASSERT_EQUAL(0xFF, buf[1]);
TEST_ASSERT_EQUAL(0xFF, buf[2]);
TEST_ASSERT_EQUAL(0xFF, buf[3]);
}
/** @test varint 编码1073741824需要8字节 */
void test_varint_encode_8byte(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(1073741824ULL, buf);
TEST_ASSERT_EQUAL(8, n);
TEST_ASSERT_EQUAL(0xC0, buf[0]);
}
/** @test varint 编码:最大值 */
void test_varint_encode_max(void) {
uint8_t buf[8];
size_t n = http3_encode_varint(4611686018427387903ULL, buf);
TEST_ASSERT_EQUAL(8, n);
TEST_ASSERT_EQUAL(0xFF, buf[0]);
TEST_ASSERT_EQUAL(0xFF, buf[1]);
TEST_ASSERT_EQUAL(0xFF, buf[2]);
TEST_ASSERT_EQUAL(0xFF, buf[3]);
TEST_ASSERT_EQUAL(0xFF, buf[4]);
TEST_ASSERT_EQUAL(0xFF, buf[5]);
TEST_ASSERT_EQUAL(0xFF, buf[6]);
TEST_ASSERT_EQUAL(0xFF, buf[7]);
}
/** @test varint 编码NULL 缓冲区 */
void test_varint_encode_null_buf(void) {
size_t n = http3_encode_varint(42, NULL);
TEST_ASSERT_EQUAL(0, n);
}
/** @test varint 解码0 */
void test_varint_decode_zero(void) {
uint8_t buf[1] = {0x00};
uint64_t value = 0;
int n = http3_decode_varint(buf, 1, &value);
TEST_ASSERT_EQUAL(1, n);
TEST_ASSERT_EQUAL(0ULL, value);
}
/** @test varint 解码63 */
void test_varint_decode_63(void) {
uint8_t buf[1] = {0x3F};
uint64_t value = 0;
int n = http3_decode_varint(buf, 1, &value);
TEST_ASSERT_EQUAL(1, n);
TEST_ASSERT_EQUAL(63ULL, value);
}
/** @test varint 解码64 */
void test_varint_decode_64(void) {
uint8_t buf[2] = {0x40, 0x40};
uint64_t value = 0;
int n = http3_decode_varint(buf, 2, &value);
TEST_ASSERT_EQUAL(2, n);
TEST_ASSERT_EQUAL(64ULL, value);
}
/** @test varint 解码16383 */
void test_varint_decode_16383(void) {
uint8_t buf[2] = {0x7F, 0xFF};
uint64_t value = 0;
int n = http3_decode_varint(buf, 2, &value);
TEST_ASSERT_EQUAL(2, n);
TEST_ASSERT_EQUAL(16383ULL, value);
}
/** @test varint 解码4字节值 */
void test_varint_decode_4byte(void) {
uint8_t buf[4] = {0x80, 0x00, 0x00, 0x01};
uint64_t value = 0;
int n = http3_decode_varint(buf, 4, &value);
TEST_ASSERT_EQUAL(4, n);
TEST_ASSERT_EQUAL(1ULL, value);
}
/** @test varint 解码:数据不足 */
void test_varint_decode_insufficient_data(void) {
uint8_t buf[1] = {0x40}; /* 需要2字节但只给1字节 */
uint64_t value = 0;
int n = http3_decode_varint(buf, 1, &value);
TEST_ASSERT_EQUAL(-1, n);
}
/** @test varint 解码NULL 输入 */
void test_varint_decode_null(void) {
uint64_t value = 0;
int n = http3_decode_varint(NULL, 0, &value);
TEST_ASSERT_EQUAL(-1, n);
}
/** @test varint 编解码往返测试 */
void test_varint_roundtrip(void) {
uint64_t test_values[] = {0, 1, 63, 64, 100, 16383, 16384, 100000,
1073741823ULL, 1073741824ULL,
4294967295ULL, 4611686018427387903ULL};
int num_values = sizeof(test_values) / sizeof(test_values[0]);
for (int i = 0; i < num_values; i++) {
uint8_t buf[8];
size_t enc_n = http3_encode_varint(test_values[i], buf);
TEST_ASSERT_MESSAGE(enc_n > 0, "编码失败");
uint64_t decoded = 0;
int dec_n = http3_decode_varint(buf, enc_n, &decoded);
TEST_ASSERT_EQUAL_MESSAGE(enc_n, (size_t)dec_n, "编解码长度不匹配");
TEST_ASSERT_EQUAL_MESSAGE(test_values[i], decoded, "编解码值不匹配");
}
}
/** @test 帧头大小计算 */
void test_frame_header_size(void) {
TEST_ASSERT_EQUAL(2, http3_frame_header_size(0, 0));
TEST_ASSERT_EQUAL(2, http3_frame_header_size(1, 10));
TEST_ASSERT_EQUAL(4, http3_frame_header_size(100, 1000));
}
/* ===== HTTP/3 帧处理测试 ===== */
/** @test 帧头编码/解码 */
void test_frame_header_encode_decode(void) {
uint8_t buf[16];
uint64_t ft = HTTP3_FRAME_HEADERS;
uint64_t flen = 42;
size_t n = http3_encode_frame_header(ft, flen, buf);
TEST_ASSERT_GREATER_THAN(0, n);
uint64_t decoded_ft = 0, decoded_len = 0;
int dn = http3_decode_frame_header(buf, n, &decoded_ft, &decoded_len);
TEST_ASSERT_EQUAL(n, (size_t)dn);
TEST_ASSERT_EQUAL(ft, decoded_ft);
TEST_ASSERT_EQUAL(flen, decoded_len);
}
/** @test 完整帧编码 */
void test_full_frame_encode(void) {
uint8_t payload[] = "Hello HTTP/3";
uint8_t buf[64];
int n = http3_encode_frame(HTTP3_FRAME_DATA, payload, sizeof(payload) - 1,
buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
/* 验证帧头 */
uint64_t ft = 0, flen = 0;
int hd = http3_decode_frame_header(buf, (size_t)n, &ft, &flen);
TEST_ASSERT_GREATER_THAN(0, hd);
TEST_ASSERT_EQUAL(HTTP3_FRAME_DATA, ft);
TEST_ASSERT_EQUAL(sizeof(payload) - 1, flen);
}
/** @test 帧头解码:数据不足 */
void test_frame_header_decode_insufficient(void) {
uint8_t buf[1] = {0x80}; /* 需要更多数据 */
uint64_t ft = 0, flen = 0;
int n = http3_decode_frame_header(buf, 1, &ft, &flen);
TEST_ASSERT_EQUAL(-1, n);
}
/** @test 帧头解码NULL 输入 */
void test_frame_header_decode_null(void) {
uint64_t ft = 0, flen = 0;
int n = http3_decode_frame_header(NULL, 0, &ft, &flen);
TEST_ASSERT_EQUAL(-1, n);
}
/** @test 编码 DATA 帧 */
void test_encode_data_frame(void) {
uint8_t payload[] = {0x01, 0x02, 0x03, 0x04};
uint8_t buf[32];
int n = http3_encode_frame(HTTP3_FRAME_DATA, payload, 4, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
uint64_t ft = 0, flen = 0;
int hd = http3_decode_frame_header(buf, (size_t)n, &ft, &flen);
TEST_ASSERT_GREATER_THAN(0, hd);
TEST_ASSERT_EQUAL(HTTP3_FRAME_DATA, ft);
TEST_ASSERT_EQUAL(4, flen);
}
/** @test 编码 HEADERS 帧 */
void test_encode_headers_frame(void) {
uint8_t payload[] = {0xC0, 0x01, 0xD5}; /* 一些 QPACK 编码数据 */
uint8_t buf[32];
int n = http3_encode_frame(HTTP3_FRAME_HEADERS, payload, 3, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
uint64_t ft = 0, flen = 0;
int hd = http3_decode_frame_header(buf, (size_t)n, &ft, &flen);
TEST_ASSERT_GREATER_THAN(0, hd);
TEST_ASSERT_EQUAL(HTTP3_FRAME_HEADERS, ft);
TEST_ASSERT_EQUAL(3, flen);
}
/** @test 编码 SETTINGS 帧 */
void test_encode_settings_frame(void) {
uint8_t payload[16];
size_t pos = 0;
pos += http3_encode_varint(HTTP3_SETTING_MAX_FIELD_SECTION_SIZE, payload + pos);
pos += http3_encode_varint(16384, payload + pos);
uint8_t buf[32];
int n = http3_encode_frame(HTTP3_FRAME_SETTINGS, payload, pos, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
uint64_t ft = 0, flen = 0;
int hd = http3_decode_frame_header(buf, (size_t)n, &ft, &flen);
TEST_ASSERT_GREATER_THAN(0, hd);
TEST_ASSERT_EQUAL(HTTP3_FRAME_SETTINGS, ft);
}
/** @test 编码 GOAWAY 帧 */
void test_encode_goaway_frame(void) {
uint8_t payload[8];
size_t pos = http3_encode_varint(100, payload);
uint8_t buf[32];
int n = http3_encode_frame(HTTP3_FRAME_GOAWAY, payload, pos, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
uint64_t ft = 0, flen = 0;
int hd = http3_decode_frame_header(buf, (size_t)n, &ft, &flen);
TEST_ASSERT_GREATER_THAN(0, hd);
TEST_ASSERT_EQUAL(HTTP3_FRAME_GOAWAY, ft);
}
/* ===== QPACK 编码/解码测试 ===== */
/** @test QPACK 编码:静态表完全匹配 :method=GET */
void test_qpack_encode_static_match_method_get(void) {
uint8_t buf[16];
int n = qpack_encode_header(":method", "GET", buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
/* Should use indexed encoding (0b11xxxxxx) with static index 2 */
/* buf[0] should be 0xC0 | 0x02 = 0xC2 = 194 */
TEST_ASSERT_EQUAL(0xC0, buf[0] & 0xC0); /* 0b11 prefix */
}
/** @test QPACK 编码:静态表完全匹配 :path=/ */
void test_qpack_encode_static_match_path(void) {
uint8_t buf[16];
int n = qpack_encode_header(":path", "/", buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
TEST_ASSERT_EQUAL(0xC0, buf[0] & 0xC0); /* 0b11 prefix */
}
/** @test QPACK 编码:静态表完全匹配 :scheme=https */
void test_qpack_encode_static_match_scheme(void) {
uint8_t buf[16];
int n = qpack_encode_header(":scheme", "https", buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
TEST_ASSERT_EQUAL(0xC0, buf[0] & 0xC0); /* 0b11 prefix */
}
/** @test QPACK 编码:字面量编码未知字段 */
void test_qpack_encode_literal_field(void) {
uint8_t buf[64];
int n = qpack_encode_header("x-custom-header", "custom-value", buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
/* Should use literal encoding (0b001xxxxx) */
TEST_ASSERT_EQUAL(0x20, buf[0]);
}
/** @test QPACK 编码:名称匹配静态表,值用字面量 */
void test_qpack_encode_name_ref_value_literal(void) {
uint8_t buf[64];
int n = qpack_encode_header("accept", "text/html", buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
/* accept=text/html is NOT a full static table match (accept="" at idx 17),
* so it uses literal with name reference (0b0101xxxx) */
TEST_ASSERT_EQUAL(0x40, buf[0] & 0xE0); /* 0b010 prefix */
}
/** @test QPACK 解码:索引编码的头部 */
void test_qpack_decode_indexed_header(void) {
/* Encode :method=GET - static index 2 */
uint8_t encoded[16];
int enc_n = qpack_encode_header(":method", "GET", encoded, sizeof(encoded));
TEST_ASSERT_GREATER_THAN(0, enc_n);
/* Now decode it */
qpack_decoded_t decoded;
size_t consumed = 0;
int rc = qpack_decode_header(encoded, (size_t)enc_n, &decoded, &consumed);
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_TRUE(decoded.valid);
TEST_ASSERT_EQUAL_STRING(":method", decoded.name);
TEST_ASSERT_EQUAL_STRING("GET", decoded.value);
}
/** @test QPACK 解码:字面量编码的头部 */
void test_qpack_decode_literal_header(void) {
/* Encode a literal field */
uint8_t encoded[64];
int enc_n = qpack_encode_header("x-test", "test-value", encoded, sizeof(encoded));
TEST_ASSERT_GREATER_THAN(0, enc_n);
/* Decode it */
qpack_decoded_t decoded;
size_t consumed = 0;
int rc = qpack_decode_header(encoded, (size_t)enc_n, &decoded, &consumed);
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_TRUE(decoded.valid);
TEST_ASSERT_EQUAL_STRING("x-test", decoded.name);
TEST_ASSERT_EQUAL_STRING("test-value", decoded.value);
}
/** @test QPACK 编解码往返 */
void test_qpack_roundtrip(void) {
const char *test_names[] = {
":method", ":path", ":scheme", ":authority",
"content-type", "accept", "user-agent"
};
const char *test_values[] = {
"GET", "/api/v1/test", "https", "example.com",
"application/json", "*/*", "TestAgent/1.0"
};
int num = sizeof(test_names) / sizeof(test_names[0]);
for (int i = 0; i < num; i++) {
uint8_t encoded[128];
int enc_n = qpack_encode_header(test_names[i], test_values[i],
encoded, sizeof(encoded));
TEST_ASSERT_GREATER_THAN_MESSAGE(0, enc_n, "编码失败");
qpack_decoded_t decoded;
size_t consumed = 0;
int rc = qpack_decode_header(encoded, (size_t)enc_n, &decoded, &consumed);
TEST_ASSERT_EQUAL_MESSAGE(0, rc, "解码失败");
TEST_ASSERT_TRUE_MESSAGE(decoded.valid, "解码结果无效");
TEST_ASSERT_EQUAL_STRING_MESSAGE(test_names[i], decoded.name, "名称不匹配");
TEST_ASSERT_EQUAL_STRING_MESSAGE(test_values[i], decoded.value, "值不匹配");
}
}
/** @test QPACK 编码完整请求头 */
void test_qpack_encode_request_headers(void) {
http_request_t req;
memset(&req, 0, sizeof(req));
req.method = HTTP_GET;
strncpy(req.path, "/index.html", sizeof(req.path) - 1);
strncpy(req.headers[0].name, "host", sizeof(req.headers[0].name) - 1);
strncpy(req.headers[0].value, "example.com", sizeof(req.headers[0].value) - 1);
req.num_headers = 1;
uint8_t buf[1024];
int n = qpack_encode_request_headers(&req, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
}
/** @test QPACK 解码完整请求头 */
void test_qpack_decode_request_headers(void) {
/* Build an encoded request */
http_request_t req;
memset(&req, 0, sizeof(req));
req.method = HTTP_GET;
strncpy(req.path, "/test", sizeof(req.path) - 1);
strncpy(req.headers[0].name, "host", sizeof(req.headers[0].name) - 1);
strncpy(req.headers[0].value, "example.com", sizeof(req.headers[0].value) - 1);
req.num_headers = 1;
uint8_t encoded[1024];
int enc_n = qpack_encode_request_headers(&req, encoded, sizeof(encoded));
TEST_ASSERT_GREATER_THAN(0, enc_n);
/* Decode it */
http_request_t decoded_req;
int rc = qpack_decode_request_headers(encoded, (size_t)enc_n, &decoded_req);
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_EQUAL(HTTP_GET, decoded_req.method);
TEST_ASSERT_EQUAL_STRING("/test", decoded_req.path);
http_request_free(&decoded_req);
}
/** @test QPACK 编码响应头 */
void test_qpack_encode_response_headers(void) {
http_response_t resp;
memset(&resp, 0, sizeof(resp));
resp.status_code = 200;
resp.content_type = "text/html";
resp.content_length = 42;
uint8_t buf[1024];
int n = qpack_encode_response_headers(&resp, buf, sizeof(buf));
TEST_ASSERT_GREATER_THAN(0, n);
}
/** @test QPACK 解码响应头 */
void test_qpack_decode_response_headers(void) {
/* Build an encoded response */
http_response_t resp;
memset(&resp, 0, sizeof(resp));
resp.status_code = 404;
resp.content_type = "text/plain";
resp.content_length = 0;
uint8_t encoded[1024];
int enc_n = qpack_encode_response_headers(&resp, encoded, sizeof(encoded));
TEST_ASSERT_GREATER_THAN(0, enc_n);
/* Decode it */
http_response_t decoded_resp;
int rc = qpack_decode_response_headers(encoded, (size_t)enc_n, &decoded_resp);
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_EQUAL(404, decoded_resp.status_code);
if (decoded_resp.content_type) {
free((void *)decoded_resp.content_type);
}
}
/** @test QPACK 解码:无效输入 */
void test_qpack_decode_invalid(void) {
qpack_decoded_t decoded;
size_t consumed = 0;
int rc = qpack_decode_header(NULL, 0, &decoded, &consumed);
TEST_ASSERT_EQUAL(-1, rc);
}
/* ===== QUIC 流管理测试 ===== */
/** @test QUIC 流创建和查找 */
void test_quic_stream_create_and_find(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s = quic_stream_get_or_create(conn, 0);
TEST_ASSERT_NOT_NULL(s);
TEST_ASSERT_EQUAL(0, s->stream_id);
quic_stream_t *found = quic_stream_find(conn, 0);
TEST_ASSERT_NOT_NULL(found);
TEST_ASSERT_EQUAL_PTR(s, found);
quic_connection_destroy(conn);
}
/** @test QUIC 流数据写入和读取 */
void test_quic_stream_write_read(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s = quic_stream_get_or_create(conn, 0);
TEST_ASSERT_NOT_NULL(s);
uint8_t data[] = "Hello, QUIC Stream!";
int rc = quic_stream_write(s, data, sizeof(data));
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_EQUAL(sizeof(data), s->recv_buf_len);
uint8_t read_buf[64];
ssize_t n = quic_stream_read(s, read_buf, sizeof(read_buf));
TEST_ASSERT_EQUAL(sizeof(data), n);
TEST_ASSERT_EQUAL_MEMORY(data, read_buf, sizeof(data));
TEST_ASSERT_EQUAL(0, s->recv_buf_len);
quic_connection_destroy(conn);
}
/** @test QUIC 流 FIN 标志 */
void test_quic_stream_fin(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s = quic_stream_get_or_create(conn, 0);
TEST_ASSERT_NOT_NULL(s);
TEST_ASSERT_FALSE(s->local_fin);
quic_stream_set_fin(s);
TEST_ASSERT_TRUE(s->local_fin);
quic_connection_destroy(conn);
}
/** @test QUIC 流销毁 */
void test_quic_stream_destroy(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s = quic_stream_get_or_create(conn, 4);
TEST_ASSERT_NOT_NULL(s);
quic_stream_destroy(conn, s);
quic_stream_t *found = quic_stream_find(conn, 4);
TEST_ASSERT_NULL(found);
quic_connection_destroy(conn);
}
/** @test QUIC 流:多个流 */
void test_quic_multiple_streams(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s0 = quic_stream_get_or_create(conn, 0);
quic_stream_t *s4 = quic_stream_get_or_create(conn, 4);
quic_stream_t *s8 = quic_stream_get_or_create(conn, 8);
TEST_ASSERT_NOT_NULL(s0);
TEST_ASSERT_NOT_NULL(s4);
TEST_ASSERT_NOT_NULL(s8);
TEST_ASSERT(s0 != s4);
TEST_ASSERT(s4 != s8);
quic_connection_destroy(conn);
}
/** @test QUIC 流:写入大数据 */
void test_quic_stream_large_write(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(12345,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_stream_t *s = quic_stream_get_or_create(conn, 0);
TEST_ASSERT_NOT_NULL(s);
uint8_t *large_data = (uint8_t *)malloc(10000);
TEST_ASSERT_NOT_NULL(large_data);
memset(large_data, 0xAB, 10000);
int rc = quic_stream_write(s, large_data, 10000);
TEST_ASSERT_EQUAL(0, rc);
TEST_ASSERT_EQUAL(10000, s->recv_buf_len);
uint8_t *read_buf = (uint8_t *)malloc(10000);
TEST_ASSERT_NOT_NULL(read_buf);
ssize_t n = quic_stream_read(s, read_buf, 10000);
TEST_ASSERT_EQUAL(10000, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(large_data, read_buf, 10000, "大数据读写不一致");
free(large_data);
free(read_buf);
quic_connection_destroy(conn);
}
/* ===== QUIC 连接管理测试 ===== */
/** @test QUIC 连接创建和销毁 */
void test_quic_connection_create_destroy(void) {
struct sockaddr_storage addr = {0};
((struct sockaddr_in *)&addr)->sin_family = AF_INET;
quic_connection_t *conn = quic_connection_create(99999,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
TEST_ASSERT_EQUAL(99999, conn->conn_id);
TEST_ASSERT_FALSE(conn->handshake_complete);
TEST_ASSERT_FALSE(conn->closed);
quic_connection_destroy(conn);
}
/** @test QUIC 连接查找 */
void test_quic_find_connection(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(11111,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
quic_connection_t *found = quic_find_connection(11111);
TEST_ASSERT_NOT_NULL(found);
TEST_ASSERT_EQUAL_PTR(conn, found);
quic_connection_t *not_found = quic_find_connection(99999);
TEST_ASSERT_NULL(not_found);
quic_connection_destroy(conn);
}
/** @test QUIC 连接计数 */
void test_quic_connection_count(void) {
size_t before = quic_get_connection_count();
struct sockaddr_storage addr = {0};
quic_connection_t *c1 = quic_connection_create(100,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(c1);
TEST_ASSERT_EQUAL(before + 1, quic_get_connection_count());
quic_connection_t *c2 = quic_connection_create(200,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(c2);
TEST_ASSERT_EQUAL(before + 2, quic_get_connection_count());
quic_connection_destroy(c1);
TEST_ASSERT_EQUAL(before + 1, quic_get_connection_count());
quic_connection_destroy(c2);
TEST_ASSERT_EQUAL(before, quic_get_connection_count());
}
/** @test QUIC 连接 ID 生成 */
void test_quic_generate_conn_id(void) {
uint64_t id1 = quic_generate_conn_id();
uint64_t id2 = quic_generate_conn_id();
TEST_ASSERT_NOT_EQUAL(id1, id2);
TEST_ASSERT_NOT_EQUAL(0, id1);
TEST_ASSERT_NOT_EQUAL(0, id2);
}
/** @test QUIC 超时连接清理 */
void test_quic_cleanup_timeout_connections(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(300,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
size_t count_before = quic_get_connection_count();
TEST_ASSERT_GREATER_THAN(0, count_before);
/* 设置最后活动时间为很久以前 */
conn->last_activity = 0;
/* 清理超时连接(使用 1ms 超时) */
quic_cleanup_timeout_connections(1);
/* 连接应该被清理 */
quic_connection_t *found = quic_find_connection(300);
TEST_ASSERT_NULL(found);
}
/* ===== HTTP/3 会话管理测试 ===== */
/** @test HTTP/3 会话创建和销毁 */
void test_http3_session_create_destroy(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(400,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
http3_session_t *session = http3_session_create(conn);
TEST_ASSERT_NOT_NULL(session);
TEST_ASSERT_EQUAL(conn, session->conn);
TEST_ASSERT_EQUAL(HTTP3_DEFAULT_MAX_FIELD_SECTION_SIZE,
session->max_field_section_size);
http3_session_destroy(session);
quic_connection_destroy(conn);
}
/** @test HTTP/3 多次会话创建 */
void test_http3_session_multiple(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn1 = quic_connection_create(500,
COCOON_INVALID_SOCKET, &addr);
quic_connection_t *conn2 = quic_connection_create(600,
COCOON_INVALID_SOCKET, &addr);
http3_session_t *s1 = http3_session_create(conn1);
http3_session_t *s2 = http3_session_create(conn2);
TEST_ASSERT_NOT_NULL(s1);
TEST_ASSERT_NOT_NULL(s2);
TEST_ASSERT(s1 != s2);
http3_session_destroy(s1);
http3_session_destroy(s2);
quic_connection_destroy(conn1);
quic_connection_destroy(conn2);
}
/** @test HTTP/3 会话创建NULL 连接 */
void test_http3_session_create_null(void) {
http3_session_t *session = http3_session_create(NULL);
TEST_ASSERT_NULL(session);
}
/* ===== 错误码测试 ===== */
/** @test HTTP/3 错误码值 */
void test_http3_error_codes(void) {
TEST_ASSERT_EQUAL(0x0100, HTTP3_NO_ERROR);
TEST_ASSERT_EQUAL(0x0101, HTTP3_GENERAL_PROTOCOL_ERROR);
TEST_ASSERT_EQUAL(0x0102, HTTP3_INTERNAL_ERROR);
TEST_ASSERT_EQUAL(0x0103, HTTP3_STREAM_CREATION_ERROR);
TEST_ASSERT_EQUAL(0x0110, HTTP3_VERSION_FALLBACK);
}
/* ===== 时间戳测试 ===== */
/** @test 当前时间戳 */
void test_quic_current_time_ms(void) {
uint64_t t1 = quic_current_time_ms();
TEST_ASSERT_GREATER_THAN(0, t1);
/* 应该是单调递增的(至少不减) */
uint64_t t2 = quic_current_time_ms();
TEST_ASSERT_GREATER_OR_EQUAL(t1, t2);
}
/* ===== 边界条件测试 ===== */
/** @test varint 编解码边界值 */
void test_varint_boundary_values(void) {
uint64_t boundaries[] = {0, 63, 64, 16383, 16384, 1073741823ULL,
1073741824ULL, 4611686018427387903ULL};
int num = sizeof(boundaries) / sizeof(boundaries[0]);
for (int i = 0; i < num; i++) {
uint8_t buf[8];
size_t enc_n = http3_encode_varint(boundaries[i], buf);
TEST_ASSERT_GREATER_THAN(0, enc_n);
uint64_t decoded = 0;
int dec_n = http3_decode_varint(buf, enc_n, &decoded);
TEST_ASSERT_EQUAL(enc_n, (size_t)dec_n);
TEST_ASSERT_EQUAL(boundaries[i], decoded);
}
}
/** @test QUIC 流NULL 参数处理 */
void test_quic_stream_null_params(void) {
int rc = quic_stream_write(NULL, (uint8_t *)"test", 4);
TEST_ASSERT_EQUAL(-1, rc);
ssize_t n = quic_stream_read(NULL, NULL, 0);
TEST_ASSERT_EQUAL(-1, n);
quic_stream_set_fin(NULL); /* 不应崩溃 */
}
/** @test QUIC 连接NULL 参数处理 */
void test_quic_connection_null(void) {
quic_connection_destroy(NULL); /* 不应崩溃 */
quic_stream_t *s = quic_stream_get_or_create(NULL, 0);
TEST_ASSERT_NULL(s);
quic_stream_t *f = quic_stream_find(NULL, 0);
TEST_ASSERT_NULL(f);
}
/** @test http3_close_connection: NULL */
void test_http3_close_connection_null(void) {
http3_close_connection(NULL, HTTP3_NO_ERROR); /* 不应崩溃 */
}
/** @test http3_send_error: NULL session */
void test_http3_send_error_null(void) {
http3_send_error(NULL, 0, 404, "Not Found"); /* 不应崩溃 */
}
/** @test http3_send_settings: NULL */
void test_http3_send_settings_null(void) {
int rc = http3_send_settings(NULL);
TEST_ASSERT_EQUAL(-1, rc);
}
/** @test http3_process_datagram: NULL params */
void test_http3_process_datagram_null(void) {
uint8_t data[] = "test";
struct sockaddr_storage addr = {0};
http3_process_datagram(COCOON_INVALID_SOCKET, NULL, 0, &addr);
http3_process_datagram(COCOON_INVALID_SOCKET, data, 4, NULL);
}
/** @test quic_send_datagram: invalid socket */
void test_quic_send_datagram_invalid(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(700,
COCOON_INVALID_SOCKET, &addr);
TEST_ASSERT_NOT_NULL(conn);
uint8_t data[] = "test";
int rc = quic_send_datagram(conn, data, 4);
TEST_ASSERT_EQUAL(-1, rc);
quic_connection_destroy(conn);
}
/** @test 帧解码:数据不足 */
void test_parse_frame_insufficient_data(void) {
struct sockaddr_storage addr = {0};
quic_connection_t *conn = quic_connection_create(800,
COCOON_INVALID_SOCKET, &addr);
quic_stream_t *stream = quic_stream_get_or_create(conn, 0);
/* 写入不完整的帧数据 */
uint8_t partial[] = {0x00}; /* 只有类型,没有长度 */
quic_stream_write(stream, partial, 1);
uint64_t ft = 0;
const uint8_t *payload = NULL;
size_t payload_len = 0;
int rc = http3_parse_frame(stream, &ft, &payload, &payload_len);
TEST_ASSERT_EQUAL(1, rc); /* 数据不足 */
quic_connection_destroy(conn);
}
/* ===== main ===== */
int main(void) {
UNITY_BEGIN();
/* Variable-length integer encode */
RUN_TEST(test_varint_encode_zero);
RUN_TEST(test_varint_encode_63);
RUN_TEST(test_varint_encode_64);
RUN_TEST(test_varint_encode_16383);
RUN_TEST(test_varint_encode_16384);
RUN_TEST(test_varint_encode_4byte_max);
RUN_TEST(test_varint_encode_8byte);
RUN_TEST(test_varint_encode_max);
RUN_TEST(test_varint_encode_null_buf);
/* Variable-length integer decode */
RUN_TEST(test_varint_decode_zero);
RUN_TEST(test_varint_decode_63);
RUN_TEST(test_varint_decode_64);
RUN_TEST(test_varint_decode_16383);
RUN_TEST(test_varint_decode_4byte);
RUN_TEST(test_varint_decode_insufficient_data);
RUN_TEST(test_varint_decode_null);
RUN_TEST(test_varint_roundtrip);
RUN_TEST(test_varint_boundary_values);
/* Frame header */
RUN_TEST(test_frame_header_size);
RUN_TEST(test_frame_header_encode_decode);
RUN_TEST(test_frame_header_decode_insufficient);
RUN_TEST(test_frame_header_decode_null);
/* Full frame encoding */
RUN_TEST(test_full_frame_encode);
RUN_TEST(test_encode_data_frame);
RUN_TEST(test_encode_headers_frame);
RUN_TEST(test_encode_settings_frame);
RUN_TEST(test_encode_goaway_frame);
/* QPACK encode */
RUN_TEST(test_qpack_encode_static_match_method_get);
RUN_TEST(test_qpack_encode_static_match_path);
RUN_TEST(test_qpack_encode_static_match_scheme);
RUN_TEST(test_qpack_encode_literal_field);
RUN_TEST(test_qpack_encode_name_ref_value_literal);
/* QPACK decode */
RUN_TEST(test_qpack_decode_indexed_header);
RUN_TEST(test_qpack_decode_literal_header);
RUN_TEST(test_qpack_decode_invalid);
/* QPACK roundtrip */
RUN_TEST(test_qpack_roundtrip);
RUN_TEST(test_qpack_encode_request_headers);
RUN_TEST(test_qpack_decode_request_headers);
RUN_TEST(test_qpack_encode_response_headers);
RUN_TEST(test_qpack_decode_response_headers);
/* QUIC stream management */
RUN_TEST(test_quic_stream_create_and_find);
RUN_TEST(test_quic_stream_write_read);
RUN_TEST(test_quic_stream_fin);
RUN_TEST(test_quic_stream_destroy);
RUN_TEST(test_quic_multiple_streams);
RUN_TEST(test_quic_stream_large_write);
RUN_TEST(test_quic_stream_null_params);
/* QUIC connection management */
RUN_TEST(test_quic_connection_create_destroy);
RUN_TEST(test_quic_find_connection);
RUN_TEST(test_quic_connection_count);
RUN_TEST(test_quic_generate_conn_id);
RUN_TEST(test_quic_cleanup_timeout_connections);
RUN_TEST(test_quic_connection_null);
/* HTTP/3 session management */
RUN_TEST(test_http3_session_create_destroy);
RUN_TEST(test_http3_session_multiple);
RUN_TEST(test_http3_session_create_null);
/* Error codes */
RUN_TEST(test_http3_error_codes);
/* Time utilities */
RUN_TEST(test_quic_current_time_ms);
/* Boundary / NULL tests */
RUN_TEST(test_http3_close_connection_null);
RUN_TEST(test_http3_send_error_null);
RUN_TEST(test_http3_send_settings_null);
RUN_TEST(test_http3_process_datagram_null);
RUN_TEST(test_quic_send_datagram_invalid);
RUN_TEST(test_parse_frame_insufficient_data);
return UNITY_END();
}