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