#include "httpserver.hpp" #include #include #include #include #include #include #include #include #include #include #include #include // ==================== 工具函数实现 ==================== std::string HTTPServer::urlDecode(const std::string &encoded) { std::string decoded; for (size_t i = 0; i < encoded.length(); ++i) { if (encoded[i] == '%' && i + 2 < encoded.length()) { int hex; std::istringstream hexStream(encoded.substr(i + 1, 2)); if (hexStream >> std::hex >> hex) { decoded += static_cast(hex); i += 2; } else { decoded += encoded[i]; } } else if (encoded[i] == '+') { decoded += ' '; } else { decoded += encoded[i]; } } return decoded; } std::map HTTPServer::parseQueryString(const std::string &query) { std::map params; std::istringstream stream(query); std::string pair; while (std::getline(stream, pair, '&')) { size_t equalsPos = pair.find('='); if (equalsPos != std::string::npos) { std::string key = urlDecode(pair.substr(0, equalsPos)); std::string value = urlDecode(pair.substr(equalsPos + 1)); params[key] = value; } else if (!pair.empty()) { params[urlDecode(pair)] = ""; } } return params; } std::map HTTPServer::parseHeaders(const std::string &headers) { std::map header_map; std::istringstream stream(headers); std::string line; while (std::getline(stream, line)) { if (line.empty() || line == "\r") continue; // 移除可能的回车符 if (!line.empty() && line.back() == '\r') { line.pop_back(); } size_t colonPos = line.find(':'); if (colonPos != std::string::npos) { std::string key = line.substr(0, colonPos); // 跳过冒号和空格 size_t value_start = colonPos + 1; while (value_start < line.length() && line[value_start] == ' ') { value_start++; } std::string value = line.substr(value_start); header_map[key] = value; } } return header_map; } std::string HTTPServer::getContentType(const std::map &headers) { auto it = headers.find("Content-Type"); if (it != headers.end()) { return it->second; } return ""; } int HTTPServer::getContentLength(const std::map &headers) { auto it = headers.find("Content-Length"); if (it != headers.end()) { try { return std::stoi(it->second); } catch (const std::exception &e) { std::cerr << "解析Content-Length错误: " << e.what() << std::endl; return 0; } } return 0; } void HTTPServer::sendResponse(int client_socket, const std::string &response, const std::string &content_type) { std::string httpResponse = "HTTP/1.1 200 OK\r\n" "Content-Type: " + content_type + "\r\n" "Content-Length: " + std::to_string(response.length()) + "\r\n" "Connection: close\r\n" "\r\n" + response; send(client_socket, httpResponse.c_str(), httpResponse.length(), 0); } void HTTPServer::sendError(int client_socket, int code, const std::string &message) { std::string response = "

" + std::to_string(code) + " " + message + "

"; std::string httpResponse = "HTTP/1.1 " + std::to_string(code) + " " + message + "\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Content-Length: " + std::to_string(response.length()) + "\r\n" "Connection: close\r\n" "\r\n" + response; send(client_socket, httpResponse.c_str(), httpResponse.length(), 0); } void HTTPServer::sendJSONResponse(int client_socket, const std::string &json) { sendResponse(client_socket, json, "application/json"); } // ==================== 私有方法实现 ==================== std::string HTTPServer::readFullRequest(int client_socket, int timeout_ms) { std::string request; char buffer[4096]; fd_set read_fds; struct timeval timeout; // 设置非阻塞模式 int flags = fcntl(client_socket, F_GETFL, 0); fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); // 设置超时 timeout.tv_sec = timeout_ms / 1000; timeout.tv_usec = (timeout_ms % 1000) * 1000; while (true) { FD_ZERO(&read_fds); FD_SET(client_socket, &read_fds); int activity = select(client_socket + 1, &read_fds, nullptr, nullptr, &timeout); if (activity < 0) { std::cerr << "select error" << std::endl; break; } else if (activity == 0) { // 超时,检查是否已经有数据 if (!request.empty()) { break; } std::cerr << "read timeout" << std::endl; break; } if (FD_ISSET(client_socket, &read_fds)) { ssize_t bytes_read = recv(client_socket, buffer, sizeof(buffer) - 1, 0); if (bytes_read > 0) { buffer[bytes_read] = '\0'; request.append(buffer, bytes_read); // 检查是否已经收到完整的请求 if (isRequestComplete(request)) { break; } } else if (bytes_read == 0) { // 连接关闭 break; } else { if (errno != EAGAIN && errno != EWOULDBLOCK) { std::cerr << "recv error: " << strerror(errno) << std::endl; } break; } } } // 恢复阻塞模式 fcntl(client_socket, F_SETFL, flags); return request; } bool HTTPServer::isRequestComplete(const std::string& request) { // 查找请求头和请求体的分隔符 size_t header_end = request.find("\r\n\r\n"); if (header_end == std::string::npos) { return false; // 没有找到完整头部 } // 提取头部部分 std::string headers_part = request.substr(0, header_end); // 解析Content-Length int content_length = getContentLength(parseHeaders(headers_part)); // 如果有Content-Length,检查是否收到完整body if (content_length > 0) { size_t body_start = header_end + 4; if (request.length() >= body_start + content_length) { return true; } return false; } // 如果没有Content-Length,检查是否以空行结尾(表示没有body) if (request.length() >= header_end + 4 && request.substr(header_end + 4).empty()) { return true; } // 对于没有Content-Length的GET请求,有分隔符就认为完整 if (request.find("GET") == 0 || request.find("HEAD") == 0) { return true; } return false; } void HTTPServer::logRequest(const std::string& method, const std::string& client_ip, const std::map& headers) { // 获取当前时间 std::time_t now = std::time(nullptr); char time_str[100]; std::strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", std::localtime(&now)); std::cout << "[" << time_str << "] " << client_ip << " " << method; auto user_agent = headers.find("User-Agent"); if (user_agent != headers.end()) { std::cout << " (" << user_agent->second.substr(0, 50) << ")"; } std::cout << std::endl; } void HTTPServer::handleConnection(int client_socket, const std::string& client_ip) { // 读取完整的请求 std::string request = readFullRequest(client_socket); if (!request.empty()) { handleRequest(client_socket, request); } else { std::cerr << "收到空请求或读取失败" << std::endl; sendError(client_socket, 400, "Bad Request"); } close(client_socket); } void HTTPServer::handleRequest(int client_socket, const std::string& request) { // 提取请求行 size_t line_end = request.find("\r\n"); if (line_end == std::string::npos) { sendError(client_socket, 400, "Bad Request"); return; } std::string request_line = request.substr(0, line_end); std::istringstream iss(request_line); std::string method, path, protocol; iss >> method >> path >> protocol; // 提取头部 std::string headers_str; size_t headers_end = request.find("\r\n\r\n"); if (headers_end != std::string::npos) { headers_str = request.substr(line_end + 2, headers_end - line_end - 2); } auto headers = parseHeaders(headers_str); for (const auto& header : headers) { std::cout << header.first << ": " << header.second << std::endl; } // 记录请求日志 logRequest(method, "", headers); // 处理请求 if (method == "POST") { handlePOST(client_socket, request, headers); } else if (method == "GET") { handleGET(client_socket, request_line); } else { sendError(client_socket, 405, "Method Not Allowed"); } } void HTTPServer::handlePOST(int client_socket, const std::string& request, const std::map& headers) { std::cout << "\n=== 收到POST请求 ===\n"; // 获取请求体 int contentLength = getContentLength(headers); std::string body; if (contentLength > 0) { size_t bodyPos = request.find("\r\n\r\n"); if (bodyPos != std::string::npos) { bodyPos += 4; // 跳过空行 if (bodyPos < request.length()) { body = request.substr(bodyPos); // 确保body长度不超过Content-Length if (body.length() > static_cast(contentLength)) { body = body.substr(0, contentLength); } } } } std::cout << "请求体长度: " << body.length() << " 字节" << std::endl; // 如果有自定义处理器,使用它 if (custom_handler) { custom_handler(client_socket, request, headers, body); return; } // 默认处理逻辑 std::string contentType = getContentType(headers); std::string response; if (contentType.find("application/x-www-form-urlencoded") != std::string::npos) { // 表单数据 auto params = parseQueryString(body); response = "

POST数据已接收

"; response += "

表单数据:

"; if (params.empty()) { response += "

没有表单数据

"; response += "

原始body: " + body + "

"; } else { response += "
    "; for (const auto& param : params) { response += "
  • " + param.first + ": " + param.second + "
  • "; } response += "
"; } response += ""; // 打印到控制台 std::cout << "\n解析的表单数据:\n"; for (const auto& param : params) { std::cout << param.first << " = " << param.second << std::endl; } } else if (contentType.find("application/Json") != std::string::npos) { // JSON数据 this->PostMsg = body; response = "

JSON数据已接收

"; response += "

原始JSON:

"; response += "
" + body + "
"; response += ""; std::cout << "\nJSON数据:\n" << body << std::endl; } else { // 其他类型 response = "

数据已接收

"; response += "

Content-Type: " + (contentType.empty() ? "未指定" : contentType) + "

"; response += "

数据长度: " + std::to_string(body.length()) + " 字节

"; if (!body.empty() && body.length() < 1000) { response += "

原始数据:

"; response += "
" + body + "
"; } else if (!body.empty()) { response += "

原始数据(前1000字节):

"; response += "
" + body.substr(0, 1000) + "...
"; } response += ""; std::cout << "\n原始数据:\n" << body.substr(0, std::min(body.length(), (size_t)500)) << std::endl; if (body.length() > 500) { std::cout << "... (只显示前500字节)" << std::endl; } } sendResponse(client_socket, response); std::cout << "\n=== 处理完成 ===\n\n"; } void HTTPServer::handleGET(int client_socket, const std::string& request_line) { std::istringstream iss(request_line); std::string method, path, protocol; iss >> method >> path >> protocol; // 默认提供测试表单页面 std::string response = "" "HTTP POST测试服务器" "" "" "" "" "" "
" "

HTTP POST测试服务器

" "

服务器运行在端口 " + std::to_string(port) + "

" "

1. 表单提交测试 (application/x-www-form-urlencoded)

" "
" "
" "
" "
" "" "
" "

2. JSON提交测试 (application/json)

" "
" "
" "
" "" "
" "

3. 使用curl测试

" "
" "

表单数据:

" "curl -X POST http://localhost:" + std::to_string(port) + "/ \\
" " -H \"Content-Type: application/x-www-form-urlencoded\" \\
" " -d \"name=张三&email=test@example.com&message=Hello%20World\"


" "

JSON数据:

" "curl -X POST http://localhost:" + std::to_string(port) + "/ \\
" " -H \"Content-Type: application/json\" \\
" " -d '{\"name\":\"李四\",\"age\":25,\"city\":\"北京\"}'
" "
" "
" "
" ""; sendResponse(client_socket, response); } // ==================== 公有方法实现 ==================== HTTPServer::HTTPServer(int port) : port(port), server_fd(-1), custom_handler(nullptr) {} HTTPServer::~HTTPServer() { stop(); } bool HTTPServer::start() { // 创建socket server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { std::cerr << "创建socket失败: " << strerror(errno) << std::endl; return false; } // 设置socket选项,避免地址占用 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { std::cerr << "设置socket选项失败: " << strerror(errno) << std::endl; close(server_fd); return false; } // 绑定地址和端口 struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(port); if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { std::cerr << "绑定端口 " << port << " 失败: " << strerror(errno) << std::endl; close(server_fd); return false; } // 开始监听 if (listen(server_fd, 10) < 0) { std::cerr << "监听失败: " << strerror(errno) << std::endl; close(server_fd); return false; } std::cout << "HTTP服务器启动成功,监听端口: " << port << std::endl; std::cout << "访问 http://localhost:" << port << " 进行测试" << std::endl; std::cout << "按 Ctrl+C 停止服务器" << std::endl; return true; } void HTTPServer::run() { fd_set readfds; struct timeval timeout; FD_ZERO(&readfds); FD_SET(server_fd, &readfds); // 设置1秒超时 timeout.tv_sec = 1; timeout.tv_usec = 0; int activity = select(server_fd + 1, &readfds, NULL, NULL, &timeout); if (activity < 0) { std::cerr << "select error: " << strerror(errno) << std::endl; return; } if (activity == 0) { // 超时,检查是否应该停止 return; } if (FD_ISSET(server_fd, &readfds)) { struct sockaddr_in client_address; socklen_t client_len = sizeof(client_address); int client_socket = accept(server_fd, (struct sockaddr *)&client_address, &client_len); if (client_socket < 0) { std::cerr << "接受连接失败: " << strerror(errno) << std::endl; return; } char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_address.sin_addr, client_ip, INET_ADDRSTRLEN); handleConnection(client_socket, client_ip); } } void HTTPServer::stop() { if (server_fd != -1) { close(server_fd); server_fd = -1; std::cout << "服务器已停止" << std::endl; } } void HTTPServer::SetPort(int port) { this->port = port; } void HTTPServer::setRequestHandler(const RequestHandler& handler) { custom_handler = handler; }