Files
chat_rebot-connect-with-one…/c/network/http/http_rel.c
2025-11-21 18:02:37 +08:00

181 lines
5.2 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.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "network/mongoose/mongoose.h"
#include "tools/log/log.h"
#include "http_rel.h"
/* 接收状态辅助结构 */
struct recv_state {
char *request; // 完整请求字符串
int done; // 接收完成标志
int error; // 错误标志
};
static void http_recv_handler(struct mg_connection *c, int ev, void *ev_data)
{
// 从连接对象获取用户数据
struct recv_state *state = (struct recv_state *)c->fn_data;
switch (ev) {
case MG_EV_HTTP_MSG: {
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
/* 分配内存并复制完整请求(头+体) */
state->request = malloc(hm->message.len + 1);
if (state->request) {
memcpy(state->request, hm->message.buf, hm->message.len);
state->request[hm->message.len] = '\0';
} else {
state->error = 1; // 内存不足
}
state->done = 1;
break;
}
case MG_EV_CLOSE:
case MG_EV_ERROR:
state->done = 1;
break;
}
}
char *recv_http_request(int cfd)
{
struct mg_mgr mgr;
struct mg_connection *c;
struct recv_state state = {0};
/* 初始化 mongoose 管理器 */
mg_mgr_init(&mgr);
/* 将已连接的 socket 包装成 mongoose 连接 */
c = mg_wrapfd(&mgr, cfd, http_recv_handler, &state);
/* 设置 5 秒超时 */
int64_t end_time = mg_millis() + 5000;
while (!state.done && mg_millis() < end_time) {
mg_mgr_poll(&mgr, 100);
}
/* 超时处理 */
if (!state.done) {
state.error = 1;
}
/* 清理 mongoose 资源(不会关闭原始 fd */
mg_mgr_free(&mgr);
/* 出错时释放内存 */
if (state.error) {
free(state.request); // 安全释放free(NULL) 是安全的)
return NULL;
}
/* 确保返回的请求不为空 */
if (!state.request) {
return NULL;
}
return state.request;
}
/* http_get_body 无需修改,保持原样 */
const char *http_get_body(const char *buf)
{
if (!buf) return NULL;
const char *sep = strstr(buf, "\r\n\r\n");
if (!sep) return NULL;
const char *body = sep + 4;
if (*body == '\0') return NULL;
return body;
}
/**
* @brief 初始化HTTP监听socket所有错误通过logmanager记录
* @param port 监听端口
* @param logger 日志管理器实例指针
* @return 成功返回监听fd失败返回-1并记录日志
*/
int init_http_network(int port, log_manager *logger)
{
logs *log;
int fd;
/* 1. 创建socket */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
log = malloc(sizeof(logs));
// cppcheck-suppress uninitdata
snprintf(log->log, sizeof(log->log),
"[FATAL] socket() failed: %s", strerror(errno));
logger->in_log(log, logger);
return -1;
}
/* 2. 设置SO_REUSEADDR避免TIME_WAIT状态导致bind失败 */
int opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[ERROR] setsockopt(SO_REUSEADDR) on fd=%d failed: %s",
fd, strerror(errno));
logger->in_log(log, logger);
close(fd);
return -1;
}
/* 3. 设置为非阻塞模式配合epoll使用 */
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[ERROR] fcntl(F_GETFL) on fd=%d failed: %s", fd, strerror(errno));
logger->in_log(log, logger);
close(fd);
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[ERROR] fcntl(O_NONBLOCK) on fd=%d failed: %s", fd, strerror(errno));
logger->in_log(log, logger);
close(fd);
return -1;
}
/* 4. 绑定到指定端口 */
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[FATAL] bind(port %d) failed: %s (fd=%d)",
port, strerror(errno), fd);
logger->in_log(log, logger);
close(fd);
return -1;
}
/* 5. 开始监听 */
if (listen(fd, 10) == -1) {
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[FATAL] listen(fd=%d, backlog=10) failed: %s",
fd, strerror(errno));
logger->in_log(log, logger);
close(fd);
return -1;
}
/* 6. 成功日志 */
log = malloc(sizeof(logs));
snprintf(log->log, sizeof(log->log),
"[HTTP] Successfully listening on port %d (fd=%d)", port, fd);
logger->in_log(log, logger);
return fd;
}