Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:31

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
0008 //
0009 
0010 #ifndef BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
0011 #define BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
0012 
0013 #include <boost/beast/core/detail/config.hpp>
0014 #include <boost/beast/core/error.hpp>
0015 #include <boost/beast/core/file_base.hpp>
0016 #include <boost/beast/http/message.hpp>
0017 #include <boost/assert.hpp>
0018 #include <boost/optional.hpp>
0019 #include <algorithm>
0020 #include <cstdio>
0021 #include <cstdint>
0022 #include <utility>
0023 
0024 namespace boost {
0025 namespace beast {
0026 namespace http {
0027 
0028 //[example_http_file_body_1
0029 
0030 /** A message body represented by a file on the filesystem.
0031 
0032     Messages with this type have bodies represented by a
0033     file on the file system. When parsing a message using
0034     this body type, the data is stored in the file pointed
0035     to by the path, which must be writable. When serializing,
0036     the implementation will read the file and present those
0037     octets as the body content. This may be used to serve
0038     content from a directory as part of a web service.
0039 
0040     @tparam File The implementation to use for accessing files.
0041     This type must meet the requirements of <em>File</em>.
0042 */
0043 template<class File>
0044 struct basic_file_body
0045 {
0046     // Make sure the type meets the requirements
0047     static_assert(is_file<File>::value,
0048         "File type requirements not met");
0049 
0050     /// The type of File this body uses
0051     using file_type = File;
0052 
0053     // Algorithm for storing buffers when parsing.
0054     class reader;
0055 
0056     // Algorithm for retrieving buffers when serializing.
0057     class writer;
0058 
0059     // The type of the @ref message::body member.
0060     class value_type;
0061 
0062     /** Returns the size of the body
0063 
0064         @param body The file body to use
0065     */
0066     static
0067     std::uint64_t
0068     size(value_type const& body);
0069 };
0070 
0071 //]
0072 
0073 //[example_http_file_body_2
0074 
0075 /** The type of the @ref message::body member.
0076 
0077     Messages declared using `basic_file_body` will have this type for
0078     the body member. This rich class interface allow the file to be
0079     opened with the file handle maintained directly in the object,
0080     which is attached to the message.
0081 */
0082 template<class File>
0083 class basic_file_body<File>::value_type
0084 {
0085     // This body container holds a handle to the file
0086     // when it is open, and also caches the size when set.
0087 
0088 #ifndef BOOST_BEAST_DOXYGEN
0089     friend class reader;
0090     friend class writer;
0091     friend struct basic_file_body;
0092 #endif
0093 
0094     // This represents the open file
0095     File file_;
0096 
0097     // The cached file size
0098     std::uint64_t file_size_ = 0;
0099 
0100 public:
0101     /** Destructor.
0102 
0103         If the file is open, it is closed first.
0104     */
0105     ~value_type() = default;
0106 
0107     /// Constructor
0108     value_type() = default;
0109 
0110     /// Constructor
0111     value_type(value_type&& other) = default;
0112 
0113     /// Move assignment
0114     value_type& operator=(value_type&& other) = default;
0115 
0116     /// Return the file
0117     File& file()
0118     {
0119         return file_;
0120     }
0121 
0122     /// Returns `true` if the file is open
0123     bool
0124     is_open() const
0125     {
0126         return file_.is_open();
0127     }
0128 
0129     /// Returns the size of the file if open
0130     std::uint64_t
0131     size() const
0132     {
0133         return file_size_;
0134     }
0135 
0136     /// Close the file if open
0137     void
0138     close();
0139 
0140     /** Open a file at the given path with the specified mode
0141 
0142         @param path The utf-8 encoded path to the file
0143 
0144         @param mode The file mode to use
0145 
0146         @param ec Set to the error, if any occurred
0147     */
0148     void
0149     open(char const* path, file_mode mode, error_code& ec);
0150 
0151     /** Set the open file
0152 
0153         This function is used to set the open file. Any previously
0154         set file will be closed.
0155 
0156         @param file The file to set. The file must be open or else
0157         an error occurs
0158 
0159         @param ec Set to the error, if any occurred
0160     */
0161     void
0162     reset(File&& file, error_code& ec);
0163 
0164     /** Set the cursor position of the file.
0165 
0166         This function can be used to move the cursor of the file ahead
0167         so that only a part gets read. This file will also adjust the
0168         value_type, in case the file is already part of a body.
0169 
0170         @param offset The offset in bytes from the beginning of the file
0171 
0172         @param ec Set to the error, if any occurred
0173     */
0174 
0175     void seek(std::uint64_t offset, error_code& ec);
0176 };
0177 
0178 template<class File>
0179 void
0180 basic_file_body<File>::
0181 value_type::
0182 close()
0183 {
0184     error_code ignored;
0185     file_.close(ignored);
0186 }
0187 
0188 template<class File>
0189 void
0190 basic_file_body<File>::
0191 value_type::
0192 open(char const* path, file_mode mode, error_code& ec)
0193 {
0194     // Open the file
0195     file_.open(path, mode, ec);
0196     if(ec)
0197         return;
0198 
0199     // Cache the size
0200     file_size_ = file_.size(ec);
0201     if(ec)
0202     {
0203         close();
0204         return;
0205     }
0206 }
0207 
0208 template<class File>
0209 void
0210 basic_file_body<File>::
0211 value_type::
0212 reset(File&& file, error_code& ec)
0213 {
0214     // First close the file if open
0215     if(file_.is_open())
0216     {
0217         error_code ignored;
0218         file_.close(ignored);
0219     }
0220 
0221     // Take ownership of the new file
0222     file_ = std::move(file);
0223 
0224     // Cache the size
0225     file_size_ = file_.size(ec);
0226 
0227     // Consider the offset
0228     if (!ec)
0229         file_size_ -= file_.pos(ec);
0230 }
0231 
0232 template<class File>
0233 void
0234 basic_file_body<File>::
0235 value_type::
0236 seek(std::uint64_t offset, error_code& ec)
0237 {
0238     file_.seek(offset, ec);
0239     // Cache the size
0240     if (!ec)
0241         file_size_ = file_.size(ec);
0242 
0243     // Consider the offset
0244     if (!ec)
0245         file_size_ -= file_.pos(ec);
0246 }
0247 
0248 
0249 // This is called from message::payload_size
0250 template<class File>
0251 std::uint64_t
0252 basic_file_body<File>::
0253 size(value_type const& body)
0254 {
0255     // Forward the call to the body
0256     return body.size();
0257 }
0258 
0259 //]
0260 
0261 //[example_http_file_body_3
0262 
0263 /** Algorithm for retrieving buffers when serializing.
0264 
0265     Objects of this type are created during serialization
0266     to extract the buffers representing the body.
0267 */
0268 template<class File>
0269 class basic_file_body<File>::writer
0270 {
0271     value_type& body_;                       // The body we are reading from
0272     std::uint64_t remain_;                   // The number of unread bytes
0273     char buf_[BOOST_BEAST_FILE_BUFFER_SIZE]; // Small buffer for reading
0274 
0275 public:
0276     // The type of buffer sequence returned by `get`.
0277     //
0278     using const_buffers_type =
0279         net::const_buffer;
0280 
0281     // Constructor.
0282     //
0283     // `h` holds the headers of the message we are
0284     // serializing, while `b` holds the body.
0285     //
0286     // Note that the message is passed by non-const reference.
0287     // This is intentional, because reading from the file
0288     // changes its "current position" which counts makes the
0289     // operation logically not-const (although it is bitwise
0290     // const).
0291     //
0292     // The BodyWriter concept allows the writer to choose
0293     // whether to take the message by const reference or
0294     // non-const reference. Depending on the choice, a
0295     // serializer constructed using that body type will
0296     // require the same const or non-const reference to
0297     // construct.
0298     //
0299     // Readers which accept const messages usually allow
0300     // the same body to be serialized by multiple threads
0301     // concurrently, while readers accepting non-const
0302     // messages may only be serialized by one thread at
0303     // a time.
0304     //
0305     template<bool isRequest, class Fields>
0306     writer(header<isRequest, Fields>& h, value_type& b);
0307 
0308     // Initializer
0309     //
0310     // This is called before the body is serialized and
0311     // gives the writer a chance to do something that might
0312     // need to return an error code.
0313     //
0314     void
0315     init(error_code& ec);
0316 
0317     // This function is called zero or more times to
0318     // retrieve buffers. A return value of `boost::none`
0319     // means there are no more buffers. Otherwise,
0320     // the contained pair will have the next buffer
0321     // to serialize, and a `bool` indicating whether
0322     // or not there may be additional buffers.
0323     boost::optional<std::pair<const_buffers_type, bool>>
0324     get(error_code& ec);
0325 };
0326 
0327 //]
0328 
0329 //[example_http_file_body_4
0330 
0331 // Here we just stash a reference to the path for later.
0332 // Rather than dealing with messy constructor exceptions,
0333 // we save the things that might fail for the call to `init`.
0334 //
0335 template<class File>
0336 template<bool isRequest, class Fields>
0337 basic_file_body<File>::
0338 writer::
0339 writer(header<isRequest, Fields>& h, value_type& b)
0340     : body_(b)
0341 {
0342     boost::ignore_unused(h);
0343 
0344     // The file must already be open
0345     BOOST_ASSERT(body_.file_.is_open());
0346 
0347     // Get the size of the file
0348     remain_ = body_.file_size_;
0349 }
0350 
0351 // Initializer
0352 template<class File>
0353 void
0354 basic_file_body<File>::
0355 writer::
0356 init(error_code& ec)
0357 {
0358     // The error_code specification requires that we
0359     // either set the error to some value, or set it
0360     // to indicate no error.
0361     //
0362     // We don't do anything fancy so set "no error"
0363     ec = {};
0364 }
0365 
0366 // This function is called repeatedly by the serializer to
0367 // retrieve the buffers representing the body. Our strategy
0368 // is to read into our buffer and return it until we have
0369 // read through the whole file.
0370 //
0371 template<class File>
0372 auto
0373 basic_file_body<File>::
0374 writer::
0375 get(error_code& ec) ->
0376     boost::optional<std::pair<const_buffers_type, bool>>
0377 {
0378     // Calculate the smaller of our buffer size,
0379     // or the amount of unread data in the file.
0380     auto const amount =  remain_ > sizeof(buf_) ?
0381         sizeof(buf_) : static_cast<std::size_t>(remain_);
0382 
0383     // Handle the case where the file is zero length
0384     if(amount == 0)
0385     {
0386         // Modify the error code to indicate success
0387         // This is required by the error_code specification.
0388         //
0389         // NOTE We use the existing category instead of calling
0390         //      into the library to get the generic category because
0391         //      that saves us a possibly expensive atomic operation.
0392         //
0393         ec = {};
0394         return boost::none;
0395     }
0396 
0397     // Now read the next buffer
0398     auto const nread = body_.file_.read(buf_, amount, ec);
0399     if(ec)
0400         return boost::none;
0401 
0402     if (nread == 0)
0403     {
0404         BOOST_BEAST_ASSIGN_EC(ec, error::short_read);
0405         return boost::none;
0406     }
0407 
0408     // Make sure there is forward progress
0409     BOOST_ASSERT(nread != 0);
0410     BOOST_ASSERT(nread <= remain_);
0411 
0412     // Update the amount remaining based on what we got
0413     remain_ -= nread;
0414 
0415     // Return the buffer to the caller.
0416     //
0417     // The second element of the pair indicates whether or
0418     // not there is more data. As long as there is some
0419     // unread bytes, there will be more data. Otherwise,
0420     // we set this bool to `false` so we will not be called
0421     // again.
0422     //
0423     ec = {};
0424     return {{
0425         const_buffers_type{buf_, nread},    // buffer to return.
0426         remain_ > 0                         // `true` if there are more buffers.
0427         }};
0428 }
0429 
0430 //]
0431 
0432 //[example_http_file_body_5
0433 
0434 /** Algorithm for storing buffers when parsing.
0435 
0436     Objects of this type are created during parsing
0437     to store incoming buffers representing the body.
0438 */
0439 template<class File>
0440 class basic_file_body<File>::reader
0441 {
0442     value_type& body_;  // The body we are writing to
0443 
0444 public:
0445     // Constructor.
0446     //
0447     // This is called after the header is parsed and
0448     // indicates that a non-zero sized body may be present.
0449     // `h` holds the received message headers.
0450     // `b` is an instance of `basic_file_body`.
0451     //
0452     template<bool isRequest, class Fields>
0453     explicit
0454     reader(header<isRequest, Fields>&h, value_type& b);
0455 
0456     // Initializer
0457     //
0458     // This is called before the body is parsed and
0459     // gives the reader a chance to do something that might
0460     // need to return an error code. It informs us of
0461     // the payload size (`content_length`) which we can
0462     // optionally use for optimization.
0463     //
0464     void
0465     init(boost::optional<std::uint64_t> const&, error_code& ec);
0466 
0467     // This function is called one or more times to store
0468     // buffer sequences corresponding to the incoming body.
0469     //
0470     template<class ConstBufferSequence>
0471     std::size_t
0472     put(ConstBufferSequence const& buffers,
0473         error_code& ec);
0474 
0475     // This function is called when writing is complete.
0476     // It is an opportunity to perform any final actions
0477     // which might fail, in order to return an error code.
0478     // Operations that might fail should not be attempted in
0479     // destructors, since an exception thrown from there
0480     // would terminate the program.
0481     //
0482     void
0483     finish(error_code& ec);
0484 };
0485 
0486 //]
0487 
0488 //[example_http_file_body_6
0489 
0490 // We don't do much in the reader constructor since the
0491 // file is already open.
0492 //
0493 template<class File>
0494 template<bool isRequest, class Fields>
0495 basic_file_body<File>::
0496 reader::
0497 reader(header<isRequest, Fields>& h, value_type& body)
0498     : body_(body)
0499 {
0500     boost::ignore_unused(h);
0501 }
0502 
0503 template<class File>
0504 void
0505 basic_file_body<File>::
0506 reader::
0507 init(
0508     boost::optional<std::uint64_t> const& content_length,
0509     error_code& ec)
0510 {
0511     // The file must already be open for writing
0512     BOOST_ASSERT(body_.file_.is_open());
0513 
0514     // We don't do anything with this but a sophisticated
0515     // application might check available space on the device
0516     // to see if there is enough room to store the body.
0517     boost::ignore_unused(content_length);
0518 
0519     // The error_code specification requires that we
0520     // either set the error to some value, or set it
0521     // to indicate no error.
0522     //
0523     // We don't do anything fancy so set "no error"
0524     ec = {};
0525 }
0526 
0527 // This will get called one or more times with body buffers
0528 //
0529 template<class File>
0530 template<class ConstBufferSequence>
0531 std::size_t
0532 basic_file_body<File>::
0533 reader::
0534 put(ConstBufferSequence const& buffers, error_code& ec)
0535 {
0536     // This function must return the total number of
0537     // bytes transferred from the input buffers.
0538     std::size_t nwritten = 0;
0539 
0540     // Loop over all the buffers in the sequence,
0541     // and write each one to the file.
0542     for(auto it = net::buffer_sequence_begin(buffers);
0543         it != net::buffer_sequence_end(buffers); ++it)
0544     {
0545         // Write this buffer to the file
0546         net::const_buffer buffer = *it;
0547         nwritten += body_.file_.write(
0548             buffer.data(), buffer.size(), ec);
0549         if(ec)
0550             return nwritten;
0551     }
0552 
0553     // Indicate success
0554     // This is required by the error_code specification
0555     ec = {};
0556 
0557     return nwritten;
0558 }
0559 
0560 // Called after writing is done when there's no error.
0561 template<class File>
0562 void
0563 basic_file_body<File>::
0564 reader::
0565 finish(error_code& ec)
0566 {
0567     // This has to be cleared before returning, to
0568     // indicate no error. The specification requires it.
0569     ec = {};
0570 }
0571 
0572 //]
0573 
0574 #if ! BOOST_BEAST_DOXYGEN
0575 // operator<< is not supported for file_body
0576 template<bool isRequest, class File, class Fields>
0577 std::ostream&
0578 operator<<(std::ostream&, message<
0579     isRequest, basic_file_body<File>, Fields> const&) = delete;
0580 #endif
0581 
0582 } // http
0583 } // beast
0584 } // boost
0585 
0586 #endif