#pragma once #include "common.hpp" #include #include #include using std::string_view; struct Cookie { string_view name; string_view value; Cookie(string_view n, string_view v) : name(n), value(v) {} }; class CookieParser { public: static std::vector parse(string_view cookie_header) { std::vector cookies; cookies.reserve(8); // Most requests have few cookies const char* ptr = cookie_header.data(); const char* end = ptr + cookie_header.size(); while (likely(ptr < end)) { // Skip whitespace and semicolons ptr = skip_separators(ptr, end); if (unlikely(ptr >= end)) break; // Find name end (=) const char* name_start = ptr; ptr = find_char(ptr, end, '='); if (unlikely(!ptr)) break; string_view name(name_start, ptr - name_start); ptr++; // Skip '=' // Find value end (; or end) const char* value_start = ptr; const char* value_end = find_char(ptr, end, ';'); if (!value_end) value_end = end; string_view value(value_start, value_end - value_start); ptr = value_end; // Trim whitespace efficiently name = trim_fast(name); value = trim_fast(value); if (likely(!name.empty())) { cookies.emplace_back(name, value); } } return cookies; } private: static const char* skip_separators(const char* ptr, const char* end) { // Unrolled loop for common case while (likely(ptr < end - 3)) { if (*ptr != ' ' && *ptr != ';') break; ++ptr; if (*ptr != ' ' && *ptr != ';') break; ++ptr; if (*ptr != ' ' && *ptr != ';') break; ++ptr; if (*ptr != ' ' && *ptr != ';') break; ++ptr; } while (ptr < end && (*ptr == ' ' || *ptr == ';')) ++ptr; return ptr; } static const char* find_char(const char* start, const char* end, char target) { // Optimized character search while (likely(start < end - 7)) { if (start[0] == target) return start; if (start[1] == target) return start + 1; if (start[2] == target) return start + 2; if (start[3] == target) return start + 3; if (start[4] == target) return start + 4; if (start[5] == target) return start + 5; if (start[6] == target) return start + 6; if (start[7] == target) return start + 7; start += 8; } while (start < end && *start != target) ++start; return start < end ? start : nullptr; } static string_view trim_fast(string_view str) { if (unlikely(str.empty())) return str; const char* start = str.data(); const char* end = start + str.size(); // Trim leading - unrolled while (likely(start < end - 3) && *start == ' ') { if (start[1] != ' ') { start += 1; break; } if (start[2] != ' ') { start += 2; break; } if (start[3] != ' ') { start += 3; break; } start += 4; } while (start < end && *start == ' ') ++start; // Trim trailing - unrolled while (likely(end > start + 3) && *(end - 1) == ' ') { if (*(end - 2) != ' ') { end -= 1; break; } if (*(end - 3) != ' ') { end -= 2; break; } if (*(end - 4) != ' ') { end -= 3; break; } end -= 4; } while (end > start && *(end - 1) == ' ') --end; return string_view(start, end - start); } }; class CookieHelpers { public: static string_view get_cookie(const std::vector& cookies, string_view name) { // Optimized linear search with early termination for (const auto& cookie : cookies) { if (likely(cookie.name.size() == name.size()) && cookie.name == name) { return cookie.value; } } return {}; } static std::string build_set_cookie(string_view name, string_view value, int max_age = -1, string_view path = "", string_view domain = "", bool secure = false, bool http_only = false) { std::string result; result.reserve(name.size() + value.size() + 128); // Estimate size result += name; result += "="; result += value; if (unlikely(max_age >= 0)) { result += "; Max-Age="; result += std::to_string(max_age); } if (unlikely(!path.empty())) { result += "; Path="; result += path; } if (unlikely(!domain.empty())) { result += "; Domain="; result += domain; } if (unlikely(secure)) { result += "; Secure"; } if (unlikely(http_only)) { result += "; HttpOnly"; } return result; } static std::string build_delete_cookie(string_view name, string_view path = "") { std::string result; result.reserve(name.size() + 64); result += name; result += "=; Max-Age=0"; if (unlikely(!path.empty())) { result += "; Path="; result += path; } return result; } };