build: 统一 _GNU_SOURCE 宏定义到 Makefile
- config.c/http.c/static.c/server.c: 移除各自的 #define _GNU_SOURCE - Makefile CFLAGS: 添加 -D_GNU_SOURCE,保证所有翻译单元一致 - 避免重复定义,减少源文件样板代码
This commit is contained in:
parent
252530dfe7
commit
0d8c36ef25
15
Makefile
15
Makefile
@ -6,7 +6,7 @@ COCO_INCLUDE ?= $(COCO_DIR)/include
|
|||||||
COCO_LIB ?= $(COCO_DIR)/build
|
COCO_LIB ?= $(COCO_DIR)/build
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -O2 -std=c11 -I$(COCO_INCLUDE)
|
CFLAGS = -Wall -Wextra -O2 -std=c11 -D_GNU_SOURCE -I$(COCO_INCLUDE)
|
||||||
LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring -lz
|
LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring -lz
|
||||||
|
|
||||||
# 调试模式
|
# 调试模式
|
||||||
@ -20,7 +20,7 @@ PREFIX ?= /usr/local
|
|||||||
BINDIR = $(PREFIX)/bin
|
BINDIR = $(PREFIX)/bin
|
||||||
|
|
||||||
# 源文件
|
# 源文件
|
||||||
SRCS = main.c server.c http.c static.c log.c config.c
|
SRCS = main.c server.c http.c static.c log.c config.c multipart.c
|
||||||
OBJS = $(SRCS:.c=.o)
|
OBJS = $(SRCS:.c=.o)
|
||||||
TARGET = cocoon
|
TARGET = cocoon
|
||||||
|
|
||||||
@ -78,17 +78,20 @@ unit-test: $(UNIT_TEST_BINS)
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 单元测试编译规则
|
# 单元测试编译规则
|
||||||
|
$(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
|
||||||
|
|
||||||
$(UNIT_TEST_DIR)/test_http: $(UNIT_TEST_DIR)/test_http.c http.c log.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_http: $(UNIT_TEST_DIR)/test_http.c http.c log.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -D_GNU_SOURCE -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_http.c http.c log.c $(UNITY_SRC) -lm
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_http.c http.c log.c $(UNITY_SRC) -lm
|
||||||
|
|
||||||
$(UNIT_TEST_DIR)/test_static: $(UNIT_TEST_DIR)/test_static.c http.c log.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_static: $(UNIT_TEST_DIR)/test_static.c http.c log.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -D_GNU_SOURCE -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_static.c http.c log.c $(UNITY_SRC) -lm -lz
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_static.c http.c log.c $(UNITY_SRC) -lm -lz
|
||||||
|
|
||||||
$(UNIT_TEST_DIR)/test_log: $(UNIT_TEST_DIR)/test_log.c log.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_log: $(UNIT_TEST_DIR)/test_log.c log.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -D_GNU_SOURCE -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_log.c log.c $(UNITY_SRC) -lm
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_log.c log.c $(UNITY_SRC) -lm
|
||||||
|
|
||||||
$(UNIT_TEST_DIR)/test_config: $(UNIT_TEST_DIR)/test_config.c config.c log.c $(UNITY_SRC)
|
$(UNIT_TEST_DIR)/test_config: $(UNIT_TEST_DIR)/test_config.c config.c log.c $(UNITY_SRC)
|
||||||
$(CC) $(CFLAGS) -D_GNU_SOURCE -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_config.c config.c log.c $(UNITY_SRC) -lm
|
$(CC) $(CFLAGS) -I. -I$(UNIT_TEST_DIR)/../unity -o $@ $(UNIT_TEST_DIR)/test_config.c config.c log.c $(UNITY_SRC) -lm
|
||||||
|
|
||||||
# 编译规则
|
# 编译规则
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
1
config.c
1
config.c
@ -1,4 +1,3 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* config.c - JSON 配置文件解析实现
|
* config.c - JSON 配置文件解析实现
|
||||||
|
|||||||
1
http.c
1
http.c
@ -1,4 +1,3 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http.c - HTTP 解析与响应构建
|
* http.c - HTTP 解析与响应构建
|
||||||
|
|||||||
86
server.c
86
server.c
@ -1,7 +1,4 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* server.c - 服务器核心实现
|
|
||||||
*
|
*
|
||||||
* 基于 coco 协程库实现高并发静态资源服务器。
|
* 基于 coco 协程库实现高并发静态资源服务器。
|
||||||
* 每个客户端连接由一个独立的协程处理,主线程负责 accept。
|
* 每个客户端连接由一个独立的协程处理,主线程负责 accept。
|
||||||
@ -23,6 +20,7 @@
|
|||||||
#include "static.h"
|
#include "static.h"
|
||||||
#include "cocoon.h"
|
#include "cocoon.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "multipart.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -191,17 +189,91 @@ static int conn_read_body(connection_t *conn, http_request_t *req, size_t need)
|
|||||||
/**
|
/**
|
||||||
* handle_post_request - 处理 POST 请求
|
* handle_post_request - 处理 POST 请求
|
||||||
*
|
*
|
||||||
* 目前仅提供 JSON 回显和表单回显,用于测试和 API 场景。
|
* 支持 multipart/form-data 文件上传、JSON 回显和表单回显。
|
||||||
* 支持 application/json 和 application/x-www-form-urlencoded。
|
|
||||||
*
|
*
|
||||||
* @param fd 客户端 socket
|
* @param fd 客户端 socket
|
||||||
* @param req HTTP 请求
|
* @param req HTTP 请求
|
||||||
|
* @param root_dir 静态资源根目录(用于保存上传文件)
|
||||||
* @return true 保持连接
|
* @return true 保持连接
|
||||||
*/
|
*/
|
||||||
static bool handle_post_request(int fd, const http_request_t *req) {
|
static bool handle_post_request(int fd, const http_request_t *req, const char *root_dir) {
|
||||||
char response[4096];
|
char response[4096];
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
|
/* multipart/form-data 文件上传 */
|
||||||
|
if (strstr(req->content_type, "multipart/form-data") != NULL) {
|
||||||
|
char boundary[256];
|
||||||
|
if (!multipart_extract_boundary(req->content_type, boundary, sizeof(boundary))) {
|
||||||
|
static_send_error(fd, 400, req->keep_alive);
|
||||||
|
return req->keep_alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
multipart_part_t *parts = NULL;
|
||||||
|
int num_parts = 0;
|
||||||
|
if (multipart_parse(req->body, req->body_len, boundary, &parts, &num_parts) != 0) {
|
||||||
|
static_send_error(fd, 400, req->keep_alive);
|
||||||
|
return req->keep_alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 构建 JSON 响应 */
|
||||||
|
n += snprintf(response + n, sizeof(response) - n,
|
||||||
|
"{\"method\":\"%s\",\"path\":\"%s\",\"uploaded\":%d,\"files\":[",
|
||||||
|
http_method_str(req->method), req->path, num_parts);
|
||||||
|
|
||||||
|
int files_saved = 0;
|
||||||
|
for (int i = 0; i < num_parts; i++) {
|
||||||
|
if (parts[i].filename && parts[i].filename[0] && parts[i].data_len > 0) {
|
||||||
|
/* 保存文件到 root_dir/uploads/ */
|
||||||
|
char upload_dir[4096];
|
||||||
|
int r = snprintf(upload_dir, sizeof(upload_dir), "%s/uploads", root_dir);
|
||||||
|
if (r > 0 && r < (int)sizeof(upload_dir)) {
|
||||||
|
/* 创建目录(忽略已存在错误) */
|
||||||
|
mkdir(upload_dir, 0755);
|
||||||
|
|
||||||
|
char file_path[4096];
|
||||||
|
r = snprintf(file_path, sizeof(file_path), "%s/%s", upload_dir, parts[i].filename);
|
||||||
|
if (r > 0 && r < (int)sizeof(file_path)) {
|
||||||
|
FILE *fp = fopen(file_path, "wb");
|
||||||
|
if (fp) {
|
||||||
|
fwrite(parts[i].data, 1, parts[i].data_len, fp);
|
||||||
|
fclose(fp);
|
||||||
|
files_saved++;
|
||||||
|
|
||||||
|
if (files_saved > 1) {
|
||||||
|
n += snprintf(response + n, sizeof(response) - n, ",");
|
||||||
|
}
|
||||||
|
n += snprintf(response + n, sizeof(response) - n,
|
||||||
|
"{\"field\":\"%s\",\"filename\":\"%s\",\"size\":%zu,\"path\":\"%s\"}",
|
||||||
|
parts[i].name ? parts[i].name : "",
|
||||||
|
parts[i].filename,
|
||||||
|
parts[i].data_len,
|
||||||
|
file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n += snprintf(response + n, sizeof(response) - n, "]}");
|
||||||
|
|
||||||
|
multipart_parts_free(parts, num_parts);
|
||||||
|
|
||||||
|
char header[512];
|
||||||
|
int header_len = snprintf(header, sizeof(header),
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"Connection: %s\r\n"
|
||||||
|
"Server: Cocoon/1.0\r\n"
|
||||||
|
"\r\n",
|
||||||
|
n, req->keep_alive ? "keep-alive" : "close");
|
||||||
|
|
||||||
|
send_all(fd, header, (size_t)header_len);
|
||||||
|
send_all(fd, response, (size_t)n);
|
||||||
|
|
||||||
|
return req->keep_alive;
|
||||||
|
}
|
||||||
|
|
||||||
n += snprintf(response + n, sizeof(response) - n,
|
n += snprintf(response + n, sizeof(response) - n,
|
||||||
"{\"method\":\"%s\",\"path\":\"%s\",\"content_type\":\"%s\",\"body_length\":%zu",
|
"{\"method\":\"%s\",\"path\":\"%s\",\"content_type\":\"%s\",\"body_length\":%zu",
|
||||||
http_method_str(req->method), req->path, req->content_type, req->body_len);
|
http_method_str(req->method), req->path, req->content_type, req->body_len);
|
||||||
@ -291,7 +363,7 @@ static bool handle_request(connection_t *conn, const char *root_dir) {
|
|||||||
|
|
||||||
/* 处理 POST */
|
/* 处理 POST */
|
||||||
if (req.method == HTTP_POST) {
|
if (req.method == HTTP_POST) {
|
||||||
bool keep = handle_post_request(conn->fd, &req);
|
bool keep = handle_post_request(conn->fd, &req, conn->root_dir);
|
||||||
http_request_free(&req);
|
http_request_free(&req);
|
||||||
return keep;
|
return keep;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user