cocoon/grpc.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

268 lines
8.5 KiB
C
Raw Permalink 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 grpc.h - gRPC over HTTP/2 支持模块
*
* 基于现有 HTTP/2 传输层,实现 gRPC 协议支持。
* 包含 gRPC 状态码、消息帧编解码、四种 RPC 模式、trailers 格式化。
*
* gRPC message frame format:
* [1 byte: compressed flag] [4 bytes: length (big-endian)] [N bytes: payload]
*
* @author Cocoon Team
*/
#ifndef COCOON_GRPC_H
#define COCOON_GRPC_H
#include "http.h"
#include "platform.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ===== gRPC 状态码 (17 个标准状态码) ===== */
/**
* grpc_status_t - gRPC 标准状态码
*
* 参照 https://grpc.io/docs/guides/status-codes/
*/
typedef enum {
GRPC_OK = 0, /**< 成功 */
GRPC_CANCELLED = 1, /**< 操作已取消 */
GRPC_UNKNOWN = 2, /**< 未知错误 */
GRPC_INVALID_ARGUMENT = 3, /**< 无效参数 */
GRPC_DEADLINE_EXCEEDED = 4, /**< 超时 */
GRPC_NOT_FOUND = 5, /**< 未找到 */
GRPC_ALREADY_EXISTS = 6, /**< 已存在 */
GRPC_PERMISSION_DENIED = 7, /**< 权限拒绝 */
GRPC_RESOURCE_EXHAUSTED = 8, /**< 资源耗尽 */
GRPC_FAILED_PRECONDITION = 9, /**< 前置条件失败 */
GRPC_ABORTED = 10, /**< 操作已中止 */
GRPC_OUT_OF_RANGE = 11, /**< 超出范围 */
GRPC_UNIMPLEMENTED = 12, /**< 未实现 */
GRPC_INTERNAL = 13, /**< 内部错误 */
GRPC_UNAVAILABLE = 14, /**< 服务不可用 */
GRPC_DATA_LOSS = 15, /**< 数据丢失 */
GRPC_UNAUTHENTICATED = 16, /**< 未认证 */
GRPC_STATUS_MAX /**< 状态码数量上限 */
} grpc_status_t;
/* ===== gRPC 消息帧 ===== */
/**
* grpc_message_t - gRPC 长度前缀消息帧
*
* gRPC 消息格式:
* [1 byte flag] [4 bytes length (big-endian)] [length bytes payload]
* flag: 0x00 = 未压缩, 0x01 = 压缩 (protobuf 数据)
*
* payload 为 opaque 字节流,不包含 protobuf 字段级解析。
*/
typedef struct {
uint8_t compressed; /**< 压缩标志0x00=未压缩, 0x01=压缩 */
uint32_t length; /**< payload 长度 */
uint8_t *payload; /**< 消息数据(动态分配) */
} grpc_message_t;
/* ===== gRPC 请求上下文 ===== */
/**
* grpc_request_t - gRPC 请求上下文
*
* 从 HTTP/2 请求解析得到的 gRPC 调用信息。
* 包含 service/method 名、消息、元数据、流模式标记。
*/
typedef struct {
char service_name[256]; /**< 服务名(从路径 /Service/Method 解析) */
char method_name[256]; /**< 方法名 */
grpc_message_t message; /**< 请求消息帧 */
grpc_status_t status; /**< 响应状态 */
char status_message[256]; /**< 状态消息文本 */
/* 元数据 (key-value pairs) */
char metadata[16][2][256]; /**< 元数据键值对数组 */
size_t metadata_count; /**< 元数据数量 */
/* RPC 流模式标记 */
bool is_streaming; /**< 是否为流式 RPC任一方向 */
bool client_streaming; /**< 客户端流式Client Streaming / Bidi */
bool server_streaming; /**< 服务端流式Server Streaming / Bidi */
/* gRPC-Web 兼容 */
bool is_grpc_web; /**< 是否为 gRPC-Web 请求 */
} grpc_request_t;
/* ===== gRPC 流管理 ===== */
/**
* grpc_stream_t - gRPC 流状态管理
*
* 用于跟踪流式 RPC 的流生命周期。
*/
typedef struct {
uint32_t stream_id; /**< HTTP/2 流 ID */
cocoon_socket_t fd; /**< 客户端 socket */
grpc_request_t *req; /**< 关联的请求上下文 */
bool half_closed; /**< 客户端已半关闭(发送 END_STREAM */
bool closed; /**< 流已完全关闭 */
} grpc_stream_t;
/* ===== API ===== */
/**
* grpc_detect - 检测请求是否为 gRPC 请求
*
* 检查 Content-Type 是否以 "application/grpc" 开头。
* 支持 application/grpc、application/grpc+proto、application/grpc+json。
* 也支持 application/grpc-web 变体(设置 is_grpc_web 标记)。
*
* @param req HTTP 请求
* @return true 是 gRPC/gRPC-Web 请求
*/
bool grpc_detect(const http_request_t *req);
/**
* grpc_is_grpc_web - 检测请求是否为 gRPC-Web
*
* @param req HTTP 请求
* @return true 是 gRPC-Web 请求
*/
bool grpc_is_grpc_web(const http_request_t *req);
/**
* grpc_parse_request - 从 HTTP/2 请求解析 gRPC 上下文
*
* 解析 :path 伪头获取 service/method提取消息体。
* 路径格式: "/package.Service/Method" 或 "/Service/Method"
*
* @param req HTTP 请求(已解析)
* @param grpc_req 输出 gRPC 请求上下文(调用者已 zero-init
* @return 0 成功,-1 失败
*/
int grpc_parse_request(const http_request_t *req, grpc_request_t *grpc_req);
/**
* grpc_decode_message - 解码 gRPC 长度前缀消息帧
*
* 从缓冲区解码一个 gRPC 消息帧。
* 不解析 protobuf 内容,仅提取 opaque payload。
*
* @param buf 输入缓冲区
* @param len 缓冲区长度
* @param msg 输出消息结构体payload 动态分配)
* @return 成功解码的字节数(含 5 字节前缀),< 0 表示错误或数据不完整
*/
int grpc_decode_message(const uint8_t *buf, size_t len, grpc_message_t *msg);
/**
* grpc_encode_message - 编码 gRPC 长度前缀消息帧
*
* @param msg 消息结构体
* @param buf 输出缓冲区
* @param buf_size 缓冲区大小
* @return 编码后的字节数(含 5 字节前缀),< 0 表示缓冲区不足
*/
int grpc_encode_message(const grpc_message_t *msg, uint8_t *buf, size_t buf_size);
/**
* grpc_message_free - 释放 gRPC 消息中动态分配的资源
*
* @param msg 消息结构体
*/
void grpc_message_free(grpc_message_t *msg);
/**
* grpc_format_response_trailers - 格式化 gRPC trailers状态
*
* gRPC 使用 HTTP/2 trailers 传递最终状态。
* 格式: grpc-status: N\r\ngrpc-message: text\r\n
*
* @param grpc_req gRPC 请求(含 status 和 status_message
* @param buf 输出缓冲区
* @param buf_size 缓冲区大小
* @return 写入的字节数,< 0 表示缓冲区不足
*/
int grpc_format_response_trailers(const grpc_request_t *grpc_req, char *buf, size_t buf_size);
/**
* grpc_status_to_http - gRPC 状态码转 HTTP 状态码
*
* @param status gRPC 状态码
* @return 对应的 HTTP 状态码
*/
int grpc_status_to_http(grpc_status_t status);
/**
* grpc_http_to_status - HTTP 状态码转 gRPC 状态码
*
* @param http_status HTTP 状态码
* @return 对应的 gRPC 状态码
*/
grpc_status_t grpc_http_to_status(int http_status);
/**
* grpc_status_to_string - gRPC 状态码转可读字符串
*
* @param status gRPC 状态码
* @return 状态码名称字符串
*/
const char *grpc_status_to_string(grpc_status_t status);
/**
* grpc_send_unary_response - 发送 Unary RPC 响应
*
* 发送 gRPC 响应消息DATA 帧)+ trailers。
* 编码格式: [flag:1][length:4][payload]
*
* @param fd 客户端 socket
* @param grpc_req 请求上下文
* @param resp_payload 响应 protobuf payload
* @param resp_len payload 长度
* @return 成功发送的字节数,< 0 错误
*/
int grpc_send_unary_response(cocoon_socket_t fd, grpc_request_t *grpc_req,
const uint8_t *resp_payload, size_t resp_len);
/**
* grpc_send_trailers - 发送 gRPC trailers 结束流
*
* 发送 HTTP/2 trailing headers 形式的 gRPC 状态。
*
* @param fd 客户端 socket
* @param stream_id HTTP/2 流 ID
* @param status gRPC 状态码
* @param message 状态消息(可为 NULL
* @return 0 成功,< 0 错误
*/
int grpc_send_trailers(cocoon_socket_t fd, uint32_t stream_id,
grpc_status_t status, const char *message);
/**
* grpc_request_free - 释放 gRPC 请求资源
*
* 释放 grpc_request_t 中所有动态分配的内存(包括 message.payload
*
* @param grpc_req gRPC 请求上下文
*/
void grpc_request_free(grpc_request_t *grpc_req);
/**
* grpc_error_response - 发送 gRPC 错误响应
*
* 直接发送带有 grpc-status 的错误 trailers不发送消息体。
* 用于请求解析失败、方法未找到等场景。
*
* @param fd 客户端 socket
* @param stream_id HTTP/2 流 ID
* @param status gRPC 错误状态码
* @param message 错误消息(可为 NULL
*/
void grpc_error_response(cocoon_socket_t fd, uint32_t stream_id,
grpc_status_t status, const char *message);
#ifdef __cplusplus
}
#endif
#endif /* COCOON_GRPC_H */