xref: /haiku/src/kits/network/libnetservices2/HttpSerializer.cpp (revision 71e29bbeea760848317843508c711f2a0b446fbb)
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