fix(build): 修复 Range 解析嵌套问题 + 补充 errno 头文件 + 链接 zlib

- http.c: 修复 Range 请求解析被错误嵌套在 accept-encoding 分支的 bug
- static.c: 补充缺失的 <errno.h> 和 <zlib.h> 头文件,修复编译失败
- Makefile: 添加 -lz 链接参数,支持 gzip 压缩功能
This commit is contained in:
xfy911 2026-06-03 18:18:33 +08:00
parent 0dc2bb5b4e
commit 5adf5e0581
4 changed files with 136 additions and 25 deletions

View File

@ -7,7 +7,7 @@ COCO_LIB ?= $(COCO_DIR)/build
CC = gcc CC = gcc
CFLAGS = -Wall -Wextra -O2 -std=c11 -I$(COCO_INCLUDE) CFLAGS = -Wall -Wextra -O2 -std=c11 -I$(COCO_INCLUDE)
LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring LDFLAGS = -L$(COCO_LIB) -lcoco -lpthread -lm -luring -lz
# 调试模式 # 调试模式
DEBUG ?= 0 DEBUG ?= 0

5
http.c
View File

@ -130,7 +130,7 @@ static void parse_headers(const char **p, const char *end, http_request_t *req)
const char *val = req->headers[req->num_headers].value; const char *val = req->headers[req->num_headers].value;
if (strstr(val, "gzip") != NULL) req->accept_gzip = true; if (strstr(val, "gzip") != NULL) req->accept_gzip = true;
if (strstr(val, "deflate") != NULL) req->accept_deflate = true; if (strstr(val, "deflate") != NULL) req->accept_deflate = true;
/* 解析 Range: bytes=start-end */ } else if (strcmp(req->headers[req->num_headers].name, "range") == 0) {
const char *range_val = req->headers[req->num_headers].value; const char *range_val = req->headers[req->num_headers].value;
if (strncasecmp(range_val, "bytes=", 6) == 0) { if (strncasecmp(range_val, "bytes=", 6) == 0) {
req->has_range = true; req->has_range = true;
@ -257,6 +257,9 @@ int http_format_response_header(char *buf, size_t buf_size, const http_response_
if (resp->last_modified && resp->last_modified[0]) { if (resp->last_modified && resp->last_modified[0]) {
n += snprintf(buf + n, buf_size - n, "Last-Modified: %s\r\n", resp->last_modified); n += snprintf(buf + n, buf_size - n, "Last-Modified: %s\r\n", resp->last_modified);
} }
if (resp->content_encoding && resp->content_encoding[0]) {
n += snprintf(buf + n, buf_size - n, "Content-Encoding: %s\r\n", resp->content_encoding);
}
/* 连接头 + 空行 */ /* 连接头 + 空行 */
n += snprintf(buf + n, buf_size - n, n += snprintf(buf + n, buf_size - n,

3
http.h
View File

@ -76,6 +76,9 @@ typedef struct {
/* 缓存 */ /* 缓存 */
const char *etag; const char *etag;
const char *last_modified; const char *last_modified;
/* 压缩 */
const char *content_encoding;
} http_response_t; } http_response_t;
/* === API === */ /* === API === */

115
static.c
View File

@ -21,6 +21,68 @@
#include <time.h> #include <time.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <zlib.h>
/**
* is_compressible_mime - MIME
*
*
*
*
* @param mime_type MIME
* @return true
*/
static bool is_compressible_mime(const char *mime_type) {
if (!mime_type) return false;
return (
strstr(mime_type, "text/") != NULL ||
strstr(mime_type, "application/javascript") != NULL ||
strstr(mime_type, "application/json") != NULL ||
strstr(mime_type, "application/xml") != NULL ||
strstr(mime_type, "application/manifest") != NULL ||
strstr(mime_type, "image/svg") != NULL
);
}
/**
* gzip_compress - 使 zlib gzip
*
* 使 deflateInit2 windowBits = 15 + 16 gzip
*
* @param src
* @param src_len
* @param dst src_len
* @param dst_cap
* @return 0 -1
*/
static ssize_t gzip_compress(const char *src, size_t src_len,
char *dst, size_t dst_cap) {
z_stream strm = {0};
/* 15 + 16 = gzip 格式 */
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
return -1;
}
strm.avail_in = (uInt)src_len;
strm.next_in = (Bytef *)src;
strm.avail_out = (uInt)dst_cap;
strm.next_out = (Bytef *)dst;
if (deflate(&strm, Z_FINISH) != Z_STREAM_END) {
deflateEnd(&strm);
return -1;
}
size_t compressed_len = dst_cap - strm.avail_out;
deflateEnd(&strm);
/* 如果压缩后更大或差不多,就不压缩了 */
if (compressed_len >= src_len * 0.95) {
return 0;
}
return (ssize_t)compressed_len;
}
/** /**
* format_http_time - HTTP * format_http_time - HTTP
@ -278,25 +340,57 @@ int static_serve_file(int fd, const http_request_t *req, const char *root_dir) {
} }
} }
/* 计算发送范围 */ /* 判断是否 gzip 压缩 */
int64_t file_size = st.st_size; int64_t file_size = st.st_size;
bool use_gzip = false;
char *gzip_buf = NULL;
ssize_t gzip_len = 0;
if (req->accept_gzip && !req->has_range && req->method != HTTP_HEAD) {
const char *mime = http_mime_type(real_path);
if (is_compressible_mime(mime) && file_size > 256) {
/* 读取文件内容到内存 */
char *file_buf = (char *)malloc((size_t)file_size);
if (file_buf) {
ssize_t read_total = 0;
while (read_total < file_size) {
ssize_t n = read(file_fd, file_buf + read_total, (size_t)(file_size - read_total));
if (n <= 0) break;
read_total += n;
}
if (read_total == file_size) {
gzip_buf = (char *)malloc((size_t)file_size);
if (gzip_buf) {
gzip_len = gzip_compress(file_buf, (size_t)file_size, gzip_buf, (size_t)file_size);
if (gzip_len > 0) {
use_gzip = true;
}
}
}
free(file_buf);
}
}
}
/* 计算发送范围 */
int64_t send_start = 0; int64_t send_start = 0;
int64_t send_end = file_size - 1; int64_t send_end = file_size - 1;
int status_code = 200; int status_code = 200;
if (req->has_range) { if (!use_gzip && req->has_range) {
send_start = req->range_start; send_start = req->range_start;
if (req->range_end >= 0 && req->range_end < file_size) { if (req->range_end >= 0 && req->range_end < file_size) {
send_end = req->range_end; send_end = req->range_end;
} }
if (send_start >= file_size || send_start > send_end) { if (send_start >= file_size || send_start > send_end) {
close(file_fd); close(file_fd);
free(gzip_buf);
return static_send_error(fd, 416, req->keep_alive); return static_send_error(fd, 416, req->keep_alive);
} }
status_code = 206; status_code = 206;
} }
int64_t send_length = send_end - send_start + 1; int64_t send_length = use_gzip ? gzip_len : (send_end - send_start + 1);
/* 构建响应头 */ /* 构建响应头 */
http_response_t resp = { http_response_t resp = {
@ -305,24 +399,27 @@ int static_serve_file(int fd, const http_request_t *req, const char *root_dir) {
.content_type = http_mime_type(real_path), .content_type = http_mime_type(real_path),
.content_length = send_length, .content_length = send_length,
.keep_alive = req->keep_alive, .keep_alive = req->keep_alive,
.has_range = req->has_range, .has_range = !use_gzip && req->has_range,
.range_start = send_start, .range_start = send_start,
.range_end = send_end, .range_end = send_end,
.total_length = file_size, .total_length = file_size,
.etag = etag, .etag = etag,
.last_modified = last_modified .last_modified = last_modified,
.content_encoding = use_gzip ? "gzip" : NULL
}; };
char header_buf[1024]; char header_buf[1024];
int header_len = http_format_response_header(header_buf, sizeof(header_buf), &resp); int header_len = http_format_response_header(header_buf, sizeof(header_buf), &resp);
if (header_len < 0) { if (header_len < 0) {
close(file_fd); close(file_fd);
free(gzip_buf);
return static_send_error(fd, 500, req->keep_alive); return static_send_error(fd, 500, req->keep_alive);
} }
/* 发送响应头 */ /* 发送响应头 */
if (send_all(fd, header_buf, (size_t)header_len) != 0) { if (send_all(fd, header_buf, (size_t)header_len) != 0) {
close(file_fd); close(file_fd);
free(gzip_buf);
return COCOON_ERROR; return COCOON_ERROR;
} }
@ -330,9 +427,16 @@ int static_serve_file(int fd, const http_request_t *req, const char *root_dir) {
if (req->method == HTTP_HEAD) { if (req->method == HTTP_HEAD) {
/* HEAD 请求不发送 body */ /* HEAD 请求不发送 body */
close(file_fd); close(file_fd);
free(gzip_buf);
return COCOON_OK; return COCOON_OK;
} }
if (use_gzip) {
/* 发送 gzip 压缩后的数据 */
send_all(fd, gzip_buf, (size_t)gzip_len);
free(gzip_buf);
close(file_fd);
} else {
/* 定位到起始位置 */ /* 定位到起始位置 */
if (send_start > 0) { if (send_start > 0) {
lseek(file_fd, send_start, SEEK_SET); lseek(file_fd, send_start, SEEK_SET);
@ -353,6 +457,7 @@ int static_serve_file(int fd, const http_request_t *req, const char *root_dir) {
} }
close(file_fd); close(file_fd);
}
return COCOON_OK; return COCOON_OK;
} }