#include "HttpBuffer.h"
#include <DataIO.h>
#include <NetServicesDefs.h>
#include <String.h>
using namespace BPrivate::Network;
static constexpr std::array<std::byte, 2> kNewLine = {std::byte('\r'), std::byte('\n')};
HttpBuffer::HttpBuffer(size_t capacity)
{
fBuffer.reserve(capacity);
};
ssize_t
HttpBuffer::ReadFrom(BDataIO* source, std::optional<size_t> maxSize)
{
Flush();
auto currentSize = fBuffer.size();
auto remainingBufferSize = fBuffer.capacity() - currentSize;
if (maxSize && maxSize.value() < remainingBufferSize)
remainingBufferSize = maxSize.value();
fBuffer.resize(fBuffer.capacity());
ssize_t bytesRead = B_INTERRUPTED;
while (bytesRead == B_INTERRUPTED)
bytesRead = source->Read(fBuffer.data() + currentSize, remainingBufferSize);
if (bytesRead == B_WOULD_BLOCK || bytesRead == 0) {
fBuffer.resize(currentSize);
return bytesRead;
} else if (bytesRead < 0) {
throw BNetworkRequestError(
"BDataIO::Read()", BNetworkRequestError::NetworkError, bytesRead);
}
fBuffer.resize(currentSize + bytesRead);
return bytesRead;
}
size_t
HttpBuffer::WriteTo(HttpTransferFunction func, std::optional<size_t> maxSize)
{
if (RemainingBytes() == 0)
return 0;
auto size = RemainingBytes();
if (maxSize.has_value() && *maxSize < size)
size = *maxSize;
auto bytesWritten = func(fBuffer.data() + fCurrentOffset, size);
if (bytesWritten > size)
throw BRuntimeError(__PRETTY_FUNCTION__, "More bytes written than were made available");
fCurrentOffset += bytesWritten;
return bytesWritten;
}
std::optional<BString>
HttpBuffer::GetNextLine()
{
auto offset = fBuffer.cbegin() + fCurrentOffset;
auto result = std::search(offset, fBuffer.cend(), kNewLine.cbegin(), kNewLine.cend());
if (result == fBuffer.cend())
return std::nullopt;
BString line(
reinterpret_cast<const char*>(std::addressof(*offset)), std::distance(offset, result));
fCurrentOffset = std::distance(fBuffer.cbegin(), result) + 2;
return line;
}
size_t
HttpBuffer::RemainingBytes() const noexcept
{
return fBuffer.size() - fCurrentOffset;
}
void
HttpBuffer::Flush() noexcept
{
if (fCurrentOffset > 0) {
auto end = fBuffer.cbegin() + fCurrentOffset;
fBuffer.erase(fBuffer.cbegin(), end);
fCurrentOffset = 0;
}
}
void
HttpBuffer::Clear() noexcept
{
fBuffer.clear();
fCurrentOffset = 0;
}
std::string_view
HttpBuffer::Data() const noexcept
{
if (RemainingBytes() > 0) {
return std::string_view(
reinterpret_cast<const char*>(fBuffer.data()) + fCurrentOffset, RemainingBytes());
} else
return std::string_view();
}
HttpBuffer&
HttpBuffer::operator<<(const std::string_view& data)
{
if (data.size() > (fBuffer.capacity() - fBuffer.size())) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError,
"No capacity left in buffer to append data.");
}
for (const auto& character: data)
fBuffer.push_back(static_cast<const std::byte>(character));
return *this;
}