// TJP COPYRIGHT HEADER #ifdef TJP_CORE_HEADER_ONLY #pragma once #endif #include #include "Headers.hpp" #include #include #include #include #include #include #include #include namespace tjp::core::internet { TJP_CORE_HEADER_ONLY_INLINE const String *getHeader(const Headers &headers, StringView name) { for (auto &[key, value]: headers) if (core::cmp_case(key, name) == 0) return &value; return nullptr; } TJP_CORE_HEADER_ONLY_INLINE Optional parseDate(const String &s) { std::tm tm = {}; core::io::imemstream ss(s.data(), s.size()); // Example format: "Fri, 02 Jun 2023 18:24:37 GMT" ss >> std::get_time(&tm, "%a, %d %b %Y %H:%M:%S GMT"); if (ss.fail()) { return {}; } // Convert to time_t in UTC #if defined(_WIN32) time_t t = _mkgmtime(&tm); #else time_t t = timegm(&tm); #endif return static_cast(t); } TJP_CORE_HEADER_ONLY_INLINE Optional getLastModified(const Headers &headers) { if (auto value = getHeader(headers, "Last-Modified")) return parseDate(*value); return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getDate(const Headers &headers) { if (auto value = getHeader(headers, "Date")) return parseDate(*value); return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getType(const Headers &headers) { if (auto value = getHeader(headers, "Content-Type")) return *value; return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getCacheControl(const Headers &headers) { if (auto value = getHeader(headers, "Cache-Control")) return *value; return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getContentLocation(const Headers &headers) { if (auto value = getHeader(headers, "Content-Location")) return *value; return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getLocation(const Headers &headers) { if (auto value = getHeader(headers, "Location")) return *value; return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional getCookie(const Headers &headers) { if (auto value = getHeader(headers, "Set-Cookie")) return *value; return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional parseStatusCode(const String &s) { if (s.rfind("HTTP/", 0) == 0) { int code = 0; if (sscanf((char *)s.data(), "HTTP/%*s %d", &code) != 1) return {}; return code; } return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional parseRange(const String &s) { long long int start = -1, end = -1; int matched = std::sscanf(s.data(), "bytes=%lld-%lld", &start, &end); if (matched != 2) return {}; return Range { start, end }; } TJP_CORE_HEADER_ONLY_INLINE String makeRange(Range range) { ToString string; auto [begin, end] = range; string << "bytes=" << begin << '-' << end; return string; } TJP_CORE_HEADER_ONLY_INLINE Optional parseContentRange(const String &s) { char unit[16]; char totalStr[32]; long long int start = -1, end = -1, total = -1; int matched = std::sscanf(s.data(), "%15s %lld-%lld/%31s", unit, &start, &end, totalStr); if (matched != 4 || std::strcmp(unit, "bytes") != 0) return {}; if (std::strcmp(totalStr, "*") == 0) total = -1; // unknown total else total = std::atoll(totalStr); return ContentRange { start, end, total }; } TJP_CORE_HEADER_ONLY_INLINE Optional getCacheControlMaxAge(const String &s_) { StringView s(s_); const StringView prefix = "max-age="; auto maxAgeP = s.find(prefix); if (maxAgeP == s.npos) return {}; auto maxAgeB = maxAgeP + prefix.size(); auto semi = s.find(';', maxAgeB); if (semi == s.npos) semi = s.size(); auto ageS = s.substr(maxAgeB, semi - maxAgeB); return core::from_string(ageS); } TJP_CORE_HEADER_ONLY_INLINE String makeContentRange(ContentRange range) { ToString string; auto [begin, end, of] = range; string << "bytes " << begin << '-' << end << '/'; if (of >= 0) string << of; else string << '*'; return string; } TJP_CORE_HEADER_ONLY_INLINE Optional getContentRange(const Headers &headers) { if (auto value = getHeader(headers, "Content-Range")) return parseContentRange(*value); return {}; } TJP_CORE_HEADER_ONLY_INLINE Optional parseContentSize(const String &s) { long long int size = 0; if (sscanf(s.c_str(), "%lld", &size) != 1) return {}; return size; } TJP_CORE_HEADER_ONLY_INLINE Optional getContentSize(const Headers &headers) { if (auto value = getHeader(headers, "Content-Length")) return parseContentSize(*value); return {}; } TJP_CORE_HEADER_ONLY_INLINE String makeDateHeader(std::time_t t) { std::tm* gm = std::gmtime(&t); // convert to UTC if (!gm) return {}; char buffer[128]; // Format: "Fri, 02 Jun 2023 18:24:37 GMT" if (std::strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", gm) == 0) return {}; return String(buffer); } TJP_CORE_HEADER_ONLY_INLINE String makeLastModifiedHeader(std::time_t t) { return makeDateHeader(t); } } // namespace