RESTinio
Loading...
Searching...
No Matches
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
45namespace restinio
46{
47
48//! Default chunk size for sendfile operation.
49//! @since v.0.4.3
50constexpr file_size_t sendfile_default_chunk_size = 1024 * 1024;
51
52//! Maximum size of a chunk
53//! @since v.0.4.3
54constexpr 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
76 clarify_chunk_size( file_size_t chunk_size_value ) noexcept
77 {
78 if( 0 == chunk_size_value )
80
81 if( sendfile_max_chunk_size < chunk_size_value )
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
90 : m_chunk_size{ clarify_chunk_size( chunk_size_value ) }
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.
99 const file_size_t m_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.
127
128 /** @name Copy semantics.
129 * @brief Not allowed.
130 */
131 ///@{
134 ///@}
135
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 {
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 {
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 {
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
202 : m_file_total_size{ file_total_size }
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.
214 file_size_t m_file_total_size{ 0 };
215
216 //! Last modification date.
217 std::chrono::system_clock::time_point m_last_modified_at{};
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(
236 file_size_t ) noexcept;
237
239 //! File descriptor.
241 //! File meta data.
242 file_meta_t meta,
243 //! Send chunk size.
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;
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
279 : m_file_descriptor{ std::move( sf.m_file_descriptor ) }
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(
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 {
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.
455 file_offset_t m_offset;
456 //! The size of data portion in file.
457 file_size_t m_size;
458
459 //! A prefered chunk size for a single write call.
460 file_size_t m_chunk_size;
461
462 //! Timelimit for writing all the given data.
463 /*!
464 Zero value stands for default write operation timeout.
465 */
466 std::chrono::steady_clock::duration m_timelimit{ std::chrono::steady_clock::duration::zero() };
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]]
480inline 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]]
493inline 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{
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]]
508inline 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]]
519inline 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
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]]
537inline 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 */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
Wrapper class for working with native file handler.
Definition sendfile.hpp:113
file_descriptor_holder_t & operator=(const file_descriptor_holder_t &)=delete
file_descriptor_holder_t(file_descriptor_holder_t &&fdh) noexcept
Definition sendfile.hpp:136
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
file_descriptor_t m_file_descriptor
Target file descriptor.
Definition sendfile.hpp:178
bool is_valid() const noexcept
Check if file descriptor is valid.
Definition sendfile.hpp:158
file_descriptor_holder_t(const file_descriptor_holder_t &)=delete
file_descriptor_t fd() const noexcept
Definition sendfile.hpp:165
file_descriptor_holder_t(file_descriptor_t fd) noexcept
Init constructor.
Definition sendfile.hpp:124
Meta data of the file.
Definition sendfile.hpp:187
std::chrono::system_clock::time_point m_last_modified_at
Last modification date.
Definition sendfile.hpp:217
file_meta_t(file_size_t file_total_size, std::chrono::system_clock::time_point last_modified_at) noexcept
Definition sendfile.hpp:199
auto last_modified_at() const noexcept
Definition sendfile.hpp:210
file_size_t file_total_size() const noexcept
Definition sendfile.hpp:207
friend void swap(file_meta_t &r, file_meta_t &l) noexcept
Definition sendfile.hpp:190
file_size_t m_file_total_size
Total file size.
Definition sendfile.hpp:214
A guard class for setting chunk size.
Definition sendfile.hpp:68
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
constexpr sendfile_chunk_size_guarded_value_t(file_size_t chunk_size_value) noexcept
Definition sendfile.hpp:89
constexpr auto value() const noexcept
Get the valid value of a chunk size.
Definition sendfile.hpp:95
const file_size_t m_chunk_size
Valid value of the chunk size.
Definition sendfile.hpp:99
Send file write operation description.
Definition sendfile.hpp:232
friend file_descriptor_holder_t takeaway_file_descriptor(sendfile_t &target)
Take away the file description form sendfile object.
Definition sendfile.hpp:432
sendfile_t && chunk_size(sendfile_chunk_size_guarded_value_t chunk) &&
Set prefered chunk size to use in write operation.
Definition sendfile.hpp:379
void check_file_is_valid() const
Check if stored file descriptor is valid, and throws if it is not.
Definition sendfile.hpp:440
file_meta_t m_meta
File meta data.
Definition sendfile.hpp:452
file_descriptor_holder_t m_file_descriptor
Native file descriptor.
Definition sendfile.hpp:449
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
sendfile_t & timelimit(std::chrono::steady_clock::duration timelimit_value) &
Definition sendfile.hpp:394
file_descriptor_t file_descriptor() const noexcept
Get the file descriptor of a given sendfile operation.
Definition sendfile.hpp:412
sendfile_t && timelimit(std::chrono::steady_clock::duration timelimit_value) &&
Definition sendfile.hpp:403
sendfile_t & chunk_size(sendfile_chunk_size_guarded_value_t chunk) &
Definition sendfile.hpp:369
file_size_t m_chunk_size
A prefered chunk size for a single write call.
Definition sendfile.hpp:460
auto size() const noexcept
Get size of data to write.
Definition sendfile.hpp:313
friend void swap(sendfile_t &left, sendfile_t &right) noexcept
Definition sendfile.hpp:255
auto chunk_size() const noexcept
Definition sendfile.hpp:361
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 timelimit() const noexcept
Definition sendfile.hpp:386
sendfile_t(file_descriptor_holder_t fdh, file_meta_t meta, sendfile_chunk_size_guarded_value_t chunk) noexcept
Definition sendfile.hpp:238
bool is_valid() const noexcept
Check if file is valid.
Definition sendfile.hpp:298
sendfile_t(const sendfile_t &)=delete
sendfile_t & operator=(const sendfile_t &)=delete
sendfile_t(sendfile_t &&sf) noexcept
Definition sendfile.hpp:278
sendfile_t & operator=(sendfile_t &&sf) noexcept
Definition sendfile.hpp:287
file_offset_t m_offset
Data offset within the file.
Definition sendfile.hpp:455
friend sendfile_t sendfile(file_descriptor_holder_t, file_meta_t, file_size_t) noexcept
Definition sendfile.hpp:481
const file_meta_t & meta() const
Get file meta data.
Definition sendfile.hpp:302
auto offset() const noexcept
Get offset of data to write.
Definition sendfile.hpp:309
std::chrono::steady_clock::duration m_timelimit
Timelimit for writing all the given data.
Definition sendfile.hpp:466
file_size_t m_size
The size of data portion in file.
Definition sendfile.hpp:457
#define RESTINIO_FMT_FORMAT_STRING(s)
constexpr file_size_t sendfile_max_chunk_size
Maximum size of a chunk.
Definition sendfile.hpp:54
sendfile_t sendfile(const std::filesystem::path &file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition sendfile.hpp:538
sendfile_t sendfile(const std::string &file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition sendfile.hpp:509
constexpr file_descriptor_t null_file_descriptor()
Get file descriptor which stands for null.
sendfile_t sendfile(const char *file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition sendfile.hpp:494
file_descriptor_t open_file(const char *file_path)
Open file.
std::FILE * file_descriptor_t
constexpr file_size_t sendfile_default_chunk_size
Default chunk size for sendfile operation.
Definition sendfile.hpp:50
sendfile_t sendfile(string_view_t file_path, file_size_t chunk_size=sendfile_default_chunk_size)
Definition sendfile.hpp:520