#include <HttpFields.h>
#include <algorithm>
#include <ctype.h>
#include <utility>
#include "HttpPrivate.h"
using namespace BPrivate::Network;
static inline bool
validate_value_string(const std::string_view& string)
{
for (auto it = string.cbegin(); it < string.cend(); it++) {
if ((*it >= 0 && *it < 32) || *it == 127 || *it == '\t')
return false;
}
return true;
}
static inline bool
iequals(const std::string_view& a, const std::string_view& b)
{
return std::equal(a.begin(), a.end(), b.begin(), b.end(),
[](char a, char b) { return tolower(a) == tolower(b); });
}
static inline std::string_view
trim(std::string_view in)
{
auto left = in.begin();
for (;; ++left) {
if (left == in.end())
return std::string_view();
if (!isspace(*left))
break;
}
auto right = in.end() - 1;
for (; right > left && isspace(*right); --right)
;
return std::string_view(left, std::distance(left, right) + 1);
}
BHttpFields::InvalidInput::InvalidInput(const char* origin, BString input)
:
BError(origin),
input(std::move(input))
{
}
const char*
BHttpFields::InvalidInput::Message() const noexcept
{
return "Invalid format or unsupported characters in input";
}
BString
BHttpFields::InvalidInput::DebugMessage() const
{
BString output = BError::DebugMessage();
output << "\t " << input << "\n";
return output;
}
BHttpFields::FieldName::FieldName() noexcept
:
fName(std::string_view())
{
}
BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept
:
fName(name)
{
}
BHttpFields::FieldName::FieldName(const FieldName& other) noexcept = default;
BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
:
fName(std::move(other.fName))
{
other.fName = std::string_view();
}
BHttpFields::FieldName& BHttpFields::FieldName::operator=(
const BHttpFields::FieldName& other) noexcept = default;
BHttpFields::FieldName&
BHttpFields::FieldName::operator=(BHttpFields::FieldName&& other) noexcept
{
fName = std::move(other.fName);
other.fName = std::string_view();
return *this;
}
bool
BHttpFields::FieldName::operator==(const BString& other) const noexcept
{
return iequals(fName, std::string_view(other.String()));
}
bool
BHttpFields::FieldName::operator==(const std::string_view& other) const noexcept
{
return iequals(fName, other);
}
bool
BHttpFields::FieldName::operator==(const BHttpFields::FieldName& other) const noexcept
{
return iequals(fName, other.fName);
}
BHttpFields::FieldName::operator std::string_view() const
{
return fName;
}
BHttpFields::Field::Field() noexcept
:
fName(std::string_view()),
fValue(std::string_view())
{
}
BHttpFields::Field::Field(const std::string_view& name, const std::string_view& value)
{
if (name.length() == 0 || !validate_http_token_string(name))
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
if (value.length() == 0 || !validate_value_string(value))
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
BString rawField(name.data(), name.size());
rawField << ": ";
rawField.Append(value.data(), value.size());
fName = std::string_view(rawField.String(), name.size());
fValue = std::string_view(rawField.String() + name.size() + 2, value.size());
fRawField = std::move(rawField);
}
BHttpFields::Field::Field(BString& field)
{
auto separatorIndex = field.FindFirst(':');
if (separatorIndex <= 0)
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, field);
auto name = std::string_view(field.String(), separatorIndex);
auto value = trim(std::string_view(field.String() + separatorIndex + 1));
if (name.length() == 0 || !validate_http_token_string(name))
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
if (value.length() == 0 || !validate_value_string(value))
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
fRawField = std::move(field);
fName = name;
fValue = value;
}
BHttpFields::Field::Field(const BHttpFields::Field& other)
:
fName(std::string_view()),
fValue(std::string_view())
{
if (other.IsEmpty()) {
fRawField = BString();
fName = std::string_view();
fValue = std::string_view();
} else {
fRawField = other.fRawField;
auto nameSize = other.Name().fName.size();
auto valueOffset = other.fValue.data() - other.fRawField.value().String();
fName = std::string_view((*fRawField).String(), nameSize);
fValue = std::string_view((*fRawField).String() + valueOffset, other.fValue.size());
}
}
BHttpFields::Field::Field(BHttpFields::Field&& other) noexcept
:
fRawField(std::move(other.fRawField)),
fName(std::move(other.fName)),
fValue(std::move(other.fValue))
{
other.fName.fName = std::string_view();
other.fValue = std::string_view();
}
BHttpFields::Field&
BHttpFields::Field::operator=(const BHttpFields::Field& other)
{
if (other.IsEmpty()) {
fRawField = BString();
fName = std::string_view();
fValue = std::string_view();
} else {
fRawField = other.fRawField;
auto nameSize = other.Name().fName.size();
auto valueOffset = other.fValue.data() - other.fRawField.value().String();
fName = std::string_view((*fRawField).String(), nameSize);
fValue = std::string_view((*fRawField).String() + valueOffset, other.fValue.size());
}
return *this;
}
BHttpFields::Field&
BHttpFields::Field::operator=(BHttpFields::Field&& other) noexcept
{
fRawField = std::move(other.fRawField);
fName = std::move(other.fName);
other.fName.fName = std::string_view();
fValue = std::move(other.fValue);
fValue = std::string_view();
return *this;
}
const BHttpFields::FieldName&
BHttpFields::Field::Name() const noexcept
{
return fName;
}
std::string_view
BHttpFields::Field::Value() const noexcept
{
return fValue;
}
std::string_view
BHttpFields::Field::RawField() const noexcept
{
if (fRawField)
return std::string_view((*fRawField).String(), (*fRawField).Length());
else
return std::string_view();
}
bool
BHttpFields::Field::IsEmpty() const noexcept
{
return !fRawField.has_value();
}
BHttpFields::BHttpFields()
{
}
BHttpFields::BHttpFields(std::initializer_list<BHttpFields::Field> fields)
{
AddFields(fields);
}
BHttpFields::BHttpFields(const BHttpFields& other) = default;
BHttpFields::BHttpFields(BHttpFields&& other)
:
fFields(std::move(other.fFields))
{
other.fFields.clear();
}
BHttpFields::~BHttpFields() noexcept
{
}
BHttpFields& BHttpFields::operator=(const BHttpFields& other) = default;
BHttpFields&
BHttpFields::operator=(BHttpFields&& other) noexcept
{
fFields = std::move(other.fFields);
other.fFields.clear();
return *this;
}
const BHttpFields::Field&
BHttpFields::operator[](size_t index) const
{
if (index >= fFields.size())
throw BRuntimeError(__PRETTY_FUNCTION__, "Index out of bounds");
auto it = fFields.cbegin();
std::advance(it, index);
return *it;
}
void
BHttpFields::AddField(const std::string_view& name, const std::string_view& value)
{
fFields.emplace_back(name, value);
}
void
BHttpFields::AddField(BString& field)
{
fFields.emplace_back(field);
}
void
BHttpFields::AddFields(std::initializer_list<Field> fields)
{
for (auto& field: fields) {
if (!field.IsEmpty())
fFields.push_back(std::move(field));
}
}
void
BHttpFields::RemoveField(const std::string_view& name) noexcept
{
for (auto it = FindField(name); it != end(); it = FindField(name)) {
fFields.erase(it);
}
}
void
BHttpFields::RemoveField(ConstIterator it) noexcept
{
fFields.erase(it);
}
void
BHttpFields::MakeEmpty() noexcept
{
fFields.clear();
}
BHttpFields::ConstIterator
BHttpFields::FindField(const std::string_view& name) const noexcept
{
for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
if ((*it).Name() == name)
return it;
}
return fFields.cend();
}
size_t
BHttpFields::CountFields() const noexcept
{
return fFields.size();
}
size_t
BHttpFields::CountFields(const std::string_view& name) const noexcept
{
size_t count = 0;
for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
if ((*it).Name() == name)
count += 1;
}
return count;
}
BHttpFields::ConstIterator
BHttpFields::begin() const noexcept
{
return fFields.cbegin();
}
BHttpFields::ConstIterator
BHttpFields::end() const noexcept
{
return fFields.cend();
}