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 17 using namespace std::literals; 18 using namespace BPrivate::Network; 19 20 21 /*! 22 \brief Set the \a request to serialize, and load the initial data into the \a buffer. 23 */ 24 void 25 HttpSerializer::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 */ 45 size_t 46 HttpSerializer::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 109 bool 110 HttpSerializer::_IsChunked() const noexcept 111 { 112 return fBodySize == std::nullopt; 113 } 114 115 116 size_t 117 HttpSerializer::_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