- 新增 proxy_data_source_read_callback:实时从后端读取数据流式转发 - http2_serve_proxy 重构:解析响应头后立即发送,body 通过 DATA 帧实时转发 - 解除 64KB 响应缓冲区限制,支持任意大小响应体 - 流结构新增代理字段(backend_fd/tls_conn/eof/close_backend/backend) - 已缓存 body(recv_headers 预读部分)优先发送,再续读后端 - 连接池归还/关闭自动处理
207 lines
6.1 KiB
C
207 lines
6.1 KiB
C
/**
|
||
* http2.h - HTTP/2 支持头文件
|
||
*
|
||
* 使用 nghttp2 库实现 HTTP/2 协议支持。
|
||
* 支持 TLS ALPN 协商和明文 h2c 升级。
|
||
*
|
||
* @author xfy
|
||
*/
|
||
|
||
#ifndef COCOON_HTTP2_H
|
||
#define COCOON_HTTP2_H
|
||
|
||
#include "cocoon.h"
|
||
#include "proxy.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 压缩 */
|
||
cocoon_proxy_config_t *proxy_config; /**< 反向代理配置 */
|
||
struct sockaddr_storage *client_addr; /**< 客户端地址 */
|
||
cocoon_config_t *server_config; /**< 服务器配置(用于虚拟主机匹配) */
|
||
} 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; /**< 已发送的响应体字节数 */
|
||
/* 流式代理转发字段 */
|
||
cocoon_socket_t proxy_backend_fd; /**< 代理后端 socket fd */
|
||
struct proxy_tls_conn *proxy_tls_conn; /**< 代理后端 TLS 连接 */
|
||
bool proxy_use_https; /**< 后端是否使用 HTTPS */
|
||
bool proxy_eof; /**< 后端数据是否读取完毕 */
|
||
cocoon_proxy_backend_t *proxy_backend; /**< 代理后端指针(用于连接池归还) */
|
||
bool proxy_close_backend; /**< 后端是否要求关闭连接 */
|
||
} 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=TLS,false=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 proxy_config 反向代理配置(NULL 表示不启用)
|
||
* @param client_addr 客户端地址(NULL 表示不启用)
|
||
*/
|
||
void http2_session_set_proxy_config(http2_session_t *h2, cocoon_proxy_config_t *proxy_config, struct sockaddr_storage *client_addr);
|
||
|
||
/**
|
||
* http2_session_set_context - 设置会话的服务上下文
|
||
*
|
||
* 传入 root_dir 和压缩配置,供后续静态文件服务使用。
|
||
*
|
||
* @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);
|
||
|
||
/**
|
||
* http2_session_set_server_config - 设置服务器配置指针
|
||
*
|
||
* 用于虚拟主机匹配。
|
||
*
|
||
* @param h2 会话对象
|
||
* @param server_config 服务器配置指针
|
||
*/
|
||
void http2_session_set_server_config(http2_session_t *h2, cocoon_config_t *server_config);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* COCOON_HTTP2_H */
|