新增 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 头文件, 确保无需外部依赖即可编译。
268 lines
8.5 KiB
C
268 lines
8.5 KiB
C
/**
|
||
* @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 */
|