CreoOtkPluging/HttpServer.cpp
2025-07-16 17:16:59 +08:00

194 lines
5.5 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pch.h"
#include "HttpServer.h"
#include <sstream>
#include <functional>
#pragma comment(lib, "ws2_32.lib")
HttpServer::HttpServer() : server_socket_(INVALID_SOCKET), running_(false), thread_handle_(nullptr) {
}
HttpServer::~HttpServer() {
Stop();
}
bool HttpServer::Start() {
if (running_) return true;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return false;
}
server_socket_ = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket_ == INVALID_SOCKET) {
WSACleanup();
return false;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(Config::HTTP_PORT);
if (bind(server_socket_, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
closesocket(server_socket_);
WSACleanup();
return false;
}
if (listen(server_socket_, SOMAXCONN) == SOCKET_ERROR) {
closesocket(server_socket_);
WSACleanup();
return false;
}
running_ = true;
thread_handle_ = CreateThread(NULL, 0, ServerThread, this, 0, NULL);
return thread_handle_ != nullptr;
}
void HttpServer::Stop() {
running_ = false;
if (server_socket_ != INVALID_SOCKET) {
closesocket(server_socket_);
server_socket_ = INVALID_SOCKET;
}
if (thread_handle_) {
WaitForSingleObject(thread_handle_, 5000);
CloseHandle(thread_handle_);
thread_handle_ = nullptr;
}
WSACleanup();
}
void HttpServer::SetRouteHandler(const std::string& path,
std::function<HttpResponse(const HttpRequest&)> handler) {
route_handlers_[path] = handler;
}
DWORD WINAPI HttpServer::ServerThread(LPVOID lpParam) {
HttpServer* server = static_cast<HttpServer*>(lpParam);
while (server->running_) {
SOCKET client_socket = accept(server->server_socket_, NULL, NULL);
if (client_socket == INVALID_SOCKET) {
if (server->running_) {
continue;
}
break;
}
server->HandleClient(client_socket);
closesocket(client_socket);
}
return 0;
}
void HttpServer::HandleClient(SOCKET client_socket) {
char buffer[Config::BUFFER_SIZE];
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
if (bytes_received <= 0) return;
buffer[bytes_received] = '\0';
std::string raw_request(buffer);
HttpRequest request = ParseRequest(raw_request);
HttpResponse response;
// OPTIONS<4E>BCORS<52><53>
if (request.method == "OPTIONS") {
response.status_code = 200;
response.body = "";
SendResponse(client_socket, response);
return;
}
// <20>~<7E>1h
auto handler_it = route_handlers_.find(request.path);
if (handler_it != route_handlers_.end()) {
response = handler_it->second(request);
} else {
response.status_code = 404;
response.body = "{\"error\":\"Not Found\"}";
}
SendResponse(client_socket, response);
}
HttpRequest HttpServer::ParseRequest(const std::string& raw_request) {
HttpRequest request;
std::istringstream stream(raw_request);
std::string line;
// <20><>BL
if (std::getline(stream, line)) {
std::istringstream request_line(line);
std::string path_with_query;
request_line >> request.method >> path_with_query;
// <06><EFBFBD><EF848C><EFBFBD>p
size_t query_pos = path_with_query.find('?');
if (query_pos != std::string::npos) {
request.path = path_with_query.substr(0, query_pos);
request.query = path_with_query.substr(query_pos + 1);
} else {
request.path = path_with_query;
}
}
// <20><>B4
while (std::getline(stream, line) && line != "\r") {
size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
std::string key = line.substr(0, colon_pos);
std::string value = line.substr(colon_pos + 2);
if (!value.empty() && value.back() == '\r') {
value.pop_back();
}
request.headers[key] = value;
}
}
// <20><><EFBFBD>BS
std::string body_line;
while (std::getline(stream, body_line)) {
request.body += body_line + "\n";
}
if (!request.body.empty()) {
request.body.pop_back(); // <20>d<0E>bL&
}
return request;
}
void HttpServer::SendResponse(SOCKET client_socket, const HttpResponse& response) {
std::ostringstream response_stream;
// <20>L
response_stream << "HTTP/1.1 " << response.status_code << " ";
switch (response.status_code) {
case 200: response_stream << "OK"; break;
case 404: response_stream << "Not Found"; break;
case 500: response_stream << "Internal Server Error"; break;
default: response_stream << "Unknown"; break;
}
response_stream << "\r\n";
// ͔4
for (const auto& header : response.headers) {
response_stream << header.first << ": " << header.second << "\r\n";
}
response_stream << "Content-Length: " << response.body.length() << "\r\n";
response_stream << "Connection: close\r\n\r\n";
// ͔S
response_stream << response.body;
std::string response_str = response_stream.str();
send(client_socket, response_str.c_str(), (int)response_str.length(), 0);
}