ゴミ箱
basic_file_body.hpp
Go to the documentation of this file.
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/config.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 
44 template<class File>
46 {
47  // Make sure the type meets the requirements
48  static_assert(is_file<File>::value,
49  "File requirements not met");
50 
52  using file_type = File;
53 
54  // Algorithm for retrieving buffers when serializing.
55  class reader;
56 
57  // Algorithm for storing buffers when parsing.
58  class writer;
59 
60  // The type of the @ref message::body member.
61  class value_type;
62 
67  static
68  std::uint64_t
69  size(value_type const& body);
70 };
71 
72 //]
73 
74 //[example_http_file_body_2
75 
83 template<class File>
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:
104  ~value_type() = default;
105 
107  value_type() = default;
108 
110  value_type(value_type&& other) = default;
111 
113  value_type& operator=(value_type&& other) = default;
114 
116  bool
117  is_open() const
118  {
119  return file_.is_open();
120  }
121 
123  std::uint64_t
124  size() const
125  {
126  return file_size_;
127  }
128 
130  void
131  close();
132 
141  void
142  open(char const* path, file_mode mode, error_code& ec);
143 
148  void
149  reset(File&& file, error_code& ec);
150 };
151 
152 template<class File>
153 void
157 {
158  error_code ignored;
159  file_.close(ignored);
160 }
161 
162 template<class File>
163 void
166 open(char const* path, file_mode mode, error_code& ec)
167 {
168  // Open the file
169  file_.open(path, mode, ec);
170  if(ec)
171  return;
172 
173  // Cache the size
174  file_size_ = file_.size(ec);
175  if(ec)
176  {
177  close();
178  return;
179  }
180 }
181 
182 template<class File>
183 void
186 reset(File&& file, error_code& ec)
187 {
188  // First close the file if open
189  if(file_.is_open())
190  {
191  error_code ignored;
192  file_.close(ignored);
193  }
194 
195  // Take ownership of the new file
196  file_ = std::move(file);
197 
198  // Cache the size
199  file_size_ = file_.size(ec);
200 }
201 
202 // This is called from message::payload_size
203 template<class File>
204 std::uint64_t
207 {
208  // Forward the call to the body
209  return body.size();
210 }
211 
212 //]
213 
214 //[example_http_file_body_3
215 
221 template<class File>
223 {
224  value_type& body_; // The body we are reading from
225  std::uint64_t remain_; // The number of unread bytes
226  char buf_[4096]; // Small buffer for reading
227 
228 public:
229  // The type of buffer sequence returned by `get`.
230  //
231  using const_buffers_type =
232  boost::asio::const_buffers_1;
233 
234  // Constructor.
235  //
236  // `m` holds the message we are sending, which will
237  // always have the `file_body` as the body type.
238  //
239  // Note that the message is passed by non-const reference.
240  // This is intentional, because reading from the file
241  // changes its "current position" which counts makes the
242  // operation logically not-const (although it is bitwise
243  // const).
244  //
245  // The BodyReader concept allows the reader to choose
246  // whether to take the message by const reference or
247  // non-const reference. Depending on the choice, a
248  // serializer constructed using that body type will
249  // require the same const or non-const reference to
250  // construct.
251  //
252  // Readers which accept const messages usually allow
253  // the same body to be serialized by multiple threads
254  // concurrently, while readers accepting non-const
255  // messages may only be serialized by one thread at
256  // a time.
257  //
258  template<bool isRequest, class Fields>
259  reader(message<
260  isRequest, basic_file_body, Fields>& m);
261 
262  // Initializer
263  //
264  // This is called before the body is serialized and
265  // gives the reader a chance to do something that might
266  // need to return an error code.
267  //
268  void
269  init(error_code& ec);
270 
271  // This function is called zero or more times to
272  // retrieve buffers. A return value of `boost::none`
273  // means there are no more buffers. Otherwise,
274  // the contained pair will have the next buffer
275  // to serialize, and a `bool` indicating whether
276  // or not there may be additional buffers.
277  boost::optional<std::pair<const_buffers_type, bool>>
278  get(error_code& ec);
279 };
280 
281 //]
282 
283 //[example_http_file_body_4
284 
285 // Here we just stash a reference to the path for later.
286 // Rather than dealing with messy constructor exceptions,
287 // we save the things that might fail for the call to `init`.
288 //
289 template<class File>
290 template<bool isRequest, class Fields>
292 reader::
294  : body_(m.body)
295 {
296  // The file must already be open
297  BOOST_ASSERT(body_.file_.is_open());
298 
299  // Get the size of the file
300  remain_ = body_.file_size_;
301 }
302 
303 // Initializer
304 template<class File>
305 void
307 reader::
309 {
310  // The error_code specification requires that we
311  // either set the error to some value, or set it
312  // to indicate no error.
313  //
314  // We don't do anything fancy so set "no error"
315  ec.assign(0, ec.category());
316 }
317 
318 // This function is called repeatedly by the serializer to
319 // retrieve the buffers representing the body. Our strategy
320 // is to read into our buffer and return it until we have
321 // read through the whole file.
322 //
323 template<class File>
324 auto
326 reader::
328  boost::optional<std::pair<const_buffers_type, bool>>
329 {
330  // Calculate the smaller of our buffer size,
331  // or the amount of unread data in the file.
332  auto const amount = remain_ > sizeof(buf_) ?
333  sizeof(buf_) : static_cast<std::size_t>(remain_);
334 
335  // Handle the case where the file is zero length
336  if(amount == 0)
337  {
338  // Modify the error code to indicate success
339  // This is required by the error_code specification.
340  //
341  // NOTE We use the existing category instead of calling
342  // into the library to get the generic category because
343  // that saves us a possibly expensive atomic operation.
344  //
345  ec.assign(0, ec.category());
346  return boost::none;
347  }
348 
349  // Now read the next buffer
350  auto const nread = body_.file_.read(buf_, amount, ec);
351  if(ec)
352  return boost::none;
353 
354  // Make sure there is forward progress
355  BOOST_ASSERT(nread != 0);
356  BOOST_ASSERT(nread <= remain_);
357 
358  // Update the amount remaining based on what we got
359  remain_ -= nread;
360 
361  // Return the buffer to the caller.
362  //
363  // The second element of the pair indicates whether or
364  // not there is more data. As long as there is some
365  // unread bytes, there will be more data. Otherwise,
366  // we set this bool to `false` so we will not be called
367  // again.
368  //
369  ec.assign(0, ec.category());
370  return {{
371  const_buffers_type{buf_, nread}, // buffer to return.
372  remain_ > 0 // `true` if there are more buffers.
373  }};
374 }
375 
376 //]
377 
378 //[example_http_file_body_5
379 
385 template<class File>
387 {
388  value_type& body_; // The body we are writing to
389 
390 public:
391  // Constructor.
392  //
393  // This is called after the header is parsed and
394  // indicates that a non-zero sized body may be present.
395  // `m` holds the message we are receiving, which will
396  // always have the `file_body` as the body type.
397  //
398  template<bool isRequest, class Fields>
399  explicit
400  writer(
402 
403  // Initializer
404  //
405  // This is called before the body is parsed and
406  // gives the writer a chance to do something that might
407  // need to return an error code. It informs us of
408  // the payload size (`content_length`) which we can
409  // optionally use for optimization.
410  //
411  void
412  init(boost::optional<std::uint64_t> const&, error_code& ec);
413 
414  // This function is called one or more times to store
415  // buffer sequences corresponding to the incoming body.
416  //
417  template<class ConstBufferSequence>
418  std::size_t
420  error_code& ec);
421 
422  // This function is called when writing is complete.
423  // It is an opportunity to perform any final actions
424  // which might fail, in order to return an error code.
425  // Operations that might fail should not be attemped in
426  // destructors, since an exception thrown from there
427  // would terminate the program.
428  //
429  void
430  finish(error_code& ec);
431 };
432 
433 //]
434 
435 //[example_http_file_body_6
436 
437 // We don't do much in the writer constructor since the
438 // file is already open.
439 //
440 template<class File>
441 template<bool isRequest, class Fields>
443 writer::
445  : body_(m.body)
446 {
447 }
448 
449 template<class File>
450 void
452 writer::
454  boost::optional<std::uint64_t> const& content_length,
455  error_code& ec)
456 {
457  // The file must already be open for writing
458  BOOST_ASSERT(body_.file_.is_open());
459 
460  // We don't do anything with this but a sophisticated
461  // application might check available space on the device
462  // to see if there is enough room to store the body.
463  boost::ignore_unused(content_length);
464 
465  // The error_code specification requires that we
466  // either set the error to some value, or set it
467  // to indicate no error.
468  //
469  // We don't do anything fancy so set "no error"
470  ec.assign(0, ec.category());
471 }
472 
473 // This will get called one or more times with body buffers
474 //
475 template<class File>
476 template<class ConstBufferSequence>
477 std::size_t
479 writer::
481 {
482  // This function must return the total number of
483  // bytes transferred from the input buffers.
484  std::size_t nwritten = 0;
485 
486  // Loop over all the buffers in the sequence,
487  // and write each one to the file.
488  for(boost::asio::const_buffer buffer : buffers)
489  {
490  // Write this buffer to the file
491  nwritten += body_.file_.write(
492  boost::asio::buffer_cast<void const*>(buffer),
493  boost::asio::buffer_size(buffer),
494  ec);
495  if(ec)
496  return nwritten;
497  }
498 
499  // Indicate success
500  // This is required by the error_code specification
501  ec.assign(0, ec.category());
502 
503  return nwritten;
504 }
505 
506 // Called after writing is done when there's no error.
507 template<class File>
508 void
510 writer::
512 {
513  // This has to be cleared before returning, to
514  // indicate no error. The specification requires it.
515  ec.assign(0, ec.category());
516 }
517 
518 //]
519 
520 #if ! BOOST_BEAST_DOXYGEN
521 // operator<< is not supported for file_body
522 template<bool isRequest, class File, class Fields>
525  isRequest, basic_file_body<File>, Fields> const& msg) = delete;
526 #endif
527 
528 } // http
529 } // beast
530 } // boost
531 
532 #endif
BufferSequence< boost::asio::const_buffer > ConstBufferSequence
Definition: type_traits.hpp:280
std::ostream & operator<<(std::ostream &os, message< isRequest, basic_file_body< File >, Fields > const &msg)=delete
file_mode
Definition: file_base.hpp:26
void init(sha1_context &ctx) noexcept
Definition: sha1.hpp:235
void finish(error_code &ec)
Definition: basic_file_body.hpp:511
Definition: async_result.hpp:20
Definition: file_stdio.hpp:27
detail::ostream_helper< DynamicBuffer, char, std::char_traits< char >, detail::basic_streambuf_movable::value > ostream(DynamicBuffer &buffer)
Definition: ostream.hpp:91
std::uint64_t size() const
Returns the size of the file if open.
Definition: basic_file_body.hpp:124
Definition: type_traits.hpp:25
void reset(File &&file, error_code &ec)
Definition: basic_file_body.hpp:186
detail::buffers_helper< ConstBufferSequence > buffers(ConstBufferSequence const &b)
Definition: ostream.hpp:50
Definition: basic_file_body.hpp:386
std::size_t put(ConstBufferSequence const &buffers, error_code &ec)
Definition: basic_file_body.hpp:480
writer(message< isRequest, basic_file_body, Fields > &m)
Definition: basic_file_body.hpp:444
Definition: beast_common.hpp:6
void init(boost::optional< std::uint64_t > const &, error_code &ec)
Definition: basic_file_body.hpp:453
Definition: basic_file_body.hpp:84
boost::system::error_code error_code
The type of error code used by the library.
Definition: error.hpp:21
bool is_open() const
Returns true if the file is open.
Definition: basic_file_body.hpp:117
Definition: type_traits.hpp:603
void open(char const *path, file_mode mode, error_code &ec)
Definition: basic_file_body.hpp:166
reader(message< isRequest, basic_file_body, Fields > &m)
Definition: basic_file_body.hpp:293
Definition: basic_file_body.hpp:222
Definition: basic_file_body.hpp:45
void close()
Close the file if open.
Definition: basic_file_body.hpp:156
static std::uint64_t size(value_type const &body)
Definition: basic_file_body.hpp:206
File file_type
The type of File this body uses.
Definition: basic_file_body.hpp:52
boost::optional< std::pair< const_buffers_type, bool > > get(error_code &ec)
Definition: basic_file_body.hpp:327
void init(error_code &ec)
Definition: basic_file_body.hpp:308
boost::asio::const_buffers_1 const_buffers_type
Definition: basic_file_body.hpp:232