blob: 46070cc60f294da90b943068685db990984b9098 [file] [log] [blame]
Abhilash Rajub5f288d2023-11-08 22:32:44 -06001#pragma once
2
Ed Tanous52e31622024-01-23 16:31:11 -08003#include "logging.hpp"
Abhilash Rajub5f288d2023-11-08 22:32:44 -06004#include "utility.hpp"
5
Ed Tanous88c7c422024-04-06 08:52:40 -07006#include <fcntl.h>
Ed Tanous52e31622024-01-23 16:31:11 -08007#include <unistd.h>
8
9#include <boost/beast/core/buffers_range.hpp>
Abhilash Rajub5f288d2023-11-08 22:32:44 -060010#include <boost/beast/core/file_posix.hpp>
11#include <boost/beast/http/message.hpp>
12#include <boost/system/error_code.hpp>
13
Ed Tanous52e31622024-01-23 16:31:11 -080014#include <string_view>
15
Abhilash Rajub5f288d2023-11-08 22:32:44 -060016namespace bmcweb
17{
Ed Tanousb2896142024-01-31 15:25:47 -080018struct HttpBody
Abhilash Rajub5f288d2023-11-08 22:32:44 -060019{
Ed Tanous05016962024-03-19 11:53:11 -070020 // Body concept requires specific naming of classes
21 // NOLINTBEGIN(readability-identifier-naming)
Abhilash Rajub5f288d2023-11-08 22:32:44 -060022 class writer;
Ed Tanous52e31622024-01-23 16:31:11 -080023 class reader;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060024 class value_type;
Ed Tanous05016962024-03-19 11:53:11 -070025 // NOLINTEND(readability-identifier-naming)
Abhilash Rajub5f288d2023-11-08 22:32:44 -060026};
27
28enum class EncodingType
29{
30 Raw,
31 Base64,
32};
33
Ed Tanousb2896142024-01-31 15:25:47 -080034class HttpBody::value_type
Abhilash Rajub5f288d2023-11-08 22:32:44 -060035{
36 boost::beast::file_posix fileHandle;
Ed Tanous52e31622024-01-23 16:31:11 -080037 std::optional<size_t> fileSize;
38 std::string strBody;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060039
40 public:
41 EncodingType encodingType = EncodingType::Raw;
42
43 ~value_type() = default;
44 value_type() = default;
45 explicit value_type(EncodingType enc) : encodingType(enc) {}
Ed Tanous52e31622024-01-23 16:31:11 -080046 explicit value_type(std::string_view str) : strBody(str) {}
Abhilash Rajub5f288d2023-11-08 22:32:44 -060047
Ed Tanous52e31622024-01-23 16:31:11 -080048 value_type(value_type&& other) noexcept :
49 fileHandle(std::move(other.fileHandle)), fileSize(other.fileSize),
50 strBody(std::move(other.strBody)), encodingType(other.encodingType)
51 {}
52
53 value_type& operator=(value_type&& other) noexcept
54 {
55 fileHandle = std::move(other.fileHandle);
56 fileSize = other.fileSize;
57 strBody = std::move(other.strBody);
58 encodingType = other.encodingType;
59
60 return *this;
61 }
62
63 // Overload copy constructor, because posix doesn't have dup(), but linux
64 // does
65 value_type(const value_type& other) :
66 fileSize(other.fileSize), strBody(other.strBody),
67 encodingType(other.encodingType)
68 {
69 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
70 }
71
72 value_type& operator=(const value_type& other)
73 {
74 if (this != &other)
75 {
76 fileSize = other.fileSize;
77 strBody = other.strBody;
78 encodingType = other.encodingType;
79 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
80 }
81 return *this;
82 }
83
84 const boost::beast::file_posix& file()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060085 {
86 return fileHandle;
87 }
88
Ed Tanous52e31622024-01-23 16:31:11 -080089 std::string& str()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060090 {
Ed Tanous52e31622024-01-23 16:31:11 -080091 return strBody;
92 }
93
94 const std::string& str() const
95 {
96 return strBody;
97 }
98
99 std::optional<size_t> payloadSize() const
100 {
101 if (!fileHandle.is_open())
102 {
103 return strBody.size();
104 }
105 if (fileSize)
106 {
107 if (encodingType == EncodingType::Base64)
108 {
109 return crow::utility::Base64Encoder::encodedSize(*fileSize);
110 }
111 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600112 return fileSize;
113 }
114
Ed Tanous52e31622024-01-23 16:31:11 -0800115 void clear()
116 {
117 strBody.clear();
118 strBody.shrink_to_fit();
119 fileHandle = boost::beast::file_posix();
120 fileSize = std::nullopt;
Ed Tanous06fc9be2024-03-27 19:58:11 -0700121 encodingType = EncodingType::Raw;
Ed Tanous52e31622024-01-23 16:31:11 -0800122 }
123
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600124 void open(const char* path, boost::beast::file_mode mode,
125 boost::system::error_code& ec)
126 {
127 fileHandle.open(path, mode, ec);
Ed Tanous52e31622024-01-23 16:31:11 -0800128 if (ec)
129 {
130 return;
131 }
132 boost::system::error_code ec2;
133 uint64_t size = fileHandle.size(ec2);
134 if (!ec2)
135 {
136 BMCWEB_LOG_INFO("File size was {} bytes", size);
137 fileSize = static_cast<size_t>(size);
138 }
139 else
140 {
141 BMCWEB_LOG_WARNING("Failed to read file size on {}", path);
142 }
Ed Tanous88c7c422024-04-06 08:52:40 -0700143
144 int fadvise = posix_fadvise(fileHandle.native_handle(), 0, 0,
145 POSIX_FADV_SEQUENTIAL);
146 if (fadvise != 0)
147 {
148 BMCWEB_LOG_WARNING("Fasvise returned {} ignoring", fadvise);
149 }
Ed Tanous52e31622024-01-23 16:31:11 -0800150 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600151 }
152
153 void setFd(int fd, boost::system::error_code& ec)
154 {
155 fileHandle.native_handle(fd);
Ed Tanous52e31622024-01-23 16:31:11 -0800156
157 boost::system::error_code ec2;
158 uint64_t size = fileHandle.size(ec2);
159 if (!ec2)
160 {
161 if (size != 0 && size < std::numeric_limits<size_t>::max())
162 {
163 fileSize = static_cast<size_t>(size);
164 }
165 }
166 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600167 }
168};
169
Ed Tanousb2896142024-01-31 15:25:47 -0800170class HttpBody::writer
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600171{
172 public:
173 using const_buffers_type = boost::asio::const_buffer;
174
175 private:
176 std::string buf;
177 crow::utility::Base64Encoder encoder;
178
179 value_type& body;
Ed Tanous52e31622024-01-23 16:31:11 -0800180 size_t sent = 0;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600181 constexpr static size_t readBufSize = 4096;
182 std::array<char, readBufSize> fileReadBuf{};
183
184 public:
185 template <bool IsRequest, class Fields>
186 writer(boost::beast::http::header<IsRequest, Fields>& /*header*/,
187 value_type& bodyIn) :
Ed Tanous52e31622024-01-23 16:31:11 -0800188 body(bodyIn)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600189 {}
190
191 static void init(boost::beast::error_code& ec)
192 {
193 ec = {};
194 }
195
196 boost::optional<std::pair<const_buffers_type, bool>>
197 get(boost::beast::error_code& ec)
198 {
Ed Tanous52e31622024-01-23 16:31:11 -0800199 return getWithMaxSize(ec, std::numeric_limits<size_t>::max());
200 }
201
202 boost::optional<std::pair<const_buffers_type, bool>>
203 getWithMaxSize(boost::beast::error_code& ec, size_t maxSize)
204 {
205 std::pair<const_buffers_type, bool> ret;
206 if (!body.file().is_open())
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600207 {
Ed Tanous52e31622024-01-23 16:31:11 -0800208 size_t remain = body.str().size() - sent;
209 size_t toReturn = std::min(maxSize, remain);
210 ret.first = const_buffers_type(&body.str()[sent], toReturn);
211
212 sent += toReturn;
213 ret.second = sent < body.str().size();
214 BMCWEB_LOG_INFO("Returning {} bytes more={}", ret.first.size(),
215 ret.second);
216 return ret;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600217 }
Ed Tanous52e31622024-01-23 16:31:11 -0800218 size_t readReq = std::min(fileReadBuf.size(), maxSize);
219 size_t read = body.file().read(fileReadBuf.data(), readReq, ec);
220 if (ec)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600221 {
Ed Tanous52e31622024-01-23 16:31:11 -0800222 BMCWEB_LOG_CRITICAL("Failed to read from file");
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600223 return boost::none;
224 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600225
226 std::string_view chunkView(fileReadBuf.data(), read);
Ed Tanous52e31622024-01-23 16:31:11 -0800227 BMCWEB_LOG_INFO("Read {} bytes from file", read);
228 // If the number of bytes read equals the amount requested, we haven't
229 // reached EOF yet
230 ret.second = read == readReq;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600231 if (body.encodingType == EncodingType::Base64)
232 {
233 buf.clear();
234 buf.reserve(
235 crow::utility::Base64Encoder::encodedSize(chunkView.size()));
236 encoder.encode(chunkView, buf);
237 if (!ret.second)
238 {
239 encoder.finalize(buf);
240 }
241 ret.first = const_buffers_type(buf.data(), buf.size());
242 }
243 else
244 {
245 ret.first = const_buffers_type(chunkView.data(), chunkView.size());
246 }
247 return ret;
248 }
249};
Ed Tanous52e31622024-01-23 16:31:11 -0800250
Ed Tanousb2896142024-01-31 15:25:47 -0800251class HttpBody::reader
Ed Tanous52e31622024-01-23 16:31:11 -0800252{
253 value_type& value;
254
255 public:
256 template <bool IsRequest, class Fields>
257 reader(boost::beast::http::header<IsRequest, Fields>& /*headers*/,
258 value_type& body) :
259 value(body)
260 {}
261
262 void init(const boost::optional<std::uint64_t>& contentLength,
263 boost::beast::error_code& ec)
264 {
265 if (contentLength)
266 {
267 if (!value.file().is_open())
268 {
269 value.str().reserve(static_cast<size_t>(*contentLength));
270 }
271 }
272 ec = {};
273 }
274
275 template <class ConstBufferSequence>
276 std::size_t put(const ConstBufferSequence& buffers,
277 boost::system::error_code& ec)
278 {
279 size_t extra = boost::beast::buffer_bytes(buffers);
280 for (const auto b : boost::beast::buffers_range_ref(buffers))
281 {
282 const char* ptr = static_cast<const char*>(b.data());
283 value.str() += std::string_view(ptr, b.size());
284 }
285 ec = {};
286 return extra;
287 }
288
289 static void finish(boost::system::error_code& ec)
290 {
291 ec = {};
292 }
293};
294
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600295} // namespace bmcweb