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