RESTinio
Loading...
Searching...
No Matches
write_group_output_ctx.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
5/*!
6 Helper output context for writing buffers to output stream (socket).
7*/
8
9#pragma once
10
11#include <restinio/asio_include.hpp>
12
13#include <restinio/buffers.hpp>
14#include <restinio/impl/sendfile_operation.hpp>
15
16#include <restinio/compiler_features.hpp>
17
18#include <optional>
19#include <variant>
20#include <vector>
21
22namespace restinio
23{
24
25namespace impl
26{
27
29
30//
31// write_group_output_ctx_t
32//
33
34//! Helper class for writting response data.
35/*!
36 The usage scenario is some kind of the following:
37 \code{.cpp}
38 write_group_output_ctx_t output_ctx;
39 wtite_group_t wg = ...
40
41 // Feed write group to context:
42 output_ctx.start_next_write_group( std::move( wg ) );
43
44 try
45 {
46 // start handling loop:
47 for(
48 // Extract next solid output piece.
49 auto wo = output_ctx.extract_next_write_operation();
50 // Are we done with consuming a given write_group_t instance?
51 !std::holds_alternative< none_write_operation_t >( wo );
52 // Get next output piece.
53 wo = output_ctx.extract_next_write_operation() )
54 {
55 if( std::holds_alternative< trivial_write_operation_t >( wo ) )
56 {
57 handle_trivial_bufs( std::get< trivial_write_operation_t >( wo ) );
58 }
59 else
60 {
61 handle_sendfile( std::get< file_write_operation_t >( wo ) );
62 }
63 }
64
65 // Finalize.
66 output_ctx.finish_write_group();
67 }
68 catch( ec ) // asio error code
69 {
70 // Loop failed, so we finish write group abnormally.
71 output_ctx.fail_write_group( ec )
72 }
73 \endcode
74
75 Of course, the real usage is complicated by spreading in time and
76 running plenty of other logic cooperatively.
77
78*/
80{
81 //! Get the maximum number of buffers that can be written with
82 //! gather write operation.
83 static constexpr auto
84 max_iov_len() noexcept
85 {
86 using len_t = decltype( asio_ns::detail::max_iov_len );
87 return static_cast< asio_bufs_container_t::size_type >(
88 std::min< len_t >( asio_ns::detail::max_iov_len, 64 ) );
89 }
90
91 public:
92 //! Contruct an object.
93 /*
94 Space for m_asio_bufs is reserved to be ready to store max_iov_len() asio bufs.
95 */
97 {
98 m_asio_bufs.reserve( max_iov_len() );
99 }
100
101 //! Trivial write operaton.
102 /*!
103 Presented with a vector of ordinary buffers (data-size objects).
104 */
106 {
108
110 //! Container of asio buf objects.
111 const asio_bufs_container_t & asio_bufs,
112 //! Total size of data represented by buffers.
113 std::size_t total_size ) noexcept
116 {}
117
118 public:
121
124
125 //! Get buffer "iovec" for performing gather write.
126 const std::vector< asio_ns::const_buffer > &
127 get_trivial_bufs() const noexcept
128 {
129 return *m_asio_bufs;
130 }
131
132 //! The size of data within this operation.
133 auto size() const noexcept { return m_total_size; }
134
135 private:
138 };
139
140 //! Write operaton using sendfile.
142 {
144
146 sendfile_t & sendfile,
147 sendfile_operation_shared_ptr_t & sendfile_operation ) noexcept
148 : m_sendfile{ &sendfile }
149 , m_sendfile_operation{ &sendfile_operation }
150 {}
151
152 public:
155
158
159 //! Start a sendfile operation.
160 /*!
161 @note
162 Since v.0.4.9 it is non-const method. This is necessary
163 to get a non-const reference to sendfile operation.
164 */
165 template< typename Socket, typename After_Write_CB >
166 void
168 default_asio_executor executor,
169 Socket & socket,
170 After_Write_CB after_sendfile_cb )
171 {
172 assert( m_sendfile->is_valid() );
173
175 {
176 // This must never happen.
177 throw exception_t{ "invalid file descriptor in sendfile operation." };
178 }
179
180 auto sendfile_operation =
181 std::make_shared< sendfile_operation_runner_t< Socket > >(
182 *m_sendfile,
183 std::move( executor ),
184 socket,
185 std::move( after_sendfile_cb ) );
186
187 *m_sendfile_operation = std::move( sendfile_operation );
188 (*m_sendfile_operation)->start();
189 }
190
191 //! Get the timelimit on this sendfile operation.
192 auto
193 timelimit() const noexcept
194 {
195 assert( m_sendfile->is_valid() );
196
198 }
199
200 //! Reset write operation context.
201 /*!
202 * @note
203 * Since v.0.6.0 this method is noexcept.
204 */
205 void
206 reset() noexcept
207 {
209 }
210
211 //! Get the size of sendfile operation.
212 auto size() const noexcept { return m_sendfile->size(); }
213
214 private:
215 //! A pointer to sendfile.
216 sendfile_t * m_sendfile; // Pointer is used to be able to copy/assign.
217
218 //! A curernt sendfile operation.
219 /*!
220 This context must be external to
221 file_write_operation_t instance (in order to avoid circle links).
222 */
224 };
225
226 //! None write operation.
227 /*!
228 When extract_next_write_operation() returns a variant with
229 none_write_operation_t instance it means that current write group
230 was handled to the end of its buffer sequence.
231 */
233
234 //! Check if data is trunsmitting now
235 bool transmitting() const noexcept { return static_cast< bool >( m_current_wg ); }
236
237 //! Start handlong next write group.
238 void
239 start_next_write_group( std::optional< write_group_t > next_wg ) noexcept
240 {
241 m_current_wg = std::move( next_wg );
242 }
243
244 //! An alias for variant holding write operation specifics.
246 std::variant<
250
251 //! et an object with next write operation to perform.
254 {
255 assert( m_current_wg );
256
257 solid_write_operation_variant_t result{ none_write_operation_t{} };
258
259 if( m_next_writable_item_index < m_current_wg->items_count() )
260 {
261 // Has writable items.
262 const auto next_wi_type =
263 m_current_wg->items()[ m_next_writable_item_index ].write_type();
264
266 {
267 // Trivial buffers.
269 }
270 else
271 {
272 // Sendfile.
273 assert( writable_item_type_t::file_write_operation == next_wi_type );
274 result = prepare_sendfile_wo();
275 }
276 }
277
278 return result;
279 }
280
281 //! Handle current group write process failed.
282 void
283 fail_write_group( const asio_ns::error_code & ec )
284 {
285 assert( m_current_wg );
286
288 m_current_wg.reset();
289 m_sendfile_operation.reset();
290 }
291
292 //! Finish writing group normally.
293 void
295 {
296 assert( m_current_wg );
297
300 }
301
302 private:
303 //! Reset the write group and associated context.
304 void
306 {
307 m_current_wg.reset();
309 }
310
311 //! Execute notification callback if necessary.
312 void
313 invoke_after_write_notificator_if_necessary( const asio_ns::error_code & ec )
314 {
315 try
316 {
317 m_current_wg->invoke_after_write_notificator_if_exists( ec );
318 }
319 catch( const std::exception & ex )
320 {
321 // Actualy no need to reset m_current_wg as a thrown exception
322 // will break working circle of connection.
323 // But as it is used as flag for transmitting()
324 // we reset the object.
326
327 throw exception_t{
328 fmt::format(
329 RESTINIO_FMT_FORMAT_STRING( "after write callback failed: {}" ),
330 ex.what() ) };
331 }
332 }
333
334 //! Prepare write operation for trivial buffers.
337 {
338 m_asio_bufs.clear();
339
340 const auto & items = m_current_wg->items();
341 std::size_t total_size{ 0 };
342
343 for( ;m_next_writable_item_index < items.size() &&
344 writable_item_type_t::trivial_write_operation ==
345 items[ m_next_writable_item_index ].write_type() &&
346 max_iov_len() > m_asio_bufs.size();
348 {
349 const auto & item = items[ m_next_writable_item_index ];
350 m_asio_bufs.emplace_back( item.buf() );
351 total_size += item.size();
352 }
353
354 assert( !m_asio_bufs.empty() );
355 return trivial_write_operation_t{ m_asio_bufs, total_size };
356 }
357
358 //! Prepare write operation for sendfile.
361 {
362 auto & sf =
363 m_current_wg->items()[ m_next_writable_item_index++ ].sendfile_operation();
364
365 return file_write_operation_t{ sf, m_sendfile_operation };
366 }
367
368 //! Real buffers with data.
369 std::optional< write_group_t > m_current_wg;
370
371 //! Keeps track of the next writable item stored in m_current_wg.
372 /*!
373 When emitting next solid write operation
374 we need to know where the next starting item is.
375 */
377
378 //! Asio buffers storage.
380
381 //! Sendfile operation storage context.
383};
384
385} /* namespace impl */
386
387} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
sendfile_operation_runner_base_t< Socket > base_type_t
sendfile_operation_shared_ptr_t * m_sendfile_operation
A curernt sendfile operation.
file_write_operation_t & operator=(const file_write_operation_t &)=default
file_write_operation_t(sendfile_t &sendfile, sendfile_operation_shared_ptr_t &sendfile_operation) noexcept
file_write_operation_t(const file_write_operation_t &)=default
auto timelimit() const noexcept
Get the timelimit on this sendfile operation.
file_write_operation_t & operator=(file_write_operation_t &&)=default
void start_sendfile_operation(default_asio_executor executor, Socket &socket, After_Write_CB after_sendfile_cb)
Start a sendfile operation.
auto size() const noexcept
Get the size of sendfile operation.
trivial_write_operation_t(trivial_write_operation_t &&)=default
trivial_write_operation_t(const trivial_write_operation_t &)=default
trivial_write_operation_t & operator=(const trivial_write_operation_t &)=default
trivial_write_operation_t(const asio_bufs_container_t &asio_bufs, std::size_t total_size) noexcept
auto size() const noexcept
The size of data within this operation.
const std::vector< asio_ns::const_buffer > & get_trivial_bufs() const noexcept
Get buffer "iovec" for performing gather write.
trivial_write_operation_t & operator=(trivial_write_operation_t &&)=default
Helper class for writting response data.
void fail_write_group(const asio_ns::error_code &ec)
Handle current group write process failed.
solid_write_operation_variant_t extract_next_write_operation()
et an object with next write operation to perform.
std::optional< write_group_t > m_current_wg
Real buffers with data.
std::variant< none_write_operation_t, trivial_write_operation_t, file_write_operation_t > solid_write_operation_variant_t
An alias for variant holding write operation specifics.
std::size_t m_next_writable_item_index
Keeps track of the next writable item stored in m_current_wg.
trivial_write_operation_t prepare_trivial_buffers_wo()
Prepare write operation for trivial buffers.
void start_next_write_group(std::optional< write_group_t > next_wg) noexcept
Start handlong next write group.
void finish_write_group()
Finish writing group normally.
sendfile_operation_shared_ptr_t m_sendfile_operation
Sendfile operation storage context.
asio_bufs_container_t m_asio_bufs
Asio buffers storage.
void invoke_after_write_notificator_if_necessary(const asio_ns::error_code &ec)
Execute notification callback if necessary.
static constexpr auto max_iov_len() noexcept
Get the maximum number of buffers that can be written with gather write operation.
void reset_write_group()
Reset the write group and associated context.
bool transmitting() const noexcept
Check if data is trunsmitting now.
file_write_operation_t prepare_sendfile_wo()
Prepare write operation for sendfile.
Send file write operation description.
Definition sendfile.hpp:232
auto size() const noexcept
Get size of data to write.
Definition sendfile.hpp:313
auto timelimit() const noexcept
Definition sendfile.hpp:386
bool is_valid() const noexcept
Check if file is valid.
Definition sendfile.hpp:298
Group of writable items transported to the context of underlying connection as one solid piece.
Definition buffers.hpp:727
#define RESTINIO_ENSURE_NOEXCEPT_CALL(expr)
A wrapper around static_assert for checking that an expression is noexcept and execution of that expr...
#define RESTINIO_FMT_FORMAT_STRING(s)
std::shared_ptr< sendfile_operation_base_t > sendfile_operation_shared_ptr_t
writable_item_type_t
Buffers write operation type.
Definition buffers.hpp:443
@ trivial_write_operation
Item is a buffer and must be written trivially.
Definition buffers.hpp:445
@ file_write_operation
Item is a sendfile operation and implicates file write operation.
Definition buffers.hpp:448