cpp_server/cookie.hpp

185 lines
4.4 KiB
C++

#pragma once
#include "common.hpp"
#include <string_view>
#include <vector>
#include <string>
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<Cookie> parse(string_view cookie_header) {
std::vector<Cookie> 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<Cookie>& 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;
}
};