* Copyright 2022 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Niels Sascha Reedijk, niels.reedijk@gmail.com
*/
#include "HttpSerializer.h"
#include <DataIO.h>
#include <HttpRequest.h>
#include <NetServicesDefs.h>
#include "HttpBuffer.h"
using namespace std::literals;
using namespace BPrivate::Network;
\brief Set the \a request to serialize, and load the initial data into the \a buffer.
*/
void
HttpSerializer::SetTo(HttpBuffer& buffer, const BHttpRequest& request)
{
buffer.Clear();
request.SerializeHeaderTo(buffer);
fState = HttpSerializerState::Header;
if (auto requestBody = request.RequestBody()) {
fBody = requestBody->input.get();
if (requestBody->size) {
fBodySize = *(requestBody->size);
}
}
}
\brief Transfer the HTTP request to \a target while using \a buffer for intermediate storage.
\returns The number of body bytes written during the call.
*/
size_t
HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target)
{
bool finishing = false;
size_t bodyBytesWritten = 0;
while (!finishing) {
switch (fState) {
case HttpSerializerState::Uninitialized:
throw BRuntimeError(__PRETTY_FUNCTION__, "Invalid state: Uninitialized");
case HttpSerializerState::Header:
_WriteToTarget(buffer, target);
if (buffer.RemainingBytes() > 0) {
return 0;
}
if (fBody == nullptr) {
fState = HttpSerializerState::Done;
return 0;
} else if (_IsChunked())
throw BRuntimeError(
__PRETTY_FUNCTION__, "Chunked serialization not implemented");
else
fState = HttpSerializerState::Body;
break;
case HttpSerializerState::Body:
{
auto bytesWritten = _WriteToTarget(buffer, target);
bodyBytesWritten += bytesWritten;
fTransferredBodySize += bytesWritten;
if (buffer.RemainingBytes() > 0) {
finishing = true;
break;
}
if (fBodySize && fBodySize.value() == fTransferredBodySize) {
fState = HttpSerializerState::Done;
finishing = true;
}
break;
}
case HttpSerializerState::Done:
default:
finishing = true;
continue;
}
std::optional<size_t> maxReadSize = std::nullopt;
if (fBodySize)
maxReadSize = fBodySize.value() - fTransferredBodySize;
buffer.ReadFrom(fBody, maxReadSize);
}
return bodyBytesWritten;
}
bool
HttpSerializer::_IsChunked() const noexcept
{
return fBodySize == std::nullopt;
}
size_t
HttpSerializer::_WriteToTarget(HttpBuffer& buffer, BDataIO* target) const
{
size_t bytesWritten = 0;
buffer.WriteTo([target, &bytesWritten](const std::byte* buffer, size_t size) {
ssize_t result = B_INTERRUPTED;
while (result == B_INTERRUPTED) {
result = target->Write(buffer, size);
}
if (result <= 0 && result != B_WOULD_BLOCK) {
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError, result);
} else if (result > 0) {
bytesWritten += result;
return size_t(result);
} else {
return size_t(0);
}
});
return bytesWritten;
}