From 413288d83781578c883d5c444723ce2ecaf8d474 Mon Sep 17 00:00:00 2001 From: xfy911 Date: Wed, 3 Jun 2026 17:30:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(http):=20=E6=B7=BB=E5=8A=A0=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=A4=B4=E9=83=A8=E8=A7=A3=E6=9E=90=20+=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=BC=96=E8=AF=91=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - http: 解析 If-None-Match 和 If-Modified-Since 请求头 - http: 响应结构体添加 etag 和 last_modified 字段 - server/static: 替换 strncpy 为 snprintf,消除 stringop-truncation 警告 - static: 修复 CSS 百分号转义(%%) - static: 消除 unused parameter 警告 --- http.c | 15 +++++++++++++++ http.h | 10 ++++++++++ server.c | 14 +++++++++----- static.c | 10 +++++----- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/http.c b/http.c index 4ad4e94..8660f26 100644 --- a/http.c +++ b/http.c @@ -12,6 +12,7 @@ #include #include #include +#include /** * http_method_str - HTTP 方法枚举转字符串 @@ -63,6 +64,8 @@ static void parse_headers(const char **p, const char *end, http_request_t *req) req->has_range = false; req->range_start = 0; req->range_end = -1; + req->has_if_none_match = false; + req->has_if_modified_since = false; while (*p < end && req->num_headers < HTTP_MAX_HEADERS) { const char *line_start = *p; @@ -107,6 +110,18 @@ static void parse_headers(const char **p, const char *end, http_request_t *req) req->content_length = atoll(req->headers[req->num_headers].value); } else if (strcmp(req->headers[req->num_headers].name, "connection") == 0) { req->keep_alive = (strcasecmp(req->headers[req->num_headers].value, "keep-alive") == 0); + } else if (strcmp(req->headers[req->num_headers].name, "if-none-match") == 0) { + req->has_if_none_match = true; + int copy_len = strlen(req->headers[req->num_headers].value); + if (copy_len >= 64) copy_len = 63; + memcpy(req->if_none_match, req->headers[req->num_headers].value, copy_len); + req->if_none_match[copy_len] = '\0'; + } else if (strcmp(req->headers[req->num_headers].name, "if-modified-since") == 0) { + req->has_if_modified_since = true; + int copy_len = strlen(req->headers[req->num_headers].value); + if (copy_len >= 64) copy_len = 63; + memcpy(req->if_modified_since, req->headers[req->num_headers].value, copy_len); + req->if_modified_since[copy_len] = '\0'; } else if (strcmp(req->headers[req->num_headers].name, "range") == 0) { /* 解析 Range: bytes=start-end */ const char *range_val = req->headers[req->num_headers].value; diff --git a/http.h b/http.h index f32c9e6..e24c807 100644 --- a/http.h +++ b/http.h @@ -48,6 +48,12 @@ typedef struct { bool has_range; int64_t range_start; int64_t range_end; + + /* 缓存相关头 */ + char if_none_match[64]; + char if_modified_since[64]; + bool has_if_none_match; + bool has_if_modified_since; } http_request_t; /* === HTTP 响应 === */ @@ -61,6 +67,10 @@ typedef struct { int64_t range_start; int64_t range_end; int64_t total_length; + + /* 缓存 */ + const char *etag; + const char *last_modified; } http_response_t; /* === API === */ diff --git a/server.c b/server.c index 3768f1f..abc3d3c 100644 --- a/server.c +++ b/server.c @@ -173,8 +173,7 @@ static bool handle_request(connection_t *conn, const char *root_dir) { static_send_error(conn->fd, 403, req.keep_alive); return req.keep_alive; } - strncpy(real_path, resolved, sizeof(real_path) - 1); - real_path[sizeof(real_path) - 1] = '\0'; + snprintf(real_path, sizeof(real_path), "%s", resolved); } /* 判断文件类型 */ @@ -187,13 +186,18 @@ static bool handle_request(connection_t *conn, const char *root_dir) { if (S_ISDIR(st.st_mode)) { /* 目录:尝试 index.html */ char index_path[4096]; - snprintf(index_path, sizeof(index_path), "%s/index.html", real_path); + if (snprintf(index_path, sizeof(index_path), "%s/index.html", real_path) >= (int)sizeof(index_path)) { + static_send_error(conn->fd, 400, req.keep_alive); + return req.keep_alive; + } struct stat index_st; if (stat(index_path, &index_st) == 0 && S_ISREG(index_st.st_mode)) { /* 有 index.html,作为文件服务 */ http_request_t index_req = req; - strncpy(index_req.path, req.path, sizeof(index_req.path) - 1); - strncat(index_req.path, "/index.html", sizeof(index_req.path) - strlen(index_req.path) - 1); + if (snprintf(index_req.path, sizeof(index_req.path), "%s/index.html", req.path) >= (int)sizeof(index_req.path)) { + static_send_error(conn->fd, 400, req.keep_alive); + return req.keep_alive; + } static_serve_file(conn->fd, &index_req, root_dir); } else { /* 无 index.html,生成目录列表 */ diff --git a/static.c b/static.c index bd5598e..72df7b1 100644 --- a/static.c +++ b/static.c @@ -40,8 +40,7 @@ static bool safe_path_join(char *dst, size_t dst_size, /* 先规范化根目录 */ char root_normalized[4096]; if (!realpath(root, root_normalized)) { - strncpy(root_normalized, root, sizeof(root_normalized) - 1); - root_normalized[sizeof(root_normalized) - 1] = '\0'; + snprintf(root_normalized, sizeof(root_normalized), "%s", root); } size_t root_len = strlen(root_normalized); @@ -57,8 +56,7 @@ static bool safe_path_join(char *dst, size_t dst_size, if (strncmp(resolved, root_normalized, root_len) != 0) { return false; } - strncpy(dst, resolved, dst_size - 1); - dst[dst_size - 1] = '\0'; + snprintf(dst, dst_size, "%s", resolved); return true; } return false; @@ -301,6 +299,8 @@ static void html_escape(const char *src, char *dst, size_t dst_size) { */ int static_serve_directory(int fd, const http_request_t *req, const char *root_dir, const char *real_path) { + (void)root_dir; /* 未直接使用,real_path 已通过 safe_path_join 处理 */ + /* 检查目录是否可访问 */ struct stat st; if (stat(real_path, &st) != 0 || !S_ISDIR(st.st_mode)) { @@ -333,7 +333,7 @@ int static_serve_directory(int fd, const http_request_t *req, "