|
||||
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
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |