Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/beast2
8 : //
9 :
10 : #ifndef BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
11 : #define BOOST_BEAST2_SERVER_BODY_SOURCE_HPP
12 :
13 : #include <boost/beast2/detail/config.hpp>
14 : #include <boost/beast2/detail/except.hpp>
15 : #include <boost/beast2/detail/type_traits.hpp>
16 : #include <boost/http_proto/error.hpp>
17 : #include <boost/buffers/buffer.hpp>
18 : #include <boost/buffers/copy.hpp>
19 : #include <boost/buffers/slice.hpp>
20 : #include <boost/buffers/data_source.hpp>
21 : #include <boost/buffers/read_source.hpp>
22 : #include <boost/core/span.hpp>
23 : #include <boost/system/error_code.hpp>
24 :
25 : namespace boost {
26 : namespace beast2 {
27 :
28 : /** Tag for customizing body construction
29 : */
30 : struct construct_body_tag {};
31 :
32 : //-----------------------------------------------
33 :
34 : /** A type erased HTTP message body.
35 :
36 : This type erases the specific body type used to represent the message body.
37 : It provides a uniform interface for reading the body data regardless of
38 : how the body is implemented. Accessing the bytes is achieved by calling
39 : @ref read which reads data into a caller-provided buffer. Alternatively,
40 : when @ref has_buffers returns `true` the body consists of buffers in memory,
41 : and they can be accessed directly by calling @ref get_buffers.
42 :
43 : Example bodies include:
44 : - in-memory buffers
45 : - file-backed bodies
46 : - generated bodies
47 :
48 : @note A body_source is movable but not copyable.
49 :
50 : Type-erased bodies can always be rewound to the beginning by calling
51 : @ref rewind. Therefore, bodies can be read multiple times.
52 :
53 : @par Thread Safety
54 : Unsafe.
55 : */
56 : class body_source
57 : {
58 : public:
59 : /** Destructor.
60 : */
61 : BOOST_BEAST2_DECL ~body_source();
62 :
63 : /** Constructor
64 :
65 : Default-constructed bodies are empty.
66 : */
67 : body_source() = default;
68 :
69 : /** Constructor
70 :
71 : The content of @p other is transferred to `*this`. The
72 : moved-from body is left empty, as if default-constructed.
73 : */
74 2 : body_source(body_source&& other) noexcept
75 2 : : impl_(other.impl_)
76 : {
77 2 : other.impl_ = nullptr;
78 2 : }
79 :
80 : /** Assignment
81 :
82 : The previous body, if any, is released and the
83 : content of @p other is transferred to `*this`.
84 : */
85 : BOOST_BEAST2_DECL
86 : body_source& operator=(body_source&& other) noexcept;
87 :
88 : /** Construct a streaming body source.
89 : */
90 : template<class ReadSource
91 : , typename std::enable_if<
92 : ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
93 : buffers::is_read_source<typename std::decay<ReadSource>::type>::value
94 : , int>::type = 0>
95 : body_source(ReadSource&& body);
96 :
97 : /** Construct a streaming body source with a known size.
98 : */
99 : template<class ReadSource
100 : , typename std::enable_if<
101 : ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
102 : buffers::is_read_source<typename std::decay<ReadSource>::type>::value
103 : , int>::type = 0>
104 : body_source(
105 : std::size_t known_size,
106 : ReadSource&& body);
107 :
108 : /** Construct a buffers body source.
109 : */
110 : template<class DataSource
111 : , typename std::enable_if<
112 : ! std::is_same<typename std::decay<DataSource>::type, body_source>::value &&
113 : buffers::is_data_source<typename std::decay<DataSource>::type>::value
114 : , int>::type = 0>
115 : body_source(DataSource&& body);
116 :
117 : //template<class T>
118 : //body(T&& t);
119 :
120 : /** Return `true` if the size of the body is known.
121 : */
122 10 : bool has_size() const noexcept
123 : {
124 10 : if(impl_)
125 8 : return impl_->has_size();
126 2 : return true; // empty
127 : }
128 :
129 : /** Return `true` if the body consists of buffers in memory.
130 : When the body consists of buffers in memory, they can
131 : also be accessed directly using @ref get_buffers.
132 : */
133 10 : bool has_buffers() const noexcept
134 : {
135 10 : if(impl_)
136 8 : return impl_->has_buffers();
137 2 : return true; // empty
138 : }
139 :
140 : /** Return the size of the body, if available.
141 : @throw std::invalid_argument if @ref has_size returns `false`.
142 : @return The size of the body in bytes.
143 : */
144 10 : auto size() const -> std::size_t
145 : {
146 10 : if(impl_)
147 8 : return impl_->size();
148 2 : return 0; // empty
149 : }
150 :
151 : /** Return the buffers representing the body, if available.
152 : @throw std::invalid_argument if @ref has_buffers returns `false`.
153 : @return A span of buffers representing the body.
154 : */
155 10 : auto data() const ->
156 : span<buffers::const_buffer const>
157 : {
158 10 : if(impl_)
159 8 : return impl_->data();
160 2 : return {}; // empty
161 : }
162 :
163 : /** Rewind the body to the beginning.
164 : This allows the body to be accessed from the start when calling @read.
165 : */
166 154 : void rewind()
167 : {
168 154 : if(impl_)
169 136 : return impl_->rewind();
170 : // empty
171 : }
172 :
173 : /** Read from the body into a caller-provided buffer.
174 :
175 : @param dest A pointer to the buffer to read into.
176 : @param n The maximum number of bytes to read.
177 : @param ec Set to the error, if any occurred.
178 : @return The number of bytes read, which may be
179 : less than the number requested. A return value of
180 : zero indicates end-of-body.
181 : */
182 : auto
183 320 : read(void* dest, std::size_t n,
184 : system::error_code& ec) ->
185 : std::size_t
186 : {
187 320 : if(impl_)
188 304 : return impl_->read(dest, n, ec);
189 : // empty
190 16 : ec = http::error::end_of_stream;
191 16 : return 0;
192 : }
193 :
194 : private:
195 : struct impl
196 : {
197 6 : virtual ~impl() = default;
198 4 : virtual bool has_size() const noexcept { return false; }
199 5 : virtual bool has_buffers() const noexcept { return false; }
200 4 : virtual std::size_t size() const
201 : {
202 4 : detail::throw_invalid_argument();
203 : }
204 5 : virtual auto data() const ->
205 : span<buffers::const_buffer const>
206 : {
207 5 : detail::throw_invalid_argument();
208 : }
209 : virtual void rewind() = 0;
210 : virtual std::size_t read(
211 : void* dest, std::size_t n,
212 : system::error_code& ec) = 0;
213 : };
214 :
215 : impl* impl_ = nullptr;
216 : };
217 :
218 : //-----------------------------------------------
219 :
220 : template<class ReadSource
221 : , typename std::enable_if<
222 : ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
223 : buffers::is_read_source<typename std::decay<ReadSource>::type>::value
224 : , int>::type>
225 3 : body_source::
226 : body_source(
227 3 : ReadSource&& body)
228 : {
229 : struct model : impl
230 : {
231 : system::error_code ec_;
232 : typename std::decay<ReadSource>::type body_;
233 :
234 3 : explicit model(ReadSource&& body)
235 3 : : body_(std::forward<ReadSource>(body))
236 : {
237 3 : }
238 :
239 68 : void rewind() override
240 : {
241 68 : ec_ = {};
242 68 : body_.rewind();
243 68 : }
244 :
245 154 : std::size_t read(
246 : void* dest,
247 : std::size_t size,
248 : system::error_code& ec) override
249 : {
250 154 : if(ec_.failed())
251 : {
252 0 : ec = ec_;
253 0 : return 0;
254 : }
255 154 : auto nread = body_.read(
256 154 : buffers::mutable_buffer(dest, size), ec);
257 154 : ec_ = ec;
258 154 : return nread;
259 : }
260 : };
261 :
262 3 : auto p = ::operator new(sizeof(model));
263 3 : impl_ = ::new(p) model(
264 : std::forward<ReadSource>(body));
265 3 : }
266 :
267 : /** Construct a streaming body source with a known size.
268 : */
269 : template<class ReadSource
270 : , typename std::enable_if<
271 : ! std::is_same<typename std::decay<ReadSource>::type, body_source>::value &&
272 : buffers::is_read_source<typename std::decay<ReadSource>::type>::value
273 : , int>::type>
274 1 : body_source::
275 : body_source(
276 : std::size_t known_size,
277 1 : ReadSource&& body)
278 : {
279 : struct model : impl
280 : {
281 : std::size_t size_;
282 : system::error_code ec_;
283 : typename std::decay<ReadSource>::type body_;
284 :
285 1 : model(
286 : ReadSource&& body,
287 : std::size_t known_size)
288 1 : : size_(known_size)
289 1 : , body_(std::forward<ReadSource>(body))
290 : {
291 1 : }
292 :
293 1 : bool has_size() const noexcept override
294 : {
295 1 : return true;
296 : }
297 :
298 1 : std::size_t size() const override
299 : {
300 1 : return size_;
301 : }
302 :
303 17 : void rewind() override
304 : {
305 17 : ec_ = {};
306 17 : body_.rewind();
307 17 : }
308 :
309 24 : std::size_t read(
310 : void* dest,
311 : std::size_t size,
312 : system::error_code& ec) override
313 : {
314 24 : if(ec_.failed())
315 : {
316 0 : ec = ec_;
317 0 : return 0;
318 : }
319 24 : auto nread = body_.read(
320 24 : buffers::mutable_buffer(dest, size), ec);
321 24 : ec_ = ec;
322 24 : return nread;
323 : }
324 : };
325 :
326 1 : auto p = ::operator new(sizeof(model));
327 1 : impl_ = ::new(p) model(
328 : std::forward<ReadSource>(body), known_size);
329 1 : }
330 :
331 : /** Construct a buffers body source.
332 : */
333 : template<class DataSource
334 : , typename std::enable_if<
335 : ! std::is_same<typename std::decay<DataSource>::type, body_source>::value &&
336 : buffers::is_data_source<typename std::decay<DataSource>::type>::value
337 : , int>::type>
338 2 : body_source::
339 : body_source(
340 2 : DataSource&& body)
341 : {
342 : struct model : impl
343 : {
344 : typename std::decay<DataSource>::type body_;
345 : std::size_t size_ = 0;
346 : span<boost::buffers::const_buffer> bs_;
347 : std::size_t nread_ = 0;
348 :
349 2 : explicit model(
350 : DataSource&& body) noexcept
351 2 : : body_(std::forward<DataSource>(body))
352 : {
353 2 : auto const& data = body_.data();
354 2 : auto const& end = buffers::end(data);
355 2 : auto p = reinterpret_cast<
356 : buffers::const_buffer*>(this+1);
357 2 : std::size_t length = 0;
358 4 : for(auto it = buffers::begin(data); it != end; ++it)
359 : {
360 2 : boost::buffers::const_buffer cb(*it);
361 2 : size_ += cb.size();
362 2 : *p++ = cb;
363 2 : ++length;
364 : }
365 2 : bs_ = { reinterpret_cast<
366 : buffers::const_buffer*>(this + 1), length };
367 2 : }
368 :
369 3 : bool has_size() const noexcept override
370 : {
371 3 : return true;
372 : }
373 :
374 3 : bool has_buffers() const noexcept override
375 : {
376 3 : return true;
377 : }
378 :
379 3 : std::size_t size() const override
380 : {
381 3 : return size_;
382 : }
383 :
384 : span<buffers::const_buffer const>
385 3 : data() const override
386 : {
387 3 : return bs_;
388 : }
389 :
390 51 : void rewind() override
391 : {
392 51 : nread_ = 0;
393 51 : }
394 :
395 126 : std::size_t read(
396 : void* dest,
397 : std::size_t n0,
398 : system::error_code& ec) override
399 : {
400 126 : std::size_t n = buffers::copy(
401 126 : buffers::mutable_buffer(dest, n0),
402 126 : buffers::sans_prefix(bs_, nread_));
403 126 : nread_ += n;
404 126 : if(nread_ >= size_)
405 48 : ec = http::error::end_of_stream;
406 : else
407 78 : ec = {};
408 126 : return n;
409 : }
410 : };
411 :
412 2 : std::size_t length = 0;
413 2 : auto const& data = body.data();
414 2 : auto const& end = buffers::end(data);
415 2 : for(auto it = buffers::begin(data);
416 4 : it != end; ++it)
417 : {
418 2 : ++length;
419 : }
420 :
421 : // VFALCO this requires DataSource to be nothrow
422 : // move constructible for strong exception safety.
423 4 : auto p = ::operator new(sizeof(model) +
424 2 : length * sizeof(buffers::const_buffer));
425 2 : impl_ = ::new(p) model(
426 : std::forward<DataSource>(body));
427 2 : }
428 :
429 : } // beast2
430 : } // boost
431 :
432 : #endif
|