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