blob: 84351ee303bfdf387b36af22e20bae4920b1cf7c [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{
19 class writer;
Ed Tanous52e31622024-01-23 16:31:11 -080020 class reader;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060021 class value_type;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060022};
23
24enum class EncodingType
25{
26 Raw,
27 Base64,
28};
29
Ed Tanousb2896142024-01-31 15:25:47 -080030class HttpBody::value_type
Abhilash Rajub5f288d2023-11-08 22:32:44 -060031{
32 boost::beast::file_posix fileHandle;
Ed Tanous52e31622024-01-23 16:31:11 -080033 std::optional<size_t> fileSize;
34 std::string strBody;
Abhilash Rajub5f288d2023-11-08 22:32:44 -060035
36 public:
37 EncodingType encodingType = EncodingType::Raw;
38
39 ~value_type() = default;
40 value_type() = default;
41 explicit value_type(EncodingType enc) : encodingType(enc) {}
Ed Tanous52e31622024-01-23 16:31:11 -080042 explicit value_type(std::string_view str) : strBody(str) {}
Abhilash Rajub5f288d2023-11-08 22:32:44 -060043
Ed Tanous52e31622024-01-23 16:31:11 -080044 value_type(value_type&& other) noexcept :
45 fileHandle(std::move(other.fileHandle)), fileSize(other.fileSize),
46 strBody(std::move(other.strBody)), encodingType(other.encodingType)
47 {}
48
49 value_type& operator=(value_type&& other) noexcept
50 {
51 fileHandle = std::move(other.fileHandle);
52 fileSize = other.fileSize;
53 strBody = std::move(other.strBody);
54 encodingType = other.encodingType;
55
56 return *this;
57 }
58
59 // Overload copy constructor, because posix doesn't have dup(), but linux
60 // does
61 value_type(const value_type& other) :
62 fileSize(other.fileSize), strBody(other.strBody),
63 encodingType(other.encodingType)
64 {
65 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
66 }
67
68 value_type& operator=(const value_type& other)
69 {
70 if (this != &other)
71 {
72 fileSize = other.fileSize;
73 strBody = other.strBody;
74 encodingType = other.encodingType;
75 fileHandle.native_handle(dup(other.fileHandle.native_handle()));
76 }
77 return *this;
78 }
79
80 const boost::beast::file_posix& file()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060081 {
82 return fileHandle;
83 }
84
Ed Tanous52e31622024-01-23 16:31:11 -080085 std::string& str()
Abhilash Rajub5f288d2023-11-08 22:32:44 -060086 {
Ed Tanous52e31622024-01-23 16:31:11 -080087 return strBody;
88 }
89
90 const std::string& str() const
91 {
92 return strBody;
93 }
94
95 std::optional<size_t> payloadSize() const
96 {
97 if (!fileHandle.is_open())
98 {
99 return strBody.size();
100 }
101 if (fileSize)
102 {
103 if (encodingType == EncodingType::Base64)
104 {
105 return crow::utility::Base64Encoder::encodedSize(*fileSize);
106 }
107 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600108 return fileSize;
109 }
110
Ed Tanous52e31622024-01-23 16:31:11 -0800111 void clear()
112 {
113 strBody.clear();
114 strBody.shrink_to_fit();
115 fileHandle = boost::beast::file_posix();
116 fileSize = std::nullopt;
117 }
118
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600119 void open(const char* path, boost::beast::file_mode mode,
120 boost::system::error_code& ec)
121 {
122 fileHandle.open(path, mode, ec);
Ed Tanous52e31622024-01-23 16:31:11 -0800123 if (ec)
124 {
125 return;
126 }
127 boost::system::error_code ec2;
128 uint64_t size = fileHandle.size(ec2);
129 if (!ec2)
130 {
131 BMCWEB_LOG_INFO("File size was {} bytes", size);
132 fileSize = static_cast<size_t>(size);
133 }
134 else
135 {
136 BMCWEB_LOG_WARNING("Failed to read file size on {}", path);
137 }
138 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600139 }
140
141 void setFd(int fd, boost::system::error_code& ec)
142 {
143 fileHandle.native_handle(fd);
Ed Tanous52e31622024-01-23 16:31:11 -0800144
145 boost::system::error_code ec2;
146 uint64_t size = fileHandle.size(ec2);
147 if (!ec2)
148 {
149 if (size != 0 && size < std::numeric_limits<size_t>::max())
150 {
151 fileSize = static_cast<size_t>(size);
152 }
153 }
154 ec = {};
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600155 }
156};
157
Ed Tanousb2896142024-01-31 15:25:47 -0800158class HttpBody::writer
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600159{
160 public:
161 using const_buffers_type = boost::asio::const_buffer;
162
163 private:
164 std::string buf;
165 crow::utility::Base64Encoder encoder;
166
167 value_type& body;
Ed Tanous52e31622024-01-23 16:31:11 -0800168 size_t sent = 0;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600169 constexpr static size_t readBufSize = 4096;
170 std::array<char, readBufSize> fileReadBuf{};
171
172 public:
173 template <bool IsRequest, class Fields>
174 writer(boost::beast::http::header<IsRequest, Fields>& /*header*/,
175 value_type& bodyIn) :
Ed Tanous52e31622024-01-23 16:31:11 -0800176 body(bodyIn)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600177 {}
178
179 static void init(boost::beast::error_code& ec)
180 {
181 ec = {};
182 }
183
184 boost::optional<std::pair<const_buffers_type, bool>>
185 get(boost::beast::error_code& ec)
186 {
Ed Tanous52e31622024-01-23 16:31:11 -0800187 return getWithMaxSize(ec, std::numeric_limits<size_t>::max());
188 }
189
190 boost::optional<std::pair<const_buffers_type, bool>>
191 getWithMaxSize(boost::beast::error_code& ec, size_t maxSize)
192 {
193 std::pair<const_buffers_type, bool> ret;
194 if (!body.file().is_open())
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600195 {
Ed Tanous52e31622024-01-23 16:31:11 -0800196 size_t remain = body.str().size() - sent;
197 size_t toReturn = std::min(maxSize, remain);
198 ret.first = const_buffers_type(&body.str()[sent], toReturn);
199
200 sent += toReturn;
201 ret.second = sent < body.str().size();
202 BMCWEB_LOG_INFO("Returning {} bytes more={}", ret.first.size(),
203 ret.second);
204 return ret;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600205 }
Ed Tanous52e31622024-01-23 16:31:11 -0800206 size_t readReq = std::min(fileReadBuf.size(), maxSize);
207 size_t read = body.file().read(fileReadBuf.data(), readReq, ec);
208 if (ec)
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600209 {
Ed Tanous52e31622024-01-23 16:31:11 -0800210 BMCWEB_LOG_CRITICAL("Failed to read from file");
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600211 return boost::none;
212 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600213
214 std::string_view chunkView(fileReadBuf.data(), read);
Ed Tanous52e31622024-01-23 16:31:11 -0800215 BMCWEB_LOG_INFO("Read {} bytes from file", read);
216 // If the number of bytes read equals the amount requested, we haven't
217 // reached EOF yet
218 ret.second = read == readReq;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600219 if (body.encodingType == EncodingType::Base64)
220 {
221 buf.clear();
222 buf.reserve(
223 crow::utility::Base64Encoder::encodedSize(chunkView.size()));
224 encoder.encode(chunkView, buf);
225 if (!ret.second)
226 {
227 encoder.finalize(buf);
228 }
229 ret.first = const_buffers_type(buf.data(), buf.size());
230 }
231 else
232 {
233 ret.first = const_buffers_type(chunkView.data(), chunkView.size());
234 }
235 return ret;
236 }
237};
Ed Tanous52e31622024-01-23 16:31:11 -0800238
Ed Tanousb2896142024-01-31 15:25:47 -0800239class HttpBody::reader
Ed Tanous52e31622024-01-23 16:31:11 -0800240{
241 value_type& value;
242
243 public:
244 template <bool IsRequest, class Fields>
245 reader(boost::beast::http::header<IsRequest, Fields>& /*headers*/,
246 value_type& body) :
247 value(body)
248 {}
249
250 void init(const boost::optional<std::uint64_t>& contentLength,
251 boost::beast::error_code& ec)
252 {
253 if (contentLength)
254 {
255 if (!value.file().is_open())
256 {
257 value.str().reserve(static_cast<size_t>(*contentLength));
258 }
259 }
260 ec = {};
261 }
262
263 template <class ConstBufferSequence>
264 std::size_t put(const ConstBufferSequence& buffers,
265 boost::system::error_code& ec)
266 {
267 size_t extra = boost::beast::buffer_bytes(buffers);
268 for (const auto b : boost::beast::buffers_range_ref(buffers))
269 {
270 const char* ptr = static_cast<const char*>(b.data());
271 value.str() += std::string_view(ptr, b.size());
272 }
273 ec = {};
274 return extra;
275 }
276
277 static void finish(boost::system::error_code& ec)
278 {
279 ec = {};
280 }
281};
282
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600283} // namespace bmcweb