flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:39:24 -05:00
commit fa54be052a
315 changed files with 49791 additions and 0 deletions

260
tjp/core/internet/Headers.cpp Executable file
View File

@@ -0,0 +1,260 @@
// TJP COPYRIGHT HEADER
#ifdef TJP_CORE_HEADER_ONLY
#pragma once
#endif
#include <tjp/core/header_only/compile.h>
#include "Headers.hpp"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstring>
#include <tjp/core/io/memstream.hpp>
#include <tjp/core/string/from_string.hpp>
#include <tjp/core/string/ToString.hpp>
#include <tjp/core/string/cmp_case.hpp>
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<std::time_t> 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<std::time_t>(t);
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<std::time_t> getLastModified(const Headers &headers)
{
if (auto value = getHeader(headers, "Last-Modified"))
return parseDate(*value);
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<std::time_t> getDate(const Headers &headers)
{
if (auto value = getHeader(headers, "Date"))
return parseDate(*value);
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<String> getType(const Headers &headers)
{
if (auto value = getHeader(headers, "Content-Type"))
return *value;
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<String> getCacheControl(const Headers &headers)
{
if (auto value = getHeader(headers, "Cache-Control"))
return *value;
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<String> getContentLocation(const Headers &headers)
{
if (auto value = getHeader(headers, "Content-Location"))
return *value;
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<String> getLocation(const Headers &headers)
{
if (auto value = getHeader(headers, "Location"))
return *value;
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<String> getCookie(const Headers &headers)
{
if (auto value = getHeader(headers, "Set-Cookie"))
return *value;
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<s64> 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<Range> 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<ContentRange> 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<s64> 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<s64>(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<ContentRange> getContentRange(const Headers &headers)
{
if (auto value = getHeader(headers, "Content-Range"))
return parseContentRange(*value);
return {};
}
TJP_CORE_HEADER_ONLY_INLINE
Optional<s64> 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<s64> 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