]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/http/basic_file_body.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / beast / http / basic_file_body.hpp
1 //
2 // Copyright (c) 2016-2017 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/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
11 #define BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
12
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/error.hpp>
15 #include <boost/beast/core/file_base.hpp>
16 #include <boost/beast/core/type_traits.hpp>
17 #include <boost/beast/http/message.hpp>
18 #include <boost/assert.hpp>
19 #include <boost/optional.hpp>
20 #include <algorithm>
21 #include <cstdio>
22 #include <cstdint>
23 #include <utility>
24
25 namespace boost {
26 namespace beast {
27 namespace http {
28
29 //[example_http_file_body_1
30
31 /** A message body represented by a file on the filesystem.
32
33 Messages with this type have bodies represented by a
34 file on the file system. When parsing a message using
35 this body type, the data is stored in the file pointed
36 to by the path, which must be writable. When serializing,
37 the implementation will read the file and present those
38 octets as the body content. This may be used to serve
39 content from a directory as part of a web service.
40
41 @tparam File The implementation to use for accessing files.
42 This type must meet the requirements of @b File.
43 */
44 template<class File>
45 struct basic_file_body
46 {
47 // Make sure the type meets the requirements
48 static_assert(is_file<File>::value,
49 "File requirements not met");
50
51 /// The type of File this body uses
52 using file_type = File;
53
54 // Algorithm for storing buffers when parsing.
55 class reader;
56
57 // Algorithm for retrieving buffers when serializing.
58 class writer;
59
60 // The type of the @ref message::body member.
61 class value_type;
62
63 /** Returns the size of the body
64
65 @param body The file body to use
66 */
67 static
68 std::uint64_t
69 size(value_type const& body);
70 };
71
72 //]
73
74 //[example_http_file_body_2
75
76 /** The type of the @ref message::body member.
77
78 Messages declared using `basic_file_body` will have this type for
79 the body member. This rich class interface allow the file to be
80 opened with the file handle maintained directly in the object,
81 which is attached to the message.
82 */
83 template<class File>
84 class basic_file_body<File>::value_type
85 {
86 // This body container holds a handle to the file
87 // when it is open, and also caches the size when set.
88
89 friend class reader;
90 friend class writer;
91 friend struct basic_file_body;
92
93 // This represents the open file
94 File file_;
95
96 // The cached file size
97 std::uint64_t file_size_ = 0;
98
99 public:
100 /** Destructor.
101
102 If the file is open, it is closed first.
103 */
104 ~value_type() = default;
105
106 /// Constructor
107 value_type() = default;
108
109 /// Constructor
110 value_type(value_type&& other) = default;
111
112 /// Move assignment
113 value_type& operator=(value_type&& other) = default;
114
115 /// Returns `true` if the file is open
116 bool
117 is_open() const
118 {
119 return file_.is_open();
120 }
121
122 /// Returns the size of the file if open
123 std::uint64_t
124 size() const
125 {
126 return file_size_;
127 }
128
129 /// Close the file if open
130 void
131 close();
132
133 /** Open a file at the given path with the specified mode
134
135 @param path The utf-8 encoded path to the file
136
137 @param mode The file mode to use
138
139 @param ec Set to the error, if any occurred
140 */
141 void
142 open(char const* path, file_mode mode, error_code& ec);
143
144 /** Set the open file
145
146 This function is used to set the open file. Any previously
147 set file will be closed.
148
149 @param file The file to set. The file must be open or else
150 an error occurs
151
152 @param ec Set to the error, if any occurred
153 */
154 void
155 reset(File&& file, error_code& ec);
156 };
157
158 template<class File>
159 void
160 basic_file_body<File>::
161 value_type::
162 close()
163 {
164 error_code ignored;
165 file_.close(ignored);
166 }
167
168 template<class File>
169 void
170 basic_file_body<File>::
171 value_type::
172 open(char const* path, file_mode mode, error_code& ec)
173 {
174 // Open the file
175 file_.open(path, mode, ec);
176 if(ec)
177 return;
178
179 // Cache the size
180 file_size_ = file_.size(ec);
181 if(ec)
182 {
183 close();
184 return;
185 }
186 }
187
188 template<class File>
189 void
190 basic_file_body<File>::
191 value_type::
192 reset(File&& file, error_code& ec)
193 {
194 // First close the file if open
195 if(file_.is_open())
196 {
197 error_code ignored;
198 file_.close(ignored);
199 }
200
201 // Take ownership of the new file
202 file_ = std::move(file);
203
204 // Cache the size
205 file_size_ = file_.size(ec);
206 }
207
208 // This is called from message::payload_size
209 template<class File>
210 std::uint64_t
211 basic_file_body<File>::
212 size(value_type const& body)
213 {
214 // Forward the call to the body
215 return body.size();
216 }
217
218 //]
219
220 //[example_http_file_body_3
221
222 /** Algorithm for retrieving buffers when serializing.
223
224 Objects of this type are created during serialization
225 to extract the buffers representing the body.
226 */
227 template<class File>
228 class basic_file_body<File>::writer
229 {
230 value_type& body_; // The body we are reading from
231 std::uint64_t remain_; // The number of unread bytes
232 char buf_[4096]; // Small buffer for reading
233
234 public:
235 // The type of buffer sequence returned by `get`.
236 //
237 using const_buffers_type =
238 boost::asio::const_buffer;
239
240 // Constructor.
241 //
242 // `m` holds the message we are serializing, which will
243 // always have the `basic_file_body` as the body type.
244 //
245 // Note that the message is passed by non-const reference.
246 // This is intentional, because reading from the file
247 // changes its "current position" which counts makes the
248 // operation logically not-const (although it is bitwise
249 // const).
250 //
251 // The BodyWriter concept allows the writer to choose
252 // whether to take the message by const reference or
253 // non-const reference. Depending on the choice, a
254 // serializer constructed using that body type will
255 // require the same const or non-const reference to
256 // construct.
257 //
258 // Readers which accept const messages usually allow
259 // the same body to be serialized by multiple threads
260 // concurrently, while readers accepting non-const
261 // messages may only be serialized by one thread at
262 // a time.
263 //
264 template<bool isRequest, class Fields>
265 writer(message<
266 isRequest, basic_file_body, Fields>& m);
267
268 // Initializer
269 //
270 // This is called before the body is serialized and
271 // gives the writer a chance to do something that might
272 // need to return an error code.
273 //
274 void
275 init(error_code& ec);
276
277 // This function is called zero or more times to
278 // retrieve buffers. A return value of `boost::none`
279 // means there are no more buffers. Otherwise,
280 // the contained pair will have the next buffer
281 // to serialize, and a `bool` indicating whether
282 // or not there may be additional buffers.
283 boost::optional<std::pair<const_buffers_type, bool>>
284 get(error_code& ec);
285 };
286
287 //]
288
289 //[example_http_file_body_4
290
291 // Here we just stash a reference to the path for later.
292 // Rather than dealing with messy constructor exceptions,
293 // we save the things that might fail for the call to `init`.
294 //
295 template<class File>
296 template<bool isRequest, class Fields>
297 basic_file_body<File>::
298 writer::
299 writer(message<isRequest, basic_file_body, Fields>& m)
300 : body_(m.body())
301 {
302 // The file must already be open
303 BOOST_ASSERT(body_.file_.is_open());
304
305 // Get the size of the file
306 remain_ = body_.file_size_;
307 }
308
309 // Initializer
310 template<class File>
311 void
312 basic_file_body<File>::
313 writer::
314 init(error_code& ec)
315 {
316 // The error_code specification requires that we
317 // either set the error to some value, or set it
318 // to indicate no error.
319 //
320 // We don't do anything fancy so set "no error"
321 ec.assign(0, ec.category());
322 }
323
324 // This function is called repeatedly by the serializer to
325 // retrieve the buffers representing the body. Our strategy
326 // is to read into our buffer and return it until we have
327 // read through the whole file.
328 //
329 template<class File>
330 auto
331 basic_file_body<File>::
332 writer::
333 get(error_code& ec) ->
334 boost::optional<std::pair<const_buffers_type, bool>>
335 {
336 // Calculate the smaller of our buffer size,
337 // or the amount of unread data in the file.
338 auto const amount = remain_ > sizeof(buf_) ?
339 sizeof(buf_) : static_cast<std::size_t>(remain_);
340
341 // Handle the case where the file is zero length
342 if(amount == 0)
343 {
344 // Modify the error code to indicate success
345 // This is required by the error_code specification.
346 //
347 // NOTE We use the existing category instead of calling
348 // into the library to get the generic category because
349 // that saves us a possibly expensive atomic operation.
350 //
351 ec.assign(0, ec.category());
352 return boost::none;
353 }
354
355 // Now read the next buffer
356 auto const nread = body_.file_.read(buf_, amount, ec);
357 if(ec)
358 return boost::none;
359
360 // Make sure there is forward progress
361 BOOST_ASSERT(nread != 0);
362 BOOST_ASSERT(nread <= remain_);
363
364 // Update the amount remaining based on what we got
365 remain_ -= nread;
366
367 // Return the buffer to the caller.
368 //
369 // The second element of the pair indicates whether or
370 // not there is more data. As long as there is some
371 // unread bytes, there will be more data. Otherwise,
372 // we set this bool to `false` so we will not be called
373 // again.
374 //
375 ec.assign(0, ec.category());
376 return {{
377 const_buffers_type{buf_, nread}, // buffer to return.
378 remain_ > 0 // `true` if there are more buffers.
379 }};
380 }
381
382 //]
383
384 //[example_http_file_body_5
385
386 /** Algorithm for storing buffers when parsing.
387
388 Objects of this type are created during parsing
389 to store incoming buffers representing the body.
390 */
391 template<class File>
392 class basic_file_body<File>::reader
393 {
394 value_type& body_; // The body we are writing to
395
396 public:
397 // Constructor.
398 //
399 // This is called after the header is parsed and
400 // indicates that a non-zero sized body may be present.
401 // `m` holds the message we are receiving, which will
402 // always have the `basic_file_body` as the body type.
403 //
404 template<bool isRequest, class Fields>
405 explicit
406 reader(
407 message<isRequest, basic_file_body, Fields>& m);
408
409 // Initializer
410 //
411 // This is called before the body is parsed and
412 // gives the reader a chance to do something that might
413 // need to return an error code. It informs us of
414 // the payload size (`content_length`) which we can
415 // optionally use for optimization.
416 //
417 void
418 init(boost::optional<std::uint64_t> const&, error_code& ec);
419
420 // This function is called one or more times to store
421 // buffer sequences corresponding to the incoming body.
422 //
423 template<class ConstBufferSequence>
424 std::size_t
425 put(ConstBufferSequence const& buffers,
426 error_code& ec);
427
428 // This function is called when writing is complete.
429 // It is an opportunity to perform any final actions
430 // which might fail, in order to return an error code.
431 // Operations that might fail should not be attemped in
432 // destructors, since an exception thrown from there
433 // would terminate the program.
434 //
435 void
436 finish(error_code& ec);
437 };
438
439 //]
440
441 //[example_http_file_body_6
442
443 // We don't do much in the reader constructor since the
444 // file is already open.
445 //
446 template<class File>
447 template<bool isRequest, class Fields>
448 basic_file_body<File>::
449 reader::
450 reader(message<isRequest, basic_file_body, Fields>& m)
451 : body_(m.body())
452 {
453 }
454
455 template<class File>
456 void
457 basic_file_body<File>::
458 reader::
459 init(
460 boost::optional<std::uint64_t> const& content_length,
461 error_code& ec)
462 {
463 // The file must already be open for writing
464 BOOST_ASSERT(body_.file_.is_open());
465
466 // We don't do anything with this but a sophisticated
467 // application might check available space on the device
468 // to see if there is enough room to store the body.
469 boost::ignore_unused(content_length);
470
471 // The error_code specification requires that we
472 // either set the error to some value, or set it
473 // to indicate no error.
474 //
475 // We don't do anything fancy so set "no error"
476 ec.assign(0, ec.category());
477 }
478
479 // This will get called one or more times with body buffers
480 //
481 template<class File>
482 template<class ConstBufferSequence>
483 std::size_t
484 basic_file_body<File>::
485 reader::
486 put(ConstBufferSequence const& buffers, error_code& ec)
487 {
488 // This function must return the total number of
489 // bytes transferred from the input buffers.
490 std::size_t nwritten = 0;
491
492 // Loop over all the buffers in the sequence,
493 // and write each one to the file.
494 for(auto it = boost::asio::buffer_sequence_begin(buffers);
495 it != boost::asio::buffer_sequence_end(buffers); ++it)
496 {
497 // Write this buffer to the file
498 boost::asio::const_buffer buffer = *it;
499 nwritten += body_.file_.write(
500 buffer.data(), buffer.size(), ec);
501 if(ec)
502 return nwritten;
503 }
504
505 // Indicate success
506 // This is required by the error_code specification
507 ec.assign(0, ec.category());
508
509 return nwritten;
510 }
511
512 // Called after writing is done when there's no error.
513 template<class File>
514 void
515 basic_file_body<File>::
516 reader::
517 finish(error_code& ec)
518 {
519 // This has to be cleared before returning, to
520 // indicate no error. The specification requires it.
521 ec.assign(0, ec.category());
522 }
523
524 //]
525
526 #if ! BOOST_BEAST_DOXYGEN
527 // operator<< is not supported for file_body
528 template<bool isRequest, class File, class Fields>
529 std::ostream&
530 operator<<(std::ostream& os, message<
531 isRequest, basic_file_body<File>, Fields> const& msg) = delete;
532 #endif
533
534 } // http
535 } // beast
536 } // boost
537
538 #endif