43 std::string lower_name = name;
44 std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
45 [](
unsigned char c) { return std::tolower(c); });
47 for (
const auto &[key, value] :
headers) {
48 std::string lower_key = key;
49 std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(),
50 [](
unsigned char c) { return std::tolower(c); });
51 if (lower_key == lower_name) {
63 std::ostringstream oss;
69 bool has_content_length =
false;
70 bool has_connection =
false;
72 for (
const auto &[key, value] :
headers) {
73 oss << key <<
": " << value <<
"\r\n";
74 std::string lower_key = key;
75 std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(),
76 [](
unsigned char c) { return std::tolower(c); });
77 if (lower_key ==
"content-length")
78 has_content_length =
true;
79 if (lower_key ==
"connection")
80 has_connection =
true;
84 if (!has_content_length) {
85 oss <<
"Content-Length: " <<
body.size() <<
"\r\n";
89 if (!has_connection) {
90 oss <<
"Connection: close\r\n";
94 oss <<
"Access-Control-Allow-Origin: *\r\n";
95 oss <<
"Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\n";
96 oss <<
"Access-Control-Allow-Headers: Content-Type\r\n";
115 WSAStartup(MAKEWORD(2, 2), &wsa_data);
131 routes_.push_back({method, path, std::move(handler)});
138 last_error_ =
"Server already running";
143 server_socket_ =
static_cast<int>(socket(AF_INET, SOCK_STREAM, 0));
145 last_error_ =
"Failed to create socket";
152 setsockopt(server_socket_, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char *
>(&opt),
sizeof(opt));
154 setsockopt(server_socket_, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt));
158 struct sockaddr_in addr {};
159 addr.sin_family = AF_INET;
160 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
161 addr.sin_port = htons(
port);
163 if (bind(server_socket_,
reinterpret_cast<struct sockaddr *
>(&addr),
sizeof(addr)) < 0) {
164 last_error_ =
"Failed to bind to port " + std::to_string(
port);
171 if (listen(server_socket_, 10) < 0) {
172 last_error_ =
"Failed to listen on socket";
182 server_thread_ = std::thread(&HttpServer::serverLoop,
this);
195 if (server_socket_ >= 0) {
197 shutdown(server_socket_, SD_BOTH);
199 shutdown(server_socket_, SHUT_RDWR);
206 if (server_thread_.joinable()) {
207 server_thread_.join();
211void HttpServer::serverLoop() {
213 struct sockaddr_in client_addr {};
214 socklen_t client_len =
sizeof(client_addr);
216 socket_t client_socket = accept(server_socket_,
reinterpret_cast<struct sockaddr *
>(&client_addr), &client_len);
226 handleClient(
static_cast<int>(client_socket));
231 if (server_socket_ < 0) {
235 struct sockaddr_in client_addr {};
236 socklen_t client_len =
sizeof(client_addr);
238 socket_t client_socket = accept(server_socket_,
reinterpret_cast<struct sockaddr *
>(&client_addr), &client_len);
244 handleClient(
static_cast<int>(client_socket));
248void HttpServer::handleClient(
int client_socket) {
254 bytes_read = recv(client_socket, buffer,
sizeof(buffer) - 1, 0);
256 bytes_read = read(client_socket, buffer,
sizeof(buffer) - 1);
259 if (bytes_read <= 0) {
263 buffer[bytes_read] =
'\0';
267 if (!parseRequest(buffer, request)) {
269 std::string raw = response.serialize();
270 send(client_socket, raw.c_str(),
static_cast<int>(raw.size()), 0);
276 if (request.method ==
"OPTIONS") {
278 std::string raw = response.serialize();
279 send(client_socket, raw.c_str(),
static_cast<int>(raw.size()), 0);
285 RouteHandler handler = findHandler(request.method, request.path);
288 HttpResponse response;
290 response = handler(request);
291 }
catch (
const std::exception &e) {
298 std::string raw = response.serialize();
299 send(client_socket, raw.c_str(),
static_cast<int>(raw.size()), 0);
304bool HttpServer::parseRequest(
const std::string &raw, HttpRequest &request) {
306 size_t line_end = raw.find(
"\r\n");
307 if (line_end == std::string::npos) {
312 std::string request_line = raw.substr(0, line_end);
313 std::istringstream iss(request_line);
314 std::string http_version;
315 std::string full_path;
317 if (!(iss >> request.method >> full_path >> http_version)) {
322 size_t query_pos = full_path.find(
'?');
323 if (query_pos != std::string::npos) {
324 request.path = full_path.substr(0, query_pos);
325 request.query_string = full_path.substr(query_pos + 1);
326 parseQueryString(request.query_string, request.params);
328 request.path = full_path;
332 size_t pos = line_end + 2;
333 while (pos < raw.size()) {
334 size_t next_line = raw.find(
"\r\n", pos);
335 if (next_line == std::string::npos) {
339 std::string header_line = raw.substr(pos, next_line - pos);
340 if (header_line.empty()) {
346 size_t colon = header_line.find(
':');
347 if (colon != std::string::npos) {
348 std::string name = header_line.substr(0, colon);
349 std::string value = header_line.substr(colon + 1);
352 size_t value_start = value.find_first_not_of(
" \t");
353 if (value_start != std::string::npos) {
354 value = value.substr(value_start);
357 request.headers[name] = value;
364 if (pos < raw.size()) {
365 request.body = raw.substr(pos);
371void HttpServer::parseQueryString(
const std::string &query, std::unordered_map<std::string, std::string> ¶ms) {
373 while (pos < query.size()) {
375 size_t amp = query.find(
'&', pos);
376 if (amp == std::string::npos) {
380 std::string param = query.substr(pos, amp - pos);
383 size_t eq = param.find(
'=');
384 if (eq != std::string::npos) {
385 std::string key = urlDecode(param.substr(0, eq));
386 std::string value = urlDecode(param.substr(eq + 1));
390 std::string key = urlDecode(param);
400std::string HttpServer::urlDecode(
const std::string &str) {
402 result.reserve(str.size());
404 for (
size_t i = 0; i < str.size(); i++) {
405 if (str[i] ==
'%' && i + 2 < str.size()) {
407 char hex[3] = {str[i + 1], str[i + 2],
'\0'};
408 if (sscanf(hex,
"%2x", &val) == 1) {
409 result +=
static_cast<char>(val);
414 }
else if (str[i] ==
'+') {
424RouteHandler HttpServer::findHandler(
const std::string &method,
const std::string &path) {
425 for (
const auto &route : routes_) {
426 if (route.method == method && route.path == path) {
427 return route.handler;
430 return default_handler_;