cocoon/http3.h
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

626 lines
18 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 http3.h
* @brief HTTP/3 (QUIC) 支持模块头文件
*
* 实现 HTTP/3 over QUIC 协议支持,包括:
* - QUIC 基础传输层简化版UDP socket、连接 ID、流管理
* - HTTP/3 帧处理HEADERS、DATA、SETTINGS、GOAWAY 等
* - QPACK 头部压缩:静态表编码/解码RFC 9204 Appendix A
* - Variable-length integer 编解码
* - TLS 1.3 集成接口条件编译OpenSSL 3.2+
*
* @author Cocoon Team
*/
#ifndef COCOON_HTTP3_H
#define COCOON_HTTP3_H
#include "http.h"
#include "platform.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ===== HTTP/3 帧类型 ===== */
#define HTTP3_FRAME_DATA 0x00
#define HTTP3_FRAME_HEADERS 0x01
#define HTTP3_FRAME_CANCEL_PUSH 0x03
#define HTTP3_FRAME_SETTINGS 0x04
#define HTTP3_FRAME_PUSH_PROMISE 0x05
#define HTTP3_FRAME_GOAWAY 0x06
#define HTTP3_FRAME_MAX_PUSH_ID 0x07
#define HTTP3_FRAME_PRIORITY_UPDATE_REQ 0xF0700
#define HTTP3_FRAME_PRIORITY_UPDATE_PUSH 0xF0701
/* ===== HTTP/3 设置参数 ===== */
#define HTTP3_SETTING_MAX_FIELD_SECTION_SIZE 0x06
#define HTTP3_DEFAULT_MAX_FIELD_SECTION_SIZE (16384) /**< 16KB */
#define HTTP3_SETTING_QPACK_MAX_TABLE_CAPACITY 0x01
#define HTTP3_SETTING_BLOCKED_STREAMS 0x07
/* ===== QUIC 常量 ===== */
#define QUIC_MAX_DATAGRAM_SIZE 1200
#define QUIC_MAX_STREAMS_PER_CONN 100
#define QUIC_DEFAULT_IDLE_TIMEOUT 30000 /**< 30 秒(毫秒) */
#define QUIC_MAX_CONN_ID_LEN 8
#define HTTP3_MAX_HEADER_ENTRIES 64
#define QPACK_STATIC_TABLE_SIZE 99
/* ===== HTTP/3 错误码 ===== */
typedef enum {
HTTP3_NO_ERROR = 0x0100,
HTTP3_GENERAL_PROTOCOL_ERROR = 0x0101,
HTTP3_INTERNAL_ERROR = 0x0102,
HTTP3_STREAM_CREATION_ERROR = 0x0103,
HTTP3_CLOSED_CRITICAL_STREAM = 0x0104,
HTTP3_FRAME_UNEXPECTED = 0x0105,
HTTP3_FRAME_ERROR = 0x0106,
HTTP3_EXCESSIVE_LOAD = 0x0107,
HTTP3_ID_ERROR = 0x0108,
HTTP3_SETTINGS_ERROR = 0x0109,
HTTP3_MISSING_SETTINGS = 0x010A,
HTTP3_REQUEST_REJECTED = 0x010B,
HTTP3_REQUEST_CANCELLED = 0x010C,
HTTP3_REQUEST_INCOMPLETE = 0x010D,
HTTP3_EARLY_RESPONSE = 0x010E,
HTTP3_CONNECT_ERROR = 0x010F,
HTTP3_VERSION_FALLBACK = 0x0110
} http3_error_t;
/* ===== 前向声明 ===== */
typedef struct quic_stream quic_stream_t;
typedef struct quic_connection quic_connection_t;
/**
* @brief QPACK 静态表条目
*
* RFC 9204 Appendix A 定义的静态表。
* 包含常见 HTTP 字段名和值的预定义索引。
*/
typedef struct {
const char *name; /**< 字段名 */
const char *value; /**< 字段值(可为空字符串) */
} qpack_static_entry_t;
/**
* @brief QUIC 流结构
*
* 每个 QUIC 流对应一个此结构体。
* 流 ID 的低 2 位表示类型:
* - 0x00: 客户端发起的双向流
* - 0x01: 服务器发起的双向流
* - 0x02: 客户端发起的单向流
* - 0x03: 服务器发起的单向流
*/
struct quic_stream {
uint64_t stream_id; /**< 流 ID */
uint64_t offset; /**< 当前发送偏移 */
uint64_t recv_offset; /**< 当前接收偏移 */
bool peer_fin; /**< 对端已发送 FIN */
bool local_fin; /**< 本端已发送 FIN */
bool reset; /**< 流是否被重置 */
uint8_t *recv_buf; /**< 接收缓冲区(动态分配) */
size_t recv_buf_len; /**< 接收缓冲区已用长度 */
size_t recv_buf_cap; /**< 接收缓冲区容量 */
quic_connection_t *conn; /**< 所属连接 */
quic_stream_t *next; /**< 链表下一个节点 */
};
/**
* @brief QUIC 连接结构
*
* 简化版 QUIC 连接管理。
* 不实现完整拥塞控制,依赖内核 UDP 传输。
*/
struct quic_connection {
uint64_t conn_id; /**< 64-bit 连接 ID */
cocoon_socket_t udp_fd; /**< 底层 UDP socket */
struct sockaddr_storage peer_addr; /**< 对端地址 */
socklen_t peer_addr_len; /**< 对端地址长度 */
bool handshake_complete; /**< TLS 1.3 握手完成标志 */
void *tls_conn; /**< TLS 连接指针( opaque避免暴露 tls_conn_t */
uint32_t max_streams_bidi; /**< 最大双向流数 */
uint32_t next_stream_id; /**< 下一个客户端发起的双向流 ID */
quic_stream_t *streams; /**< 活跃流链表头 */
quic_stream_t *streams_tail; /**< 活跃流链表尾 */
uint64_t bytes_received; /**< 接收字节数 */
uint64_t bytes_sent; /**< 发送字节数 */
uint64_t idle_timeout_ms; /**< 空闲超时(毫秒) */
uint64_t last_activity; /**< 最后活动时间戳(毫秒) */
bool closed; /**< 连接已关闭 */
bool closing; /**< 连接正在关闭 */
quic_connection_t *next; /**< 全局连接链表下一个节点 */
};
/**
* @brief HTTP/3 流结构
*
* 封装 QUIC 流,添加 HTTP/3 语义状态。
*/
typedef struct {
quic_stream_t *qstream; /**< 底层 QUIC 流 */
bool headers_received; /**< 已接收 HEADERS 帧 */
bool data_received; /**< 已接收 DATA 帧 */
bool headers_sent; /**< 已发送 HEADERS 帧 */
bool trailers_sent; /**< 已发送 trailers */
http3_error_t error_code; /**< 流错误码 */
bool request_complete; /**< 请求接收完整 */
bool response_complete; /**< 响应发送完整 */
} http3_stream_t;
/**
* @brief HTTP/3 会话结构
*
* 管理 HTTP/3 连接状态,包含 SETTINGS 和 QPACK 上下文。
*/
typedef struct {
quic_connection_t *conn; /**< 底层 QUIC 连接 */
uint64_t max_field_section_size; /**< SETTINGS: 最大字段段大小 */
uint64_t qpack_encoder_max_capacity; /**< QPACK 编码器最大容量 */
uint64_t qpack_decoder_max_capacity; /**< QPACK 解码器最大容量 */
http3_stream_t *h3_streams[QUIC_MAX_STREAMS_PER_CONN]; /**< HTTP/3 流数组 */
uint64_t goaway_stream_id; /**< GOAWAY 流 ID */
bool settings_received; /**< 已收到客户端 SETTINGS */
bool settings_sent; /**< 已发送服务端 SETTINGS */
} http3_session_t;
/**
* @brief QPACK 编码头部字段结果
*/
typedef struct {
uint8_t *data; /**< 编码后的数据(动态分配) */
size_t len; /**< 编码后长度 */
} qpack_encoded_t;
/**
* @brief QPACK 解码头部字段结果
*/
typedef struct {
char name[HTTP_HEADER_NAME_MAX]; /**< 字段名 */
char value[HTTP_HEADER_VALUE_MAX]; /**< 字段值 */
bool valid; /**< 解码是否成功 */
} qpack_decoded_t;
/* ===== 全局初始化 / 清理 ===== */
/**
* @brief 全局 HTTP/3 初始化
*
* 初始化 QPACK 静态表等全局资源。
* 应在服务器启动前调用一次。
*
* @return true 成功false 失败
*/
bool http3_init(void);
/**
* @brief 全局 HTTP/3 清理
*
* 释放所有全局资源。
*/
void http3_cleanup(void);
/* ===== 会话管理 ===== */
/**
* @brief 创建 HTTP/3 会话
*
* 为给定的 QUIC 连接创建 HTTP/3 会话,
* 发送初始 SETTINGS 帧。
*
* @param conn QUIC 连接
* @return HTTP/3 会话,失败返回 NULL
*/
http3_session_t *http3_session_create(quic_connection_t *conn);
/**
* @brief 销毁 HTTP/3 会话
*
* 释放会话及其管理的所有 HTTP/3 流。
*
* @param session HTTP/3 会话
*/
void http3_session_destroy(http3_session_t *session);
/* ===== QUIC 连接管理 ===== */
/**
* @brief 接受新的 QUIC 连接
*
* 从 UDP socket 接收数据报,查找或创建 QUIC 连接。
*
* @param udp_fd UDP socket 描述符
* @return 新连接或已有连接NULL 表示无新连接
*/
quic_connection_t *http3_accept(cocoon_socket_t udp_fd);
/**
* @brief 创建 QUIC 连接(内部使用/测试用)
*
* @param conn_id 连接 ID
* @param udp_fd UDP socket
* @param peer_addr 对端地址
* @return 新连接,失败返回 NULL
*/
quic_connection_t *quic_connection_create(uint64_t conn_id,
cocoon_socket_t udp_fd, const struct sockaddr_storage *peer_addr);
/**
* @brief 销毁 QUIC 连接
*
* @param conn QUIC 连接
*/
void quic_connection_destroy(quic_connection_t *conn);
/**
* @brief 获取或创建 QUIC 流
*
* @param conn QUIC 连接
* @param stream_id 流 ID
* @return 流指针,失败返回 NULL
*/
quic_stream_t *quic_stream_get_or_create(quic_connection_t *conn, uint64_t stream_id);
/**
* @brief 销毁 QUIC 流
*
* @param conn 所属连接
* @param stream 要销毁的流
*/
void quic_stream_destroy(quic_connection_t *conn, quic_stream_t *stream);
/**
* @brief 查找 QUIC 流
*
* @param conn QUIC 连接
* @param stream_id 流 ID
* @return 流指针,未找到返回 NULL
*/
quic_stream_t *quic_stream_find(quic_connection_t *conn, uint64_t stream_id);
/**
* @brief 向 QUIC 流写入数据
*
* @param stream QUIC 流
* @param data 数据
* @param len 数据长度
* @return 0 成功,< 0 错误
*/
int quic_stream_write(quic_stream_t *stream, const uint8_t *data, size_t len);
/**
* @brief 从 QUIC 流读取数据
*
* @param stream QUIC 流
* @param buf 输出缓冲区
* @param len 最大读取长度
* @return 读取字节数,< 0 错误
*/
ssize_t quic_stream_read(quic_stream_t *stream, uint8_t *buf, size_t len);
/**
* @brief 设置 QUIC 流 FIN 标志
*
* @param stream QUIC 流
*/
void quic_stream_set_fin(quic_stream_t *stream);
/* ===== HTTP/3 请求/响应处理 ===== */
/**
* @brief 从 HTTP/3 流读取请求
*
* 解析 HEADERS 帧QPACK 解码)和 DATA 帧,构建 http_request_t。
*
* @param session HTTP/3 会话
* @param req 输出 HTTP 请求
* @return 成功返回流 ID>= 0< 0 表示无可用请求
*/
int64_t http3_read_request(http3_session_t *session, http_request_t *req);
/**
* @brief 发送 HTTP/3 响应
*
* 发送 HEADERS 帧QPACK 编码)和 DATA 帧。
*
* @param session HTTP/3 会话
* @param stream_id 流 ID
* @param resp HTTP 响应
* @param body 响应体数据
* @param body_len 响应体长度
* @return 0 成功,< 0 错误
*/
int http3_send_response(http3_session_t *session, uint64_t stream_id,
const http_response_t *resp,
const uint8_t *body, size_t body_len);
/**
* @brief 发送 HTTP/3 错误响应
*
* @param session HTTP/3 会话
* @param stream_id 流 ID
* @param status_code HTTP 状态码
* @param message 错误消息
*/
void http3_send_error(http3_session_t *session, uint64_t stream_id,
int status_code, const char *message);
/**
* @brief 关闭 QUIC 连接
*
* @param conn QUIC 连接
* @param error HTTP/3 错误码
*/
void http3_close_connection(quic_connection_t *conn, http3_error_t error);
/**
* @brief 发送 SETTINGS 帧
*
* @param session HTTP/3 会话
* @return 0 成功,< 0 错误
*/
int http3_send_settings(http3_session_t *session);
/**
* @brief 处理 UDP 数据报
*
* 接收并处理 UDP 数据报,分发给对应的 QUIC 连接。
*
* @param udp_fd UDP socket
* @param buf 数据报内容
* @param len 数据报长度
* @param peer_addr 对端地址
*/
void http3_process_datagram(cocoon_socket_t udp_fd,
const uint8_t *buf, size_t len,
const struct sockaddr_storage *peer_addr);
/**
* @brief 处理 HTTP/3 控制流
*
* 处理 SETTINGS、GOAWAY 等控制帧。
*
* @param session HTTP/3 会话
* @param stream QUIC 控制流
* @param data 数据
* @param len 数据长度
* @return 0 成功,< 0 错误
*/
int http3_handle_control_stream(http3_session_t *session, quic_stream_t *stream,
const uint8_t *data, size_t len);
/* ===== Variable-Length Integer 编解码 ===== */
/**
* @brief 编码 QUIC variable-length integer
*
* 2-bit prefix 指示长度:
* - 00 = 1 byte (0..63)
* - 01 = 2 bytes (0..16383)
* - 10 = 4 bytes (0..1073741823)
* - 11 = 8 bytes (0..4611686018427387903)
*
* @param value 要编码的值
* @param buf 输出缓冲区(至少 8 字节)
* @return 编码后的字节数1, 2, 4, or 8
*/
size_t http3_encode_varint(uint64_t value, uint8_t *buf);
/**
* @brief 解码 QUIC variable-length integer
*
* @param buf 输入缓冲区
* @param len 缓冲区长度
* @param value 输出值
* @return 解码后的字节数,< 0 表示错误
*/
int http3_decode_varint(const uint8_t *buf, size_t len, uint64_t *value);
/**
* @brief 计算帧头编码后的大小
*
* @param frame_type 帧类型
* @param length 帧负载长度
* @return 帧头总字节数
*/
size_t http3_frame_header_size(uint64_t frame_type, uint64_t length);
/* ===== QPACK 编码/解码 ===== */
/**
* @brief QPACK 编码头部字段
*
* 使用静态表索引或字面量编码单个头部字段。
*
* @param name 字段名
* @param value 字段值
* @param out 输出缓冲区
* @param out_cap 输出缓冲区容量
* @return 编码后字节数,< 0 表示错误
*/
int qpack_encode_header(const char *name, const char *value,
uint8_t *out, size_t out_cap);
/**
* @brief QPACK 解码头部字段
*
* @param in 输入缓冲区
* @param in_len 输入长度
* @param out 输出解码结果
* @param consumed 消耗字节数
* @return 0 成功,< 0 错误
*/
int qpack_decode_header(const uint8_t *in, size_t in_len,
qpack_decoded_t *out, size_t *consumed);
/**
* @brief QPACK 编码完整请求头
*
* 编码所有请求伪头和常规头。
*
* @param req HTTP 请求
* @param out 输出缓冲区
* @param out_cap 输出缓冲区容量
* @return 编码后字节数,< 0 错误
*/
int qpack_encode_request_headers(const http_request_t *req,
uint8_t *out, size_t out_cap);
/**
* @brief QPACK 解码完整请求头
*
* @param in 输入缓冲区
* @param in_len 输入长度
* @param req 输出 HTTP 请求
* @return 0 成功,< 0 错误
*/
int qpack_decode_request_headers(const uint8_t *in, size_t in_len,
http_request_t *req);
/**
* @brief QPACK 编码响应头
*
* @param resp HTTP 响应
* @param out 输出缓冲区
* @param out_cap 输出缓冲区容量
* @return 编码后字节数,< 0 错误
*/
int qpack_encode_response_headers(const http_response_t *resp,
uint8_t *out, size_t out_cap);
/**
* @brief QPACK 解码响应头
*
* @param in 输入缓冲区
* @param in_len 输入长度
* @param resp 输出 HTTP 响应
* @return 0 成功,< 0 错误
*/
int qpack_decode_response_headers(const uint8_t *in, size_t in_len,
http_response_t *resp);
/* ===== 帧处理辅助函数 ===== */
/**
* @brief 编码 HTTP/3 帧头
*
* 格式:[Type: varint][Length: varint]
*
* @param frame_type 帧类型
* @param length 负载长度
* @param buf 输出缓冲区(至少 16 字节)
* @return 编码后帧头字节数
*/
size_t http3_encode_frame_header(uint64_t frame_type, uint64_t length,
uint8_t *buf);
/**
* @brief 解码 HTTP/3 帧头
*
* @param buf 输入缓冲区
* @param len 缓冲区长度
* @param frame_type 输出帧类型
* @param length 输出负载长度
* @return 解码后帧头字节数,< 0 错误
*/
int http3_decode_frame_header(const uint8_t *buf, size_t len,
uint64_t *frame_type, uint64_t *length);
/**
* @brief 编码完整 HTTP/3 帧
*
* @param frame_type 帧类型
* @param payload 负载数据
* @param payload_len 负载长度
* @param buf 输出缓冲区
* @param buf_cap 缓冲区容量
* @return 编码后总字节数,< 0 错误
*/
int http3_encode_frame(uint64_t frame_type,
const uint8_t *payload, size_t payload_len,
uint8_t *buf, size_t buf_cap);
/**
* @brief 解析 HTTP/3 帧(从流缓冲区)
*
* 从 QUIC 流接收缓冲区解析完整帧。
*
* @param stream QUIC 流
* @param frame_type 输出帧类型
* @param payload 输出负载指针(指向流缓冲区内部)
* @param payload_len 输出负载长度
* @return 0 成功1 数据不足,< 0 错误
*/
int http3_parse_frame(quic_stream_t *stream,
uint64_t *frame_type,
const uint8_t **payload,
size_t *payload_len);
/* ===== 全局连接管理 ===== */
/**
* @brief 查找现有 QUIC 连接
*
* 根据连接 ID 在全局链表中查找。
*
* @param conn_id 连接 ID
* @return 连接指针,未找到返回 NULL
*/
quic_connection_t *quic_find_connection(uint64_t conn_id);
/**
* @brief 移除并清理超时连接
*
* 检查全局连接链表,关闭并释放超时的连接。
*
* @param timeout_ms 超时毫秒数
*/
void quic_cleanup_timeout_connections(uint64_t timeout_ms);
/**
* @brief 生成 64-bit 随机连接 ID
*
* @return 随机连接 ID
*/
uint64_t quic_generate_conn_id(void);
/**
* @brief 获取当前时间戳(毫秒)
*
* @return 毫秒时间戳
*/
uint64_t quic_current_time_ms(void);
/**
* @brief 发送 QUIC 数据报
*
* 通过 UDP socket 发送数据报给对端。
*
* @param conn QUIC 连接
* @param data 数据
* @param len 数据长度
* @return 0 成功,< 0 错误
*/
int quic_send_datagram(quic_connection_t *conn, const uint8_t *data, size_t len);
/* ===== 连接计数 ===== */
/**
* @brief 获取活跃 QUIC 连接数
*
* @return 活跃连接数
*/
size_t quic_get_connection_count(void);
#ifdef __cplusplus
}
#endif
#endif /* COCOON_HTTP3_H */