#pragma once #include "http_common.hpp" #include "http_request.hpp" #include "router.hpp" #include "cookie.hpp" #include #include #include #include using std::string_view; class HttpParser { public: static HttpRequest parse(string_view data) { HttpRequest req; const char* ptr = data.data(); const char* end = ptr + data.size(); // Parse method const char* method_end = find_char(ptr, end, ' '); if (!method_end) return req; req.method = parse_method(string_view(ptr, method_end - ptr)); ptr = method_end + 1; // Parse path and query const char* path_end = find_char(ptr, end, ' '); if (!path_end) return req; const char* query_start = find_char(ptr, path_end, '?'); if (query_start) { req.path = string_view(ptr, query_start - ptr); req.query = string_view(query_start + 1, path_end - query_start - 1); } else { req.path = string_view(ptr, path_end - ptr); } ptr = path_end + 1; // Parse version const char* version_end = find_char(ptr, end, '\r'); if (!version_end || version_end + 1 >= end || *(version_end + 1) != '\n') return req; req.version = string_view(ptr, version_end - ptr); ptr = version_end + 2; // Parse headers while (ptr < end - 1) { if (*ptr == '\r' && *(ptr + 1) == '\n') { // End of headers ptr += 2; break; } const char* header_end = find_char(ptr, end, '\r'); if (!header_end || header_end + 1 >= end || *(header_end + 1) != '\n') break; const char* colon = find_char(ptr, header_end, ':'); if (!colon) { ptr = header_end + 2; continue; } string_view name(ptr, colon - ptr); const char* value_start = colon + 1; while (value_start < header_end && *value_start == ' ') value_start++; string_view value(value_start, header_end - value_start); req.headers[name] = value; // Check for Content-Length if (name.size() == 14 && strncasecmp(name.data(), "content-length", 14) == 0) { req.content_length = parse_int(value); } // Check for Cookie header else if (name.size() == 6 && strncasecmp(name.data(), "cookie", 6) == 0) { req.cookies = CookieParser::parse(value); } ptr = header_end + 2; } // Body if (ptr < end) { req.body = string_view(ptr, end - ptr); } req.valid = true; return req; } private: static const char* find_char(const char* start, const char* end, char c) { for (const char* p = start; p < end; ++p) { if (*p == c) return p; } return nullptr; } static HttpMethod parse_method(string_view method) { switch (method.size()) { case 3: if (method == "GET") return HttpMethod::GET; if (method == "PUT") return HttpMethod::PUT; break; case 4: if (method == "POST") return HttpMethod::POST; if (method == "HEAD") return HttpMethod::HEAD; break; case 5: if (method == "PATCH") return HttpMethod::PATCH; break; case 6: if (method == "DELETE") return HttpMethod::DELETE; break; case 7: if (method == "OPTIONS") return HttpMethod::OPTIONS; break; } return HttpMethod::UNKNOWN; } static size_t parse_int(string_view str) { size_t result = 0; for (char c : str) { if (c >= '0' && c <= '9') { result = result * 10 + (c - '0'); } else { break; } } return result; } static int strncasecmp(const char* s1, const char* s2, size_t n) { for (size_t i = 0; i < n; ++i) { char c1 = s1[i] >= 'A' && s1[i] <= 'Z' ? s1[i] + 32 : s1[i]; char c2 = s2[i] >= 'A' && s2[i] <= 'Z' ? s2[i] + 32 : s2[i]; if (c1 != c2) return c1 - c2; if (c1 == 0) break; } return 0; } };