flatten 20260225
This commit is contained in:
260
tjp/core/internet/Headers.cpp
Executable file
260
tjp/core/internet/Headers.cpp
Executable 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
|
||||
Reference in New Issue
Block a user