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