blob: f2ec53fec2cace89ded2fe810424b5bc09e11812 [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 Tanous52e31622024-01-23 16:31:11 -08006#include <unistd.h>
7
8#include <boost/beast/core/buffers_range.hpp>
Abhilash Rajub5f288d2023-11-08 22:32:44 -06009#include <boost/beast/core/file_posix.hpp>
10#include <boost/beast/http/message.hpp>
11#include <boost/system/error_code.hpp>
12
Ed Tanous52e31622024-01-23 16:31:11 -080013#include <string_view>
14
Abhilash Rajub5f288d2023-11-08 22:32:44 -060015namespace bmcweb
16{
Ed Tanousb2896142024-01-31 15:25:47 -080017struct HttpBody
Abhilash Rajub5f288d2023-11-08 22:32:44 -060018{
Ed Tanous05016962024-03-19 11:53:11 -070019 // Body concept requires specific naming of classes
20 // NOLINTBEGIN(readability-identifier-naming)
Abhilash Rajub5f288d2023-11-08 22:32:44 -060021 class writer;
Ed Tanous52e31622024-01-23 16:31:11 -080022 class reader;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060023 class value_type;
Ed Tanous05016962024-03-19 11:53:11 -070024 // NOLINTEND(readability-identifier-naming)
Abhilash Rajub5f288d2023-11-08 22:32:44 -060025};
26
27enum class EncodingType
28{
29 Raw,
30 Base64,
31};
32
Ed Tanousb2896142024-01-31 15:25:47 -080033class HttpBody::value_type
Abhilash Rajub5f288d2023-11-08 22:32:44 -060034{
35 boost::beast::file_posix fileHandle;
Ed Tanous52e31622024-01-23 16:31:11 -080036 std::optional<size_t> fileSize;
37 std::string strBody;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060038
39 public:
40 EncodingType encodingType = EncodingType::Raw;
41
42 ~value_type() = default;
43 value_type() = default;
44 explicit value_type(EncodingType enc) : encodingType(enc) {}
Ed Tanous52e31622024-01-23 16:31:11 -080045 explicit value_type(std::string_view str) : strBody(str) {}
Abhilash Rajub5f288d2023-11-08 22:32:44 -060046
Ed Tanous52e31622024-01-23 16:31:11 -080047 value_type(value_type&& other) noexcept :
48 fileHandle(std::move(other.fileHandle)), fileSize(other.fileSize),
49 strBody(std::move(other.strBody)), encodingType(other.encodingType)
50 {}
51
52 value_type& operator=(value_type&& other) noexcept
53 {
54 fileHandle = std::move(other.fileHandle);
55 fileSize = other.fileSize;
56 strBody = std::move(other.strBody);
57 encodingType = other.encodingType;
58
59 return *this;
60 }
61
62 // Overload copy constructor, because posix doesn't have dup(), but linux
63 // does
64 value_type(const value_type& other) :
65 fileSize(other.fileSize), strBody(other.strBody),
66 encodingType(other.encodingType)
67 {
68 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
69 }
70
71 value_type& operator=(const value_type& other)
72 {
73 if (this != &other)
74 {
75 fileSize = other.fileSize;
76 strBody = other.strBody;
77 encodingType = other.encodingType;
78 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
79 }
80 return *this;
81 }
82
83 const boost::beast::file_posix& file()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060084 {
85 return fileHandle;
86 }
87
Ed Tanous52e31622024-01-23 16:31:11 -080088 std::string& str()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060089 {
Ed Tanous52e31622024-01-23 16:31:11 -080090 return strBody;
91 }
92
93 const std::string& str() const
94 {
95 return strBody;
96 }
97
98 std::optional<size_t> payloadSize() const
99 {
100 if (!fileHandle.is_open())
101 {
102 return strBody.size();
103 }
104 if (fileSize)
105 {
106 if (encodingType == EncodingType::Base64)
107 {
108 return crow::utility::Base64Encoder::encodedSize(*fileSize);
109 }
110 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600111 return fileSize;
112 }
113
Ed Tanous52e31622024-01-23 16:31:11 -0800114 void clear()
115 {
116 strBody.clear();
117 strBody.shrink_to_fit();
118 fileHandle = boost::beast::file_posix();
119 fileSize = std::nullopt;
Ed Tanous06fc9be2024-03-27 19:58:11 -0700120 encodingType = EncodingType::Raw;
Ed Tanous52e31622024-01-23 16:31:11 -0800121 }
122
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600123 void open(const char* path, boost::beast::file_mode mode,
124 boost::system::error_code& ec)
125 {
126 fileHandle.open(path, mode, ec);
Ed Tanous52e31622024-01-23 16:31:11 -0800127 if (ec)
128 {
129 return;
130 }
131 boost::system::error_code ec2;
132 uint64_t size = fileHandle.size(ec2);
133 if (!ec2)
134 {
135 BMCWEB_LOG_INFO("File size was {} bytes", size);
136 fileSize = static_cast<size_t>(size);
137 }
138 else
139 {
140 BMCWEB_LOG_WARNING("Failed to read file size on {}", path);
141 }
142 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600143 }
144
145 void setFd(int fd, boost::system::error_code& ec)
146 {
147 fileHandle.native_handle(fd);
Ed Tanous52e31622024-01-23 16:31:11 -0800148
149 boost::system::error_code ec2;
150 uint64_t size = fileHandle.size(ec2);
151 if (!ec2)
152 {
153 if (size != 0 && size < std::numeric_limits<size_t>::max())
154 {
155 fileSize = static_cast<size_t>(size);
156 }
157 }
158 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600159 }
160};
161
Ed Tanousb2896142024-01-31 15:25:47 -0800162class HttpBody::writer
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600163{
164 public:
165 using const_buffers_type = boost::asio::const_buffer;
166
167 private:
168 std::string buf;
169 crow::utility::Base64Encoder encoder;
170
171 value_type& body;
Ed Tanous52e31622024-01-23 16:31:11 -0800172 size_t sent = 0;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600173 constexpr static size_t readBufSize = 4096;
174 std::array<char, readBufSize> fileReadBuf{};
175
176 public:
177 template <bool IsRequest, class Fields>
178 writer(boost::beast::http::header<IsRequest, Fields>& /*header*/,
179 value_type& bodyIn) :
Ed Tanous52e31622024-01-23 16:31:11 -0800180 body(bodyIn)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600181 {}
182
183 static void init(boost::beast::error_code& ec)
184 {
185 ec = {};
186 }
187
188 boost::optional<std::pair<const_buffers_type, bool>>
189 get(boost::beast::error_code& ec)
190 {
Ed Tanous52e31622024-01-23 16:31:11 -0800191 return getWithMaxSize(ec, std::numeric_limits<size_t>::max());
192 }
193
194 boost::optional<std::pair<const_buffers_type, bool>>
195 getWithMaxSize(boost::beast::error_code& ec, size_t maxSize)
196 {
197 std::pair<const_buffers_type, bool> ret;
198 if (!body.file().is_open())
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600199 {
Ed Tanous52e31622024-01-23 16:31:11 -0800200 size_t remain = body.str().size() - sent;
201 size_t toReturn = std::min(maxSize, remain);
202 ret.first = const_buffers_type(&body.str()[sent], toReturn);
203
204 sent += toReturn;
205 ret.second = sent < body.str().size();
206 BMCWEB_LOG_INFO("Returning {} bytes more={}", ret.first.size(),
207 ret.second);
208 return ret;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600209 }
Ed Tanous52e31622024-01-23 16:31:11 -0800210 size_t readReq = std::min(fileReadBuf.size(), maxSize);
211 size_t read = body.file().read(fileReadBuf.data(), readReq, ec);
212 if (ec)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600213 {
Ed Tanous52e31622024-01-23 16:31:11 -0800214 BMCWEB_LOG_CRITICAL("Failed to read from file");
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600215 return boost::none;
216 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600217
218 std::string_view chunkView(fileReadBuf.data(), read);
Ed Tanous52e31622024-01-23 16:31:11 -0800219 BMCWEB_LOG_INFO("Read {} bytes from file", read);
220 // If the number of bytes read equals the amount requested, we haven't
221 // reached EOF yet
222 ret.second = read == readReq;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600223 if (body.encodingType == EncodingType::Base64)
224 {
225 buf.clear();
226 buf.reserve(
227 crow::utility::Base64Encoder::encodedSize(chunkView.size()));
228 encoder.encode(chunkView, buf);
229 if (!ret.second)
230 {
231 encoder.finalize(buf);
232 }
233 ret.first = const_buffers_type(buf.data(), buf.size());
234 }
235 else
236 {
237 ret.first = const_buffers_type(chunkView.data(), chunkView.size());
238 }
239 return ret;
240 }
241};
Ed Tanous52e31622024-01-23 16:31:11 -0800242
Ed Tanousb2896142024-01-31 15:25:47 -0800243class HttpBody::reader
Ed Tanous52e31622024-01-23 16:31:11 -0800244{
245 value_type& value;
246
247 public:
248 template <bool IsRequest, class Fields>
249 reader(boost::beast::http::header<IsRequest, Fields>& /*headers*/,
250 value_type& body) :
251 value(body)
252 {}
253
254 void init(const boost::optional<std::uint64_t>& contentLength,
255 boost::beast::error_code& ec)
256 {
257 if (contentLength)
258 {
259 if (!value.file().is_open())
260 {
261 value.str().reserve(static_cast<size_t>(*contentLength));
262 }
263 }
264 ec = {};
265 }
266
267 template <class ConstBufferSequence>
268 std::size_t put(const ConstBufferSequence& buffers,
269 boost::system::error_code& ec)
270 {
271 size_t extra = boost::beast::buffer_bytes(buffers);
272 for (const auto b : boost::beast::buffers_range_ref(buffers))
273 {
274 const char* ptr = static_cast<const char*>(b.data());
275 value.str() += std::string_view(ptr, b.size());
276 }
277 ec = {};
278 return extra;
279 }
280
281 static void finish(boost::system::error_code& ec)
282 {
283 ec = {};
284 }
285};
286
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600287} // namespace bmcweb