1/* 2 * Copyright 2022 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Niels Sascha Reedijk, niels.reedijk@gmail.com 7 */ 8 9#include "HttpSerializer.h" 10 11#include <DataIO.h> 12#include <HttpRequest.h> 13#include <NetServicesDefs.h> 14 15#include "HttpBuffer.h" 16 17using namespace std::literals; 18using namespace BPrivate::Network; 19 20 21/*! 22 \brief Set the \a request to serialize, and load the initial data into the \a buffer. 23*/ 24void 25HttpSerializer::SetTo(HttpBuffer& buffer, const BHttpRequest& request) 26{ 27 buffer.Clear(); 28 request.SerializeHeaderTo(buffer); 29 fState = HttpSerializerState::Header; 30 31 if (auto requestBody = request.RequestBody()) { 32 fBody = requestBody->input.get(); 33 if (requestBody->size) { 34 fBodySize = *(requestBody->size); 35 } 36 } 37} 38 39 40/*! 41 \brief Transfer the HTTP request to \a target while using \a buffer for intermediate storage. 42 43 \returns The number of body bytes written during the call. 44*/ 45size_t 46HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target) 47{ 48 bool finishing = false; 49 size_t bodyBytesWritten = 0; 50 while (!finishing) { 51 switch (fState) { 52 case HttpSerializerState::Uninitialized: 53 throw BRuntimeError(__PRETTY_FUNCTION__, "Invalid state: Uninitialized"); 54 55 case HttpSerializerState::Header: 56 _WriteToTarget(buffer, target); 57 if (buffer.RemainingBytes() > 0) { 58 // There are more bytes to be processed; wait for the next iteration 59 return 0; 60 } 61 62 if (fBody == nullptr) { 63 fState = HttpSerializerState::Done; 64 return 0; 65 } else if (_IsChunked()) 66 // fState = HttpSerializerState::ChunkHeader; 67 throw BRuntimeError( 68 __PRETTY_FUNCTION__, "Chunked serialization not implemented"); 69 else 70 fState = HttpSerializerState::Body; 71 break; 72 73 case HttpSerializerState::Body: 74 { 75 auto bytesWritten = _WriteToTarget(buffer, target); 76 bodyBytesWritten += bytesWritten; 77 fTransferredBodySize += bytesWritten; 78 if (buffer.RemainingBytes() > 0) { 79 // did not manage to write all the bytes in the buffer; continue in the next 80 // round 81 finishing = true; 82 break; 83 } 84 85 if (fBodySize && fBodySize.value() == fTransferredBodySize) { 86 fState = HttpSerializerState::Done; 87 finishing = true; 88 } 89 break; 90 } 91 92 case HttpSerializerState::Done: 93 default: 94 finishing = true; 95 continue; 96 } 97 98 // Load more data into the buffer 99 std::optional<size_t> maxReadSize = std::nullopt; 100 if (fBodySize) 101 maxReadSize = fBodySize.value() - fTransferredBodySize; 102 buffer.ReadFrom(fBody, maxReadSize); 103 } 104 105 return bodyBytesWritten; 106} 107 108 109bool 110HttpSerializer::_IsChunked() const noexcept 111{ 112 return fBodySize == std::nullopt; 113} 114 115 116size_t 117HttpSerializer::_WriteToTarget(HttpBuffer& buffer, BDataIO* target) const 118{ 119 size_t bytesWritten = 0; 120 buffer.WriteTo([target, &bytesWritten](const std::byte* buffer, size_t size) { 121 ssize_t result = B_INTERRUPTED; 122 while (result == B_INTERRUPTED) { 123 result = target->Write(buffer, size); 124 } 125 126 if (result <= 0 && result != B_WOULD_BLOCK) { 127 throw BNetworkRequestError( 128 __PRETTY_FUNCTION__, BNetworkRequestError::NetworkError, result); 129 } else if (result > 0) { 130 bytesWritten += result; 131 return size_t(result); 132 } else { 133 return size_t(0); 134 } 135 }); 136 137 return bytesWritten; 138} 139