RESTinio
response_coordinator.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
10#pragma once
11
12#include <string>
13#include <deque>
14
16
18
21#include <restinio/buffers.hpp>
22#include <restinio/optional.hpp>
23
24namespace restinio
25{
26
27namespace impl
28{
29
30using write_groups_container_t = std::vector< write_group_t >;
31
32//
33// response_context_t
34//
35
38{
39 public:
43 {
44 return ctx.m_write_groups;
45 }
46
48 void
51 request_id_t request_id ) noexcept
52 {
58 }
59
61 void
63 {
64 // There is at least one group.
65 // So we check if this group can be merged with existing (the last one).
66 if( !m_write_groups.empty() &&
67 !m_write_groups.back().has_after_write_notificator() &&
68 std::size_t{ 0 } == wg.status_line_size() )
69 {
70 m_write_groups.back().merge( std::move( wg ) );
71 }
72 else
73 {
74 m_write_groups.emplace_back( std::move( wg ) );
75 }
76
77 }
78
80 bool empty() const noexcept { return m_write_groups.empty(); }
81
84 dequeue_group() noexcept
85 {
86 assert( !m_write_groups.empty() );
87
88 // Move constructor for write_group_t shouldn't throw.
90 write_group_t{ std::declval<write_group_t>() } );
91
92 write_group_t result{ std::move( m_write_groups.front() ) };
93
94 // Some STL implementation can have std::vector::erase that
95 // doesn't throw. So we use a kind of static if to select
96 // an appropriate behaviour.
97 static_if_else< noexcept(m_write_groups.erase(m_write_groups.begin())) >(
98 // This is for the case when std::vector::erase doesn't throw.
99 [this]() noexcept {
100 m_write_groups.erase( m_write_groups.begin() );
101 },
102 // This is for the case when std::vector::erase does throw.
103 [this]() {
105 m_write_groups.erase( m_write_groups.begin() );
106 } );
107 } );
108
109 return result;
110 }
111
113 auto request_id() const noexcept { return m_request_id; }
114
116 void
118 {
120 }
121
123 auto
125 {
127 }
128
130 bool
132 {
133 return m_write_groups.empty() &&
136 }
137
138 private:
140
143
149};
150
151//
152// response_context_table_t
153//
154
157{
158 public:
159 response_context_table_t( std::size_t max_elements_count )
160 {
161 m_contexts.resize( max_elements_count );
162 }
163
165 bool
166 empty() const noexcept
167 {
168 return !m_elements_exists;
169 }
170
172 bool
173 is_full() const noexcept
174 {
175 return m_contexts.size() == m_elements_exists;
176 }
177
180 front() noexcept
181 {
183 }
184
187 back() noexcept
188 {
189 return m_contexts[
191 m_contexts.size() ];
192 }
193
196 get_by_req_id( request_id_t req_id ) noexcept
197 {
198 if( empty() ||
199 req_id < front().request_id() ||
200 req_id > back().request_id() )
201 {
202 return nullptr;
203 }
204
205 return &m_contexts[ get_real_index( req_id ) ];
206 }
207
209 void
211 {
212 if( is_full() )
213 throw exception_t{
214 "unable to insert context because "
215 "response_context_table is full" };
216
217 auto & ctx =
219 // Current next.
221 ];
222
223 ctx.reinit( req_id );
224
225 // 1 more element added.
227 }
228
230 void
232 {
233 if( empty() )
234 throw exception_t{
235 "unable to pop context because "
236 "response_context_table is empty" };
237
239 }
240
243
251 void
253 {
256 if( m_contexts.size() == m_first_element_index )
257 {
258 m_first_element_index = std::size_t{0};
259 }
260 }
261
262 private:
263 std::size_t
264 get_real_index( request_id_t req_id ) noexcept
265 {
266 const auto distance_from_first =
267 req_id - front().request_id();
268
269 return ( m_first_element_index + distance_from_first ) % m_contexts.size();
270 }
271
272 std::vector< response_context_t > m_contexts;
273 std::size_t m_first_element_index{0};
274 std::size_t m_elements_exists{0};
275};
276
277//
278// response_coordinator_t
279//
280
283/*
284 Keeps track of maximum N (max_req_count) pipelined requests,
285 gathers pieces (write groups) of responses and provides access to
286 ready-to-send buffers on demand.
287*/
289{
290 public:
293 std::size_t max_req_count )
294 : m_context_table{ max_req_count }
295 {}
296
302 bool empty() const noexcept { return m_context_table.empty(); }
303 bool is_full() const noexcept { return m_context_table.is_full(); }
305
307 bool
309 {
310 return !closed() && !is_full();
311 }
312
316 {
318
319 return m_request_id_counter++;
320 }
321
323 void
326 request_id_t req_id,
328 response_output_flags_t response_output_flags,
330 write_group_t wg )
331 {
332 // Nothing to do if already closed response emitted.
333 if( closed() )
334 throw exception_t{
335 "unable to append response parts, "
336 "response coordinator is closed" };
337
338 auto * ctx = m_context_table.get_by_req_id( req_id );
339
340 if( nullptr == ctx )
341 {
342 // Request is unknown...
343 throw exception_t{
344 fmt::format(
345 "no context associated with request {}",
346 req_id ) };
347 }
348
350 ctx->response_output_flags().m_response_parts )
351 {
352 // Request is already completed...
353 throw exception_t{
354 "unable to append response, "
355 "it marked as complete" };
356 }
357
358 ctx->response_output_flags( response_output_flags );
359
360 ctx->enqueue_group( std::move( wg ) );
361 }
362
364
372 {
373 if( closed() )
374 throw exception_t{
375 "unable to prepare output buffers, "
376 "response coordinator is closed" };
377
379
380 // Check for custom write operation.
381 if( !m_context_table.empty() )
382 {
383 auto & current_ctx = m_context_table.front();
384
385 if( !current_ctx.empty() )
386 {
387 result =
388 std::make_pair(
389 current_ctx.dequeue_group(),
390 current_ctx.request_id() );
391
392 if( current_ctx.is_complete() )
393 {
396 current_ctx.response_output_flags().m_response_parts )
398 current_ctx.response_output_flags().m_response_connection );
399
401 }
402 }
403 }
404
405 return result;
406 }
407
409
415 void
416 reset() noexcept
417 {
423
426
427 for(; !m_context_table.empty();
429 {
430 const auto ec =
433
434 auto & current_ctx = m_context_table.front();
435 while( !current_ctx.empty() )
436 {
437 auto wg = current_ctx.dequeue_group();
438
440 wg.invoke_after_write_notificator_if_exists( ec );
441 } );
442 }
443 }
444 }
445
446 private:
449
452
455};
456
457} /* namespace impl */
458
459} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
A context for a single response.
write_group_t dequeue_group() noexcept
Extract write group from data queue.
friend write_groups_container_t & utest_access(response_context_t &ctx)
Access write-groups container (used in unit tests)
void reinit(request_id_t request_id) noexcept
Reinitialize context.
response_output_flags_t m_response_output_flags
Response flags.
auto response_output_flags() const noexcept
Get flags of corrent response data flow.
write_groups_container_t m_write_groups
Unsent responses parts.
auto request_id() const noexcept
Get id of associated request.
bool is_complete() const noexcept
Is response data of a given request is complete.
bool empty() const noexcept
Is context empty.
void enqueue_group(write_group_t wg)
Put write group to data queue.
void response_output_flags(response_output_flags_t flags) noexcept
Get flags of corrent response data flow.
Helper storage for responses' contexts.
response_context_t & back() noexcept
Get last context.
response_context_table_t(std::size_t max_elements_count)
response_context_t & front() noexcept
Get first context.
void pop_response_context()
Remove the first context from queue.
bool empty() const noexcept
If table is empty.
void pop_response_context_nonchecked() noexcept
Remove the first context from queue with the check for emptiness of the queue.
std::size_t get_real_index(request_id_t req_id) noexcept
response_context_t * get_by_req_id(request_id_t req_id) noexcept
Get context of specified request.
std::vector< response_context_t > m_contexts
void push_response_context(request_id_t req_id)
Insert new context into queue.
bool is_full() const noexcept
If table is full.
Coordinator for process of sending responses with respect to http pipeline technique and chunk transf...
void reset() noexcept
Remove all contexts.
request_id_t register_new_request()
Create a new request and reserve context for its response.
bool is_able_to_get_more_messages() const noexcept
Check if it is possible to accept more requests.
request_id_t m_request_id_counter
Counter for asigining id to new requests.
response_context_table_t m_context_table
A storage for resp-context items.
bool m_connection_closed_response_occured
Indicate whether a response with connection close flag was emitted.
optional_t< std::pair< write_group_t, request_id_t > > pop_ready_buffers()
Extract a portion of data available for write.
response_coordinator_t(std::size_t max_req_count)
void append_response(request_id_t req_id, response_output_flags_t response_output_flags, write_group_t wg)
Add outgoing data for specified request.
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:692
void status_line_size(std::size_t n)
Definition: buffers.hpp:763
#define RESTINIO_STATIC_ASSERT_NOEXCEPT(expr)
A wrapper around static_assert for checking that an expression is noexcept.
A special wrapper around fmtlib include files.
std::vector< write_group_t > write_groups_container_t
void suppress_exceptions_quietly(Lambda &&lambda) noexcept
Helper function for execution a block of code with suppression of any exceptions raised inside that b...
unsigned int request_id_t
Request id in scope of single connection.
@ write_was_not_executed
After write notificator error: data was not sent, connection closed (or aborted) before a given piece...
decltype(auto) static_if_else(If_Part &&if_part, Else_Part &&else_part)
An emulation of if constexpr for C++14.
@ connection_close
This response says to close connection.
@ connection_keepalive
This response says to keep connection.
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
@ final_parts
Final parts (response ands with these parts).
@ not_final_parts
Intermediate parts (more parts of response to follow).
Response output flags for buffers commited to response-coordinator.
response_parts_attr_t m_response_parts
Utilities for suppressing exceptions from some code block.
#define const
Definition: zconf.h:230