feat(http2): 集成 HTTP/2 到服务器主循环

- server.c: 添加 handle_http2() 函数,在 client_handler 中检测 HTTP/2 连接
- TLS ALPN 协商后自动创建 HTTP/2 会话
- HTTP/2 连接走独立处理路径(handle_http2),支持帧读写
- 连接清理时销毁 HTTP/2 会话
- tls.c: 新增 tls_negotiated_http2(),检查 ALPN 是否协商为 h2
- tls.h: 添加 tls_negotiated_http2() 声明
- 编译通过,39 项集成测试 + 127 单元测试全部通过
This commit is contained in:
xfy911 2026-06-04 14:43:39 +08:00
parent 42879cdafd
commit bc7505dc02
3 changed files with 96 additions and 0 deletions

View File

@ -22,6 +22,7 @@
#include "log.h" #include "log.h"
#include "multipart.h" #include "multipart.h"
#include "tls.h" #include "tls.h"
#include "http2.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -510,12 +511,72 @@ static void conn_cancel_timer(connection_t *conn) {
* *
* @param arg connection_t* * @param arg connection_t*
*/ */
/**
* handle_http2 - HTTP/2
*
* 使 nghttp2 HTTP/2
*
* @param conn
*/
static void handle_http2(connection_t *conn) {
http2_session_t *h2 = http2_session_get(conn->fd);
if (!h2) return;
uint32_t timeout_ms = conn->timeout_ms > 0 ? conn->timeout_ms : CONN_TIMEOUT_MS;
while (!conn->closed && http2_want_read(h2)) {
/* 读取数据 */
char buf[8192];
ssize_t n;
if (tls_has_connection(conn->fd)) {
n = tls_read(conn->fd, buf, sizeof(buf));
} else if (coco_sched_get_current() != NULL) {
n = coco_read(conn->fd, buf, sizeof(buf));
} else {
n = read(conn->fd, buf, sizeof(buf));
}
if (n > 0) {
conn_reset_timer(conn, timeout_ms);
if (http2_recv(h2, (const uint8_t *)buf, (size_t)n) != 0) {
log_debug("HTTP/2 接收错误 fd=%d", conn->fd);
break;
}
} else if (n == 0) {
break; /* 对端关闭 */
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
continue;
}
break;
}
/* 发送挂起的帧 */
if (http2_want_write(h2)) {
if (http2_send_pending(h2) != 0) {
break;
}
}
}
http2_session_destroy(h2);
}
static void client_handler(void *arg) { static void client_handler(void *arg) {
connection_t *conn = (connection_t *)arg; connection_t *conn = (connection_t *)arg;
if (!conn) return; if (!conn) return;
conn->coro = coco_self(); conn->coro = coco_self();
/* 检查是否是 HTTP/2 连接 */
if (http2_session_is_http2(conn->fd)) {
handle_http2(conn);
conn_cancel_timer(conn);
close_connection(conn);
free(conn);
return;
}
/* 启动空闲定时器 */ /* 启动空闲定时器 */
uint32_t timeout_ms = conn->timeout_ms > 0 ? conn->timeout_ms : CONN_TIMEOUT_MS; uint32_t timeout_ms = conn->timeout_ms > 0 ? conn->timeout_ms : CONN_TIMEOUT_MS;
conn->timer = coco_timer(timeout_ms, conn_timeout_handler, conn); conn->timer = coco_timer(timeout_ms, conn_timeout_handler, conn);
@ -631,11 +692,22 @@ static void accept_loop(void *arg) {
close(client_fd); close(client_fd);
continue; continue;
} }
/* 检查 ALPN 协商结果,创建 HTTP/2 会话 */
if (tls_negotiated_http2(client_fd)) {
log_debug("fd=%d ALPN 协商为 HTTP/2", client_fd);
if (http2_on_connection_accepted(client_fd, true) != 0) {
log_warn("HTTP/2 会话创建失败 fd=%d", client_fd);
close(client_fd);
continue;
}
}
} }
/* 创建连接上下文 */ /* 创建连接上下文 */
connection_t *conn = (connection_t *)calloc(1, sizeof(connection_t)); connection_t *conn = (connection_t *)calloc(1, sizeof(connection_t));
if (!conn) { if (!conn) {
http2_session_t *h2 = http2_session_get(client_fd);
if (h2) http2_session_destroy(h2);
close(client_fd); close(client_fd);
continue; continue;
} }

14
tls.c
View File

@ -323,3 +323,17 @@ void tls_close(int fd) {
bool tls_has_connection(int fd) { bool tls_has_connection(int fd) {
return tls_lookup(fd) != NULL; return tls_lookup(fd) != NULL;
} }
bool tls_negotiated_http2(int fd) {
tls_conn_t *t = tls_lookup(fd);
if (!t || !t->ssl) return false;
const unsigned char *alpn = NULL;
unsigned int alpn_len = 0;
SSL_get0_alpn_selected(t->ssl, &alpn, &alpn_len);
if (alpn_len == 2 && memcmp(alpn, "h2", 2) == 0) {
return true;
}
return false;
}

10
tls.h
View File

@ -89,4 +89,14 @@ void tls_close(int fd);
*/ */
bool tls_has_connection(int fd); bool tls_has_connection(int fd);
/**
* tls_negotiated_http2 - ALPN HTTP/2
*
* TLS
*
* @param fd socket
* @return true h2
*/
bool tls_negotiated_http2(int fd);
#endif /* COCOON_TLS_H */ #endif /* COCOON_TLS_H */