181 lines
5.2 KiB
C
181 lines
5.2 KiB
C
#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;
|
||
}
|