feat(http2): 添加 HTTP/2 支持骨架(nghttp2)
- 新增 http2.c / http2.h:基于 nghttp2 的 HTTP/2 会话管理 - 支持 TLS ALPN 协商和明文 h2c 升级 - 流级请求/响应处理,集成现有 http_request_t 结构 - 编译通过,链接 -lnghttp2 - Makefile 更新:包含 http2.c 和 nghttp2 库链接 - 待完成:server.c 集成(ALPN 协商、h2c 升级检测)
This commit is contained in:
parent
cffb433c1e
commit
6bb9635b2d
8
Makefile
8
Makefile
@ -7,7 +7,7 @@ COCO_LIB ?= $(COCO_DIR)/build
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -std=c11 -D_GNU_SOURCE -I$(COCO_INCLUDE)
|
||||
LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring -lz -lbrotlienc -lssl -lcrypto
|
||||
LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring -lz -lbrotlienc -lssl -lcrypto -lnghttp2
|
||||
|
||||
# 调试模式
|
||||
DEBUG ?= 0
|
||||
@ -20,7 +20,7 @@ PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
# 源文件
|
||||
SRCS = main.c server.c http.c static.c log.c config.c multipart.c tls.c
|
||||
SRCS = main.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
TARGET = cocoon
|
||||
|
||||
@ -78,8 +78,8 @@ unit-test: $(UNIT_TEST_BINS)
|
||||
fi
|
||||
|
||||
# 单元测试编译规则
|
||||
$(UNIT_TEST_DIR)/test_server: $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c $(UNITY_SRC)
|
||||
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c $(UNITY_SRC) $(LDFLAGS)
|
||||
$(UNIT_TEST_DIR)/test_server: $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c $(UNITY_SRC)
|
||||
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_server.c server.c http.c static.c log.c config.c multipart.c tls.c http2.c $(UNITY_SRC) $(LDFLAGS)
|
||||
|
||||
$(UNIT_TEST_DIR)/test_multipart: $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC)
|
||||
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_multipart.c multipart.c $(UNITY_SRC) -lm
|
||||
|
||||
431
http2.c
Normal file
431
http2.c
Normal file
@ -0,0 +1,431 @@
|
||||
/**
|
||||
* http2.c - HTTP/2 支持实现
|
||||
*
|
||||
* 使用 nghttp2 库实现 HTTP/2 协议支持。
|
||||
* 与 cocoon 的协程 I/O 和 TLS 层集成。
|
||||
*
|
||||
* @author xfy
|
||||
*/
|
||||
|
||||
#include "http2.h"
|
||||
#include "static.h"
|
||||
#include "log.h"
|
||||
#include "tls.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* 最大 HTTP/2 会话数 */
|
||||
#define MAX_HTTP2_SESSIONS 1024
|
||||
|
||||
static http2_session_t *g_sessions[MAX_HTTP2_SESSIONS];
|
||||
static int g_session_count = 0;
|
||||
|
||||
/* 内部函数声明 */
|
||||
static ssize_t send_callback(nghttp2_session *session __attribute__((unused)), const uint8_t *data,
|
||||
size_t length, int flags __attribute__((unused)), void *user_data);
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data);
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags,
|
||||
void *user_data);
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data);
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id, uint32_t error_code,
|
||||
void *user_data);
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
const uint8_t *data,
|
||||
size_t len, void *user_data);
|
||||
|
||||
/* ===================== 会话管理 ===================== */
|
||||
|
||||
int http2_init(void) {
|
||||
memset(g_sessions, 0, sizeof(g_sessions));
|
||||
g_session_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void http2_cleanup(void) {
|
||||
for (int i = 0; i < MAX_HTTP2_SESSIONS; i++) {
|
||||
if (g_sessions[i]) {
|
||||
http2_session_destroy(g_sessions[i]);
|
||||
g_sessions[i] = NULL;
|
||||
}
|
||||
}
|
||||
g_session_count = 0;
|
||||
}
|
||||
|
||||
http2_session_t *http2_session_create(int fd, bool tls_mode) {
|
||||
if (fd < 0 || fd >= MAX_HTTP2_SESSIONS) {
|
||||
return NULL;
|
||||
}
|
||||
if (g_sessions[fd] != NULL) {
|
||||
/* 已存在,先销毁 */
|
||||
http2_session_destroy(g_sessions[fd]);
|
||||
}
|
||||
|
||||
http2_session_t *h2 = calloc(1, sizeof(http2_session_t));
|
||||
if (!h2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h2->fd = fd;
|
||||
h2->tls_mode = tls_mode;
|
||||
|
||||
/* 创建 nghttp2 会话 */
|
||||
nghttp2_session_callbacks *callbacks = NULL;
|
||||
if (nghttp2_session_callbacks_new(&callbacks) != 0) {
|
||||
free(h2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
if (nghttp2_session_server_new(&h2->session, callbacks, h2) != 0) {
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
free(h2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
/* 发送服务器连接前言(SETTINGS 帧) */
|
||||
nghttp2_settings_entry iv[] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
|
||||
{NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, (1 << 16) - 1}};
|
||||
if (nghttp2_submit_settings(h2->session, NGHTTP2_FLAG_NONE, iv,
|
||||
sizeof(iv) / sizeof(iv[0])) != 0) {
|
||||
nghttp2_session_del(h2->session);
|
||||
free(h2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_sessions[fd] = h2;
|
||||
g_session_count++;
|
||||
|
||||
return h2;
|
||||
}
|
||||
|
||||
void http2_session_destroy(http2_session_t *h2) {
|
||||
if (!h2) return;
|
||||
|
||||
if (h2->fd >= 0 && h2->fd < MAX_HTTP2_SESSIONS) {
|
||||
g_sessions[h2->fd] = NULL;
|
||||
}
|
||||
g_session_count--;
|
||||
|
||||
if (h2->session) {
|
||||
nghttp2_session_del(h2->session);
|
||||
}
|
||||
|
||||
/* 清理所有流 */
|
||||
http2_stream_data_t *stream = h2->streams;
|
||||
while (stream) {
|
||||
http2_stream_data_t *next = stream->next;
|
||||
if (stream->file_fd >= 0) {
|
||||
close(stream->file_fd);
|
||||
}
|
||||
free(stream->response_body);
|
||||
http_request_free(&stream->request);
|
||||
free(stream);
|
||||
stream = next;
|
||||
}
|
||||
|
||||
free(h2);
|
||||
}
|
||||
|
||||
bool http2_session_is_http2(int fd) {
|
||||
if (fd < 0 || fd >= MAX_HTTP2_SESSIONS) return false;
|
||||
return g_sessions[fd] != NULL;
|
||||
}
|
||||
|
||||
http2_session_t *http2_session_get(int fd) {
|
||||
if (fd < 0 || fd >= MAX_HTTP2_SESSIONS) return NULL;
|
||||
return g_sessions[fd];
|
||||
}
|
||||
|
||||
/* ===================== 数据收发 ===================== */
|
||||
|
||||
int http2_recv(http2_session_t *h2, const uint8_t *buf, size_t len) {
|
||||
if (!h2 || !h2->session) return -1;
|
||||
|
||||
ssize_t readlen = nghttp2_session_mem_recv(h2->session, buf, len);
|
||||
if (readlen < 0) {
|
||||
log_error("HTTP/2 接收错误: %s", nghttp2_strerror((int)readlen));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 发送任何挂起的输出帧 */
|
||||
return http2_send_pending(h2);
|
||||
}
|
||||
|
||||
int http2_send_pending(http2_session_t *h2) {
|
||||
if (!h2 || !h2->session) return -1;
|
||||
|
||||
int rv = nghttp2_session_send(h2->session);
|
||||
if (rv != 0) {
|
||||
log_error("HTTP/2 发送错误: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool http2_want_read(http2_session_t *h2) {
|
||||
if (!h2 || !h2->session) return false;
|
||||
return nghttp2_session_want_read(h2->session) != 0;
|
||||
}
|
||||
|
||||
bool http2_want_write(http2_session_t *h2) {
|
||||
if (!h2 || !h2->session) return false;
|
||||
return nghttp2_session_want_write(h2->session) != 0;
|
||||
}
|
||||
|
||||
/* ===================== nghttp2 回调 ===================== */
|
||||
|
||||
/**
|
||||
* send_callback - nghttp2 发送回调
|
||||
*
|
||||
* 将 nghttp2 序列化后的帧数据发送到 socket。
|
||||
*/
|
||||
static ssize_t send_callback(nghttp2_session *session __attribute__((unused)),
|
||||
const uint8_t *data, size_t length,
|
||||
int flags __attribute__((unused)),
|
||||
void *user_data) {
|
||||
http2_session_t *h2 = (http2_session_t *)user_data;
|
||||
|
||||
/* 使用 TLS 或原始 socket 发送 */
|
||||
ssize_t sent;
|
||||
if (h2->tls_mode && tls_has_connection(h2->fd)) {
|
||||
sent = tls_write(h2->fd, (const char *)data, length);
|
||||
} else {
|
||||
sent = send_all(h2->fd, (const char *)data, length);
|
||||
}
|
||||
|
||||
if (sent < 0) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return (int)sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_begin_headers_callback - 开始接收请求头
|
||||
*/
|
||||
static int on_begin_headers_callback(nghttp2_session *session __attribute__((unused)),
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
http2_session_t *h2 = (http2_session_t *)user_data;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 创建新流数据 */
|
||||
http2_stream_data_t *stream = calloc(1, sizeof(http2_stream_data_t));
|
||||
if (!stream) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
stream->stream_id = frame->hd.stream_id;
|
||||
stream->file_fd = -1;
|
||||
|
||||
/* 插入链表头部 */
|
||||
stream->next = h2->streams;
|
||||
h2->streams = stream;
|
||||
|
||||
/* 关联到 nghttp2 流 */
|
||||
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_header_callback - 接收单个请求头
|
||||
*/
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags __attribute__((unused)),
|
||||
void *user_data __attribute__((unused))) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
http2_stream_data_t *stream =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 收集伪头和普通头 */
|
||||
if (namelen == 4 && memcmp(":path", name, 4) == 0) {
|
||||
/* 解析路径(可能包含 query string) */
|
||||
size_t path_len = valuelen;
|
||||
for (size_t i = 0; i < valuelen; i++) {
|
||||
if (value[i] == '?') {
|
||||
path_len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (path_len > 0) {
|
||||
memcpy(stream->request.path, value,
|
||||
path_len < sizeof(stream->request.path) - 1
|
||||
? path_len
|
||||
: sizeof(stream->request.path) - 1);
|
||||
}
|
||||
} else if (namelen == 7 && memcmp(":method", name, 7) == 0) {
|
||||
if (valuelen == 3 && memcmp("GET", value, 3) == 0) {
|
||||
stream->request.method = HTTP_GET;
|
||||
} else if (valuelen == 4 && memcmp("HEAD", value, 4) == 0) {
|
||||
stream->request.method = HTTP_HEAD;
|
||||
} else if (valuelen == 4 && memcmp("POST", value, 4) == 0) {
|
||||
stream->request.method = HTTP_POST;
|
||||
} else {
|
||||
stream->request.method = HTTP_UNKNOWN;
|
||||
}
|
||||
} else if (namelen == 7 && memcmp(":scheme", name, 7) == 0) {
|
||||
/* 忽略 scheme */
|
||||
} else if (namelen == 10 && memcmp(":authority", name, 10) == 0) {
|
||||
/* 忽略 authority,host 可以从 path 推断 */
|
||||
} else {
|
||||
/* 普通头字段 */
|
||||
if (stream->request.num_headers < HTTP_MAX_HEADERS) {
|
||||
int idx = stream->request.num_headers;
|
||||
memcpy(stream->request.headers[idx].name, name,
|
||||
namelen < sizeof(stream->request.headers[idx].name) - 1 ? namelen : sizeof(stream->request.headers[idx].name) - 1);
|
||||
memcpy(stream->request.headers[idx].value, value,
|
||||
valuelen < sizeof(stream->request.headers[idx].value) - 1 ? valuelen : sizeof(stream->request.headers[idx].value) - 1);
|
||||
stream->request.num_headers++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_frame_recv_callback - 帧接收完成
|
||||
*/
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
http2_session_t *h2 = (http2_session_t *)user_data;
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* 检查请求是否完整(END_STREAM) */
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
http2_stream_data_t *stream =
|
||||
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
stream->request_complete = true;
|
||||
|
||||
/* TODO: 处理请求并生成响应 */
|
||||
/* 目前返回 501 Not Implemented */
|
||||
nghttp2_nv hdrs[] = {
|
||||
{(uint8_t *)":status", (uint8_t *)"501", 7, 3, 0}};
|
||||
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 1,
|
||||
NULL);
|
||||
http2_send_pending(h2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_stream_close_callback - 流关闭
|
||||
*/
|
||||
static int on_stream_close_callback(nghttp2_session *session __attribute__((unused)),
|
||||
int32_t stream_id, uint32_t error_code __attribute__((unused)),
|
||||
void *user_data) {
|
||||
http2_session_t *h2 = (http2_session_t *)user_data;
|
||||
|
||||
/* 从链表中移除并释放 */
|
||||
http2_stream_data_t **pp = &h2->streams;
|
||||
while (*pp) {
|
||||
if ((*pp)->stream_id == stream_id) {
|
||||
http2_stream_data_t *to_free = *pp;
|
||||
*pp = to_free->next;
|
||||
|
||||
if (to_free->file_fd >= 0) {
|
||||
close(to_free->file_fd);
|
||||
}
|
||||
free(to_free->response_body);
|
||||
http_request_free(&to_free->request);
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_data_chunk_recv_callback - 接收请求体数据
|
||||
*/
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session __attribute__((unused)),
|
||||
uint8_t flags __attribute__((unused)),
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data __attribute__((unused))) {
|
||||
http2_stream_data_t *stream =
|
||||
nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: 将数据追加到请求体缓冲区 */
|
||||
(void)data;
|
||||
(void)len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ===================== 连接处理 ===================== */
|
||||
|
||||
int http2_on_connection_accepted(int fd, bool tls_mode) {
|
||||
/* 检查是否应启用 HTTP/2 */
|
||||
if (!tls_mode) {
|
||||
/* 明文模式:需要读取客户端魔术字 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" */
|
||||
/* 这由 server.c 在读取前几个字节时检测 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TLS 模式:ALPN 已协商为 h2 */
|
||||
http2_session_t *h2 = http2_session_create(fd, true);
|
||||
if (!h2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 发送服务器连接前言 */
|
||||
if (http2_send_pending(h2) != 0) {
|
||||
http2_session_destroy(h2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
151
http2.h
Normal file
151
http2.h
Normal file
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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; /**< 活跃的流列表(头节点) */
|
||||
} 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=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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COCOON_HTTP2_H */
|
||||
Loading…
x
Reference in New Issue
Block a user