修复HTTP请求体缓冲区限制问题 - 支持大规模批量删除操作

根本问题:
- HTTP服务器固定4KB缓冲区导致大JSON请求被截断
- 批量删除多个组件时请求体超过缓冲区限制

核心修复:
- HttpServer: 实现ReadCompleteRequest方法支持动态缓冲区
- 基于Content-Length头完整读取HTTP请求体
- Config: 添加MAX_REQUEST_SIZE=1MB上限保护机制
- 支持任意数量组件路径的批量删除操作

修复效果:
- 解决11个、25个路径批量删除失败问题
- 修复薄壳化分析等大JSON请求处理
- 保持所有现有功能正常运行
- 向后兼容,不影响现有API调用

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sladro 2025-09-09 16:54:03 +08:00
parent 7471895fa3
commit 45f455e0c2
3 changed files with 112 additions and 19 deletions

View File

@ -19,6 +19,9 @@ public:
// 缓冲区大小
static const int BUFFER_SIZE = 4096;
// HTTP请求限制
static const int MAX_REQUEST_SIZE = 1048576; // 1MB maximum request size
// Creo配置
static const int CREO_CHECK_INTERVAL_MS = 100; // 检查间隔

View File

@ -108,28 +108,13 @@ DWORD WINAPI HttpServer::ServerThread(LPVOID lpParam) {
}
void HttpServer::HandleClient(SOCKET client_socket) {
char buffer[Config::BUFFER_SIZE];
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
// 检查recv错误和超时情况
if (bytes_received == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAETIMEDOUT) {
// 接收超时,直接返回避免阻塞
return;
}
// 其他网络错误,直接返回
// 读取完整的HTTP请求
std::string raw_request = ReadCompleteRequest(client_socket);
if (raw_request.empty()) {
// 读取失败或超时,直接返回
return;
}
if (bytes_received <= 0) {
// 连接关闭或无数据,直接返回
return;
}
buffer[bytes_received] = '\0';
std::string raw_request(buffer);
HttpRequest request = ParseRequest(raw_request);
HttpResponse response;
@ -199,6 +184,110 @@ HttpRequest HttpServer::ParseRequest(const std::string& raw_request) {
return request;
}
std::string HttpServer::ReadCompleteRequest(SOCKET client_socket) {
std::string complete_request;
char buffer[Config::BUFFER_SIZE];
int total_received = 0;
int content_length = -1;
bool headers_complete = false;
size_t header_end_pos = 0;
try {
// First, read headers to determine Content-Length
while (!headers_complete && total_received < Config::MAX_REQUEST_SIZE) {
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
// Check for recv errors and timeout
if (bytes_received == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAETIMEDOUT) {
return ""; // Timeout, return empty
}
return ""; // Other network error, return empty
}
if (bytes_received <= 0) {
return ""; // Connection closed or no data
}
buffer[bytes_received] = '\0';
complete_request.append(buffer, bytes_received);
total_received += bytes_received;
// Check if headers are complete (look for \r\n\r\n)
size_t header_separator = complete_request.find("\r\n\r\n");
if (header_separator != std::string::npos) {
headers_complete = true;
header_end_pos = header_separator + 4;
// Parse Content-Length from headers
std::string headers_part = complete_request.substr(0, header_end_pos);
size_t cl_pos = headers_part.find("Content-Length:");
if (cl_pos != std::string::npos) {
size_t cl_start = cl_pos + 15; // Length of "Content-Length:"
size_t cl_end = headers_part.find("\r\n", cl_start);
if (cl_end != std::string::npos) {
std::string cl_str = headers_part.substr(cl_start, cl_end - cl_start);
// Remove leading/trailing whitespace
cl_str.erase(0, cl_str.find_first_not_of(" \t"));
cl_str.erase(cl_str.find_last_not_of(" \t") + 1);
try {
content_length = std::stoi(cl_str);
} catch (...) {
content_length = 0;
}
}
}
break;
}
}
// If no Content-Length header found, assume no body or return what we have
if (content_length < 0) {
return complete_request;
}
// Check if request size exceeds limit
if (content_length > Config::MAX_REQUEST_SIZE) {
return ""; // Request too large
}
// Calculate how much body data we already have
int current_body_length = complete_request.length() - header_end_pos;
// Read remaining body data if needed
while (current_body_length < content_length && total_received < Config::MAX_REQUEST_SIZE) {
int bytes_needed = content_length - current_body_length;
int bytes_to_read = min(bytes_needed, sizeof(buffer) - 1);
int bytes_received = recv(client_socket, buffer, bytes_to_read, 0);
if (bytes_received == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAETIMEDOUT) {
break; // Timeout, return what we have
}
break; // Other error, return what we have
}
if (bytes_received <= 0) {
break; // Connection closed
}
buffer[bytes_received] = '\0';
complete_request.append(buffer, bytes_received);
current_body_length += bytes_received;
total_received += bytes_received;
}
} catch (...) {
// Any exception during reading, return empty
return "";
}
return complete_request;
}
void HttpServer::SendResponse(SOCKET client_socket, const HttpResponse& response) {
std::ostringstream response_stream;

View File

@ -47,6 +47,7 @@ public:
private:
static DWORD WINAPI ServerThread(LPVOID lpParam);
void HandleClient(SOCKET client_socket);
std::string ReadCompleteRequest(SOCKET client_socket);
HttpRequest ParseRequest(const std::string& raw_request);
void SendResponse(SOCKET client_socket, const HttpResponse& response);