RESTinio
sendfile.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  Sendfile routine.
7 
8  @since v.0.4.3
9 */
10 
11 #pragma once
12 
13 #include <restinio/impl/include_fmtlib.hpp>
14 
15 #include <restinio/asio_include.hpp>
16 #include <restinio/string_view.hpp>
17 #include <restinio/exception.hpp>
18 
19 #include <restinio/detect_os.hpp>
20 
21 #include <array>
22 #include <chrono>
23 #include <filesystem>
24 #include <string>
25 
26 /*
27  Defenitions for:
28  file_descriptor_t
29  file_offset_t
30  file_size_t
31 */
32 
33 #if defined(RESTINIO_OS_WINDOWS)
34  #include "sendfile_defs_win.hpp"
35 #elif defined(RESTINIO_OS_UNIX) || defined(RESTINIO_OS_APPLE)
36  #include "sendfile_defs_posix.hpp"
37 #else
38  #if defined (RESTINIO_ENABLE_SENDFILE_DEFAULT_IMPL)
39  #include "sendfile_defs_default.hpp"
40  #else
41  #error "Sendfile not supported, to enable default implementation define RESTINIO_ENABLE_SENDFILE_DEFAULT_IMPL macro"
42  #endif
43 #endif
44 
45 namespace restinio
46 {
47 
48 //! Default chunk size for sendfile operation.
49 //! @since v.0.4.3
50 constexpr file_size_t sendfile_default_chunk_size = 1024 * 1024;
51 
52 //! Maximum size of a chunk
53 //! @since v.0.4.3
54 constexpr file_size_t sendfile_max_chunk_size = 1024 * 1024 * 1024;
55 
56 //
57 // sendfile_chunk_size_guarded_value_t
58 //
59 
60 //! A guard class for setting chunk size.
61 /*!
62  If chunk_size_value does not fit in [1, sendfile_max_chunk_size].
63  interval then it is shrinked to fit in the interval.
64 
65  @since v.0.4.3
66 */
68 {
69  //! Checks chunk_size_value and returns a value in [1, sendfile_max_chunk_size].
70  /*
71  - If chunk_size_value is zero returns 1.
72  - If chunk_size_value is greater than sendfile_max_chunk_size returns sendfile_max_chunk_size.
73  - Otherwise returns chunk_size_value itself.
74  */
75  static constexpr file_size_t
77  {
78  if( 0 == chunk_size_value )
79  return sendfile_default_chunk_size;
80 
81  if( sendfile_max_chunk_size < chunk_size_value )
82  return sendfile_max_chunk_size;
83 
84  return chunk_size_value;
85  }
86 
87  public:
88 
89  constexpr sendfile_chunk_size_guarded_value_t( file_size_t chunk_size_value ) noexcept
91  {}
92 
93  //! Get the valid value of a chunk size.
94  [[nodiscard]]
95  constexpr auto value() const noexcept { return m_chunk_size; }
96 
97  private:
98  //! Valid value of the chunk size.
100 };
101 
102 //
103 // file_descriptor_holder_t
104 //
105 
106 //! Wrapper class for working with native file handler.
107 /*
108  Class is responsible for managing file descriptor as resource.
109 
110  @since v.0.4.3
111 */
113 {
114  public:
115  //! Swap two descriptors.
116  friend void
118  {
119  using std::swap;
120  swap( left.m_file_descriptor, right.m_file_descriptor );
121  }
122 
123  //! Init constructor.
124  file_descriptor_holder_t( file_descriptor_t fd ) noexcept
125  : m_file_descriptor{ fd }
126  {}
127 
128  /** @name Copy semantics.
129  * @brief Not allowed.
130  */
131  ///@{
134  ///@}
135 
138  {
139  fdh.release();
140  }
141 
143  {
144  file_descriptor_holder_t tmp{ std::move( fdh ) };
145  swap( *this, tmp );
146 
147  return *this;
148  }
149 
151  {
152  if( is_valid() )
153  close_file( m_file_descriptor );
154  }
155 
156  //! Check if file descriptor is valid.
157  [[nodiscard]]
158  bool is_valid() const noexcept
159  {
160  return null_file_descriptor() != m_file_descriptor;
161  }
162 
163  //Get file descriptor.
164  [[nodiscard]]
165  file_descriptor_t fd() const noexcept
166  {
167  return m_file_descriptor;
168  }
169 
170  // Release stored descriptor.
171  void release() noexcept
172  {
173  m_file_descriptor = null_file_descriptor();
174  }
175 
176  private:
177  //! Target file descriptor.
179 };
180 
181 //
182 // file_meta_t
183 //
184 
185 //! Meta data of the file.
187 {
188  public:
189  friend void
190  swap( file_meta_t & r, file_meta_t & l ) noexcept
191  {
192  std::swap( r.m_file_total_size, l.m_file_total_size );
193  std::swap( r.m_last_modified_at, l.m_last_modified_at );
194  }
195 
196  file_meta_t() noexcept
197  {}
198 
200  file_size_t file_total_size,
201  std::chrono::system_clock::time_point last_modified_at ) noexcept
204  {}
205 
206  [[nodiscard]]
207  file_size_t file_total_size() const noexcept { return m_file_total_size; }
208 
209  [[nodiscard]]
210  auto last_modified_at() const noexcept { return m_last_modified_at; }
211 
212  private:
213  //! Total file size.
215 
216  //! Last modification date.
218 };
219 
220 //
221 // sendfile_t
222 //
223 
224 //! Send file write operation description.
225 /*!
226  Class gives a fluen-interface for setting various parameters
227  for performing send file operation.
228 
229  @since v.0.4.3
230 */
232 {
233  friend sendfile_t sendfile(
235  file_meta_t ,
236  file_size_t ) noexcept;
237 
239  //! File descriptor.
241  //! File meta data.
242  file_meta_t meta,
243  //! Send chunk size.
244  sendfile_chunk_size_guarded_value_t chunk ) noexcept
245  : m_file_descriptor{ std::move( fdh ) }
246  , m_meta{ meta }
247  , m_offset{ 0 }
249  , m_chunk_size{ chunk.value() }
251  {}
252 
253  public:
254  friend void
255  swap( sendfile_t & left, sendfile_t & right ) noexcept
256  {
257  using std::swap;
258  swap( left.m_file_descriptor, right.m_file_descriptor );
259  swap( left.m_meta, right.m_meta );
260  swap( left.m_offset, right.m_offset );
261  swap( left.m_size, right.m_size );
262  swap( left.m_chunk_size, right.m_chunk_size );
263  swap( left.m_timelimit, right.m_timelimit );
264  }
265 
266  /** @name Copy semantics.
267  * @brief Not allowed.
268  */
269  ///@{
270  sendfile_t( const sendfile_t & ) = delete;
271  sendfile_t & operator = ( const sendfile_t & ) = delete;
272  ///@}
273 
274  /** @name Move semantics.
275  * @brief After move sf prameter becomes invalid.
276  */
277  ///@{
278  sendfile_t( sendfile_t && sf ) noexcept
280  , m_meta{ sf.m_meta }
281  , m_offset{ sf.m_offset }
282  , m_size{ sf.m_size }
285  {}
286 
287  sendfile_t & operator = ( sendfile_t && sf ) noexcept
288  {
289  sendfile_t tmp{ std::move( sf ) };
290  swap( *this, tmp );
291 
292  return *this;
293  }
294  ///@}
295 
296  //! Check if file is valid.
297  [[nodiscard]]
298  bool is_valid() const noexcept { return m_file_descriptor.is_valid(); }
299 
300  //! Get file meta data.
301  [[nodiscard]]
302  const file_meta_t & meta() const
303  {
304  return m_meta;
305  }
306 
307  //! Get offset of data to write.
308  [[nodiscard]]
309  auto offset() const noexcept { return m_offset; }
310 
311  //! Get size of data to write.
312  [[nodiscard]]
313  auto size() const noexcept { return m_size; }
314 
315  /** @name Set file offset and size.
316  * @brief Tries to set offset parameter to offset_value and size to size value.
317  *
318  * If sendfile_t object is invalid then exception is thrown.
319  *
320  * If offset_value is a valid offset within current file then ofsett is
321  * set to new value. The size might be shrinked so to represent at most
322  * the length of file from a given offset.
323  */
324  ///@{
325  sendfile_t &
327  file_offset_t offset_value,
328  file_size_t size_value = std::numeric_limits< file_size_t >::max() ) &
329  {
331 
332  if( static_cast< file_size_t >( offset_value ) > m_meta.file_total_size() )
333  {
334  throw exception_t{
335  fmt::format(
336  RESTINIO_FMT_FORMAT_STRING(
337  "invalid file offset: {}, while file size is {}" ),
338  offset_value,
339  m_meta.file_total_size() ) };
340  }
341 
342  m_offset = offset_value;
343  m_size =
344  std::min< file_size_t >(
345  m_meta.file_total_size() - static_cast< file_size_t >( offset_value ),
346  size_value );
347 
348  return *this;
349  }
350 
351  sendfile_t &&
353  file_offset_t offset_value,
354  file_size_t size_value = std::numeric_limits< file_size_t >::max() ) &&
355  {
356  return std::move( this->offset_and_size( offset_value, size_value ) );
357  }
358  ///@}
359 
360  [[nodiscard]]
361  auto chunk_size() const noexcept { return m_chunk_size; }
362 
363  /** @name Set prefered chunk size to use in write operation.
364  * @brief Set the maximum possible size of the portion of data
365  * to be send at a single write file operation (from file to socket).
366  */
367  ///@{
368  sendfile_t &
370  {
372 
373  m_chunk_size = chunk.value();
374  return *this;
375  }
376 
377  //! Set prefered chunk size to use in write operation.
378  sendfile_t &&
380  {
381  return std::move( this->chunk_size( chunk ) );
382  }
383  ///@}
384 
385  [[nodiscard]]
386  auto timelimit() const noexcept { return m_timelimit; }
387 
388  /** @name Set timelimit on write operation..
389  * @brief Set the maximum dureation of this sendfile operation
390  * (the whole thing, not just a single iteration).
391  */
392  ///@{
393  sendfile_t &
394  timelimit( std::chrono::steady_clock::duration timelimit_value ) &
395  {
397 
398  m_timelimit = std::max( timelimit_value, std::chrono::steady_clock::duration::zero() );
399  return *this;
400  }
401 
402  sendfile_t &&
403  timelimit( std::chrono::steady_clock::duration timelimit_value ) &&
404  {
405  return std::move( this->timelimit( timelimit_value ) );
406  }
407  ///@}
408 
409  //! Get the file descriptor of a given sendfile operation.
410  [[nodiscard]]
412  file_descriptor() const noexcept
413  {
414  return m_file_descriptor.fd();
415  }
416 
417  //! Take away the file description form sendfile object.
418  /*!
419  This helper function takes the file description from sendfile
420  object. After it the sendfile object will hold invalid file
421  descriptor and will not try to close the file in the constructor.
422 
423  The take of the file description can be necessary, for example,
424  on Windows platform where an instance of Asio's random_access_handle
425  is used for file's content transmision. That instance also
426  closes the file in the destructor.
427 
428  @since v.0.4.9
429  */
430  [[nodiscard]]
433  {
434  return std::move(target.m_file_descriptor);
435  }
436 
437  private:
438  //! Check if stored file descriptor is valid, and throws if it is not.
439  void
441  {
442  if( !is_valid() )
443  {
444  throw exception_t{ "invalid file descriptor" };
445  }
446  }
447 
448  //! Native file descriptor.
450 
451  //! File meta data.
453 
454  //! Data offset within the file.
456  //! The size of data portion in file.
458 
459  //! A prefered chunk size for a single write call.
461 
462  //! Timelimit for writing all the given data.
463  /*!
464  Zero value stands for default write operation timeout.
465  */
467 };
468 
469 //
470 // sendfile()
471 //
472 
473 /** @name Functions for creating sendfile_t objects.
474  * @brief A group of function to create sendfile_t, that is convertad to writable items
475  * used as a part of response.
476  * @since v.0.4.3
477 */
478 ///@{
479 [[nodiscard]]
480 inline sendfile_t
482  //! Native file descriptor.
484  //! File meta data.
485  file_meta_t meta,
486  //! The max size of a data to be send on a single iteration.
487  file_size_t chunk_size = sendfile_default_chunk_size ) noexcept
488 {
489  return sendfile_t{ std::move( fd ), meta, chunk_size };
490 }
491 
492 [[nodiscard]]
493 inline sendfile_t
495  //! Path to file.
496  const char * file_path,
497  //! The max size of a data to be send on a single iteration.
498  file_size_t chunk_size = sendfile_default_chunk_size )
499 {
500  file_descriptor_holder_t fd{ open_file( file_path ) };
501 
502  auto meta = get_file_meta< file_meta_t >( fd.fd() );
503 
504  return sendfile( std::move( fd ), meta, chunk_size );
505 }
506 
507 [[nodiscard]]
508 inline sendfile_t
510  //! Path to file.
511  const std::string & file_path,
512  //! The max size of a data to be send on a single iteration.
513  file_size_t chunk_size = sendfile_default_chunk_size )
514 {
515  return sendfile( file_path.c_str(), chunk_size );
516 }
517 
518 [[nodiscard]]
519 inline sendfile_t
521  //! Path to file.
522  string_view_t file_path,
523  //! The max size of a data to be send on a single iteration.
524  file_size_t chunk_size = sendfile_default_chunk_size )
525 {
526  return
527  sendfile(
528  std::string{ file_path.data(), file_path.size() },
529  chunk_size );
530 }
531 
532 //FIXME: document this!
533 /*!
534  * @since v.0.7.1
535  */
536 [[nodiscard]]
537 inline sendfile_t
539  //! Path to file.
540  const std::filesystem::path & file_path,
541  //! The max size of a data to be send on a single iteration.
542  file_size_t chunk_size = sendfile_default_chunk_size )
543 {
544  file_descriptor_holder_t fd{ open_file( file_path ) };
545 
546  auto meta = get_file_meta< file_meta_t >( fd.fd() );
547 
548  return sendfile( std::move( fd ), meta, chunk_size );
549 }
550 ///@}
551 
552 } /* namespace restinio */
file_descriptor_holder_t & operator=(file_descriptor_holder_t &&fdh) noexcept
Definition: sendfile.hpp:142
friend void swap(file_descriptor_holder_t &left, file_descriptor_holder_t &right) noexcept
Swap two descriptors.
Definition: sendfile.hpp:117
constexpr file_size_t sendfile_max_chunk_size
Maximum size of a chunk.
Definition: sendfile.hpp:54
sendfile_t && offset_and_size(file_offset_t offset_value, file_size_t size_value=std::numeric_limits< file_size_t >::max()) &&
Definition: sendfile.hpp:352
auto chunk_size() const noexcept
Definition: sendfile.hpp:361
sendfile_t(sendfile_t &&sf) noexcept
Definition: sendfile.hpp:278
file_meta_t(file_size_t file_total_size, std::chrono::system_clock::time_point last_modified_at) noexcept
Definition: sendfile.hpp:199
const file_size_t m_chunk_size
Valid value of the chunk size.
Definition: sendfile.hpp:99
sendfile_t & timelimit(std::chrono::steady_clock::duration timelimit_value) &
Definition: sendfile.hpp:394
file_size_t m_size
The size of data portion in file.
Definition: sendfile.hpp:457
auto size() const noexcept
Get size of data to write.
Definition: sendfile.hpp:313
auto timelimit() const noexcept
Definition: sendfile.hpp:386
std::chrono::system_clock::time_point m_last_modified_at
Last modification date.
Definition: sendfile.hpp:217
file_descriptor_holder_t(const file_descriptor_holder_t &)=delete
Wrapper class for working with native file handler.
Definition: sendfile.hpp:112
file_offset_t m_offset
Data offset within the file.
Definition: sendfile.hpp:455
friend void swap(sendfile_t &left, sendfile_t &right) noexcept
Definition: sendfile.hpp:255
sendfile_t & operator=(const sendfile_t &)=delete
file_size_t m_file_total_size
Total file size.
Definition: sendfile.hpp:214
bool is_valid() const noexcept
Check if file is valid.
Definition: sendfile.hpp:298
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, std::optional< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in std::optional<Value_Type> if parameter exis...
Definition: value_or.hpp:64
file_descriptor_holder_t m_file_descriptor
Native file descriptor.
Definition: sendfile.hpp:449
sendfile_t & chunk_size(sendfile_chunk_size_guarded_value_t chunk) &
Definition: sendfile.hpp:369
Meta data of the file.
Definition: sendfile.hpp:186
static constexpr file_size_t clarify_chunk_size(file_size_t chunk_size_value) noexcept
Checks chunk_size_value and returns a value in [1, sendfile_max_chunk_size].
Definition: sendfile.hpp:76
sendfile_t(const sendfile_t &)=delete
auto offset() const noexcept
Get offset of data to write.
Definition: sendfile.hpp:309
file_descriptor_t m_file_descriptor
Target file descriptor.
Definition: sendfile.hpp:178
constexpr sendfile_chunk_size_guarded_value_t(file_size_t chunk_size_value) noexcept
Definition: sendfile.hpp:89
sendfile_t && chunk_size(sendfile_chunk_size_guarded_value_t chunk) &&
Set prefered chunk size to use in write operation.
Definition: sendfile.hpp:379
sendfile_t && timelimit(std::chrono::steady_clock::duration timelimit_value) &&
Definition: sendfile.hpp:403
const file_meta_t & meta() const
Get file meta data.
Definition: sendfile.hpp:302
void check_file_is_valid() const
Check if stored file descriptor is valid, and throws if it is not.
Definition: sendfile.hpp:440
sendfile_t sendfile(string_view_t file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition: sendfile.hpp:520
file_descriptor_t fd() const noexcept
Definition: sendfile.hpp:165
sendfile_t sendfile(file_descriptor_holder_t fd, file_meta_t meta, file_size_t chunk_size=sendfile_default_chunk_size) noexcept
Definition: sendfile.hpp:481
Send file write operation description.
Definition: sendfile.hpp:231
constexpr auto value() const noexcept
Get the valid value of a chunk size.
Definition: sendfile.hpp:95
file_meta_t() noexcept
Definition: sendfile.hpp:196
file_meta_t m_meta
File meta data.
Definition: sendfile.hpp:452
file_size_t m_chunk_size
A prefered chunk size for a single write call.
Definition: sendfile.hpp:460
friend void swap(file_meta_t &r, file_meta_t &l) noexcept
Definition: sendfile.hpp:190
sendfile_t & operator=(sendfile_t &&sf) noexcept
Definition: sendfile.hpp:287
file_descriptor_holder_t(file_descriptor_t fd) noexcept
Init constructor.
Definition: sendfile.hpp:124
auto last_modified_at() const noexcept
Definition: sendfile.hpp:210
friend file_descriptor_holder_t takeaway_file_descriptor(sendfile_t &target)
Take away the file description form sendfile object.
Definition: sendfile.hpp:432
std::chrono::steady_clock::duration m_timelimit
Timelimit for writing all the given data.
Definition: sendfile.hpp:466
file_descriptor_holder_t & operator=(const file_descriptor_holder_t &)=delete
sendfile_t sendfile(const std::filesystem::path &file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition: sendfile.hpp:538
sendfile_t(file_descriptor_holder_t fdh, file_meta_t meta, sendfile_chunk_size_guarded_value_t chunk) noexcept
Definition: sendfile.hpp:238
constexpr file_size_t sendfile_default_chunk_size
Default chunk size for sendfile operation.
Definition: sendfile.hpp:50
file_size_t file_total_size() const noexcept
Definition: sendfile.hpp:207
file_descriptor_holder_t(file_descriptor_holder_t &&fdh) noexcept
Definition: sendfile.hpp:136
sendfile_t sendfile(const char *file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition: sendfile.hpp:494
A guard class for setting chunk size.
Definition: sendfile.hpp:67
sendfile_t & offset_and_size(file_offset_t offset_value, file_size_t size_value=std::numeric_limits< file_size_t >::max()) &
Definition: sendfile.hpp:326
bool is_valid() const noexcept
Check if file descriptor is valid.
Definition: sendfile.hpp:158
file_descriptor_t file_descriptor() const noexcept
Get the file descriptor of a given sendfile operation.
Definition: sendfile.hpp:412