/* * 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 #include #include #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) { // There are more bytes to be processed; wait for the next iteration return 0; } if (fBody == nullptr) { fState = HttpSerializerState::Done; return 0; } else if (_IsChunked()) // fState = HttpSerializerState::ChunkHeader; 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) { // did not manage to write all the bytes in the buffer; continue in the next // round finishing = true; break; } if (fBodySize && fBodySize.value() == fTransferredBodySize) { fState = HttpSerializerState::Done; finishing = true; } break; } case HttpSerializerState::Done: default: finishing = true; continue; } // Load more data into the buffer std::optional 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; }