cocoon/http2.h
xfy911 a93e9f32f5 feat(h2c): 添加明文 HTTP/2 升级支持
- server.c: 检测 PRI 魔术字直接连接 + Upgrade: h2c 协商
- server.c: 101 Switching Protocols 响应,正确会话生命周期管理
- http2.h/c: 新增 http2_session_upgrade() 注册 stream 1
- http2.c: 修复 nghttp2_session_upgrade2 head_request 参数误用
- tests: 新增 4 项 h2c 集成测试(prior knowledge、Upgrade、404)
- plan: 标记 h2c 完成,更新测试计数 52 项
2026-06-04 17:56:44 +08:00

176 lines
4.7 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.

/**
* http2.h - HTTP/2 支持头文件
*
* 使用 nghttp2 库实现 HTTP/2 协议支持。
* 支持 TLS ALPN 协商和明文 h2c 升级。
*
* @author xfy
*/
#ifndef COCOON_HTTP2_H
#define COCOON_HTTP2_H
#include "cocoon.h"
#include "http.h"
#include <nghttp2/nghttp2.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* http2_session_t - HTTP/2 会话状态
*
* 每个启用了 HTTP/2 的连接拥有一个此结构体。
*/
typedef struct {
nghttp2_session *session; /**< nghttp2 会话对象 */
int fd; /**< 底层 socket fd */
bool tls_mode; /**< 是否通过 TLS ALPN 协商 */
struct http2_stream_data *streams; /**< 活跃的流列表(头节点) */
const char *root_dir; /**< 静态资源根目录 */
bool gzip_enabled; /**< 是否启用 gzip 压缩 */
bool brotli_enabled; /**< 是否启用 brotli 压缩 */
} http2_session_t;
/**
* http2_stream_data - HTTP/2 流级数据
*
* 每个请求流对应一个此结构体。
*/
typedef struct http2_stream_data {
struct http2_stream_data *next;
int32_t stream_id; /**< HTTP/2 流 ID */
http_request_t request; /**< 解析后的 HTTP 请求 */
bool request_complete; /**< 请求是否接收完整 */
int file_fd; /**< 响应文件 fd-1 表示无) */
char *response_body; /**< 响应体(用于动态内容) */
size_t response_len; /**< 响应体长度 */
size_t response_sent; /**< 已发送的响应体字节数 */
} http2_stream_data_t;
/**
* http2_init - 初始化 HTTP/2 全局状态
*
* 应在服务器启动前调用一次。
*
* @return 0 成功,-1 失败
*/
int http2_init(void);
/**
* http2_cleanup - 清理 HTTP/2 全局状态
*/
void http2_cleanup(void);
/**
* http2_session_create - 为连接创建 HTTP/2 会话
*
* @param fd socket 文件描述符
* @param tls_mode 是否通过 TLS ALPN 协商true=TLSfalse=h2c
* @return 新会话对象,失败返回 NULL
*/
http2_session_t *http2_session_create(int fd, bool tls_mode);
/**
* http2_session_destroy - 销毁 HTTP/2 会话
*
* @param h2 会话对象
*/
void http2_session_destroy(http2_session_t *h2);
/**
* http2_session_is_http2 - 判断连接是否已升级为 HTTP/2
*
* @param fd socket 文件描述符
* @return true 是 HTTP/2 连接
*/
bool http2_session_is_http2(int fd);
/**
* http2_session_get - 获取 fd 对应的 HTTP/2 会话
*
* @param fd socket 文件描述符
* @return 会话对象NULL 表示非 HTTP/2 连接
*/
http2_session_t *http2_session_get(int fd);
/**
* http2_recv - 接收并处理客户端数据
*
* 将读取的数据喂给 nghttp2 库,触发回调处理请求。
*
* @param h2 会话对象
* @param buf 接收缓冲区
* @param len 数据长度
* @return 0 成功,-1 错误(应关闭连接)
*/
int http2_recv(http2_session_t *h2, const uint8_t *buf, size_t len);
/**
* http2_send_pending - 发送挂起的 HTTP/2 帧
*
* 应在 socket 可写时调用。
*
* @param h2 会话对象
* @return 0 成功,-1 错误
*/
int http2_send_pending(http2_session_t *h2);
/**
* http2_want_read - 检查 nghttp2 是否还需要读取数据
*
* @param h2 会话对象
* @return true 需要继续读取
*/
bool http2_want_read(http2_session_t *h2);
/**
* http2_want_write - 检查 nghttp2 是否还需要写入数据
*
* @param h2 会话对象
* @return true 需要继续写入
*/
bool http2_want_write(http2_session_t *h2);
/**
* http2_on_connection_accepted - 新连接接受后的处理
*
* 对于 TLS 连接,在 TLS 握手完成后调用;
* 对于明文连接,在读取到客户端魔术字后调用。
*
* @param fd socket 文件描述符
* @param tls_mode 是否通过 TLS ALPN 协商
* @return 0 成功,-1 失败(应关闭连接)
*/
int http2_on_connection_accepted(int fd, bool tls_mode);
/**
* http2_session_upgrade - 处理 h2c 升级请求
*
* 注册升级流stream_id=1并提交静态文件响应。
* 调用前应先创建会话并发送服务器 SETTINGS 前言。
*
* @param h2 HTTP/2 会话
* @param req HTTP/1.1 升级请求(会被复制)
* @return 0 成功,-1 失败
*/
int http2_session_upgrade(http2_session_t *h2, const http_request_t *req);
/**
*
* @param h2 会话对象
* @param root_dir 静态资源根目录
* @param gzip_enabled 是否启用 gzip
* @param brotli_enabled 是否启用 brotli
*/
void http2_session_set_context(http2_session_t *h2, const char *root_dir, bool gzip_enabled, bool brotli_enabled);
#ifdef __cplusplus
}
#endif
#endif /* COCOON_HTTP2_H */