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 
19 #include <restinio/exception.hpp>
21 #include <restinio/buffers.hpp>
22 #include <restinio/optional.hpp>
23 
24 namespace restinio
25 {
26 
27 namespace impl
28 {
29 
30 using 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  {
56  response_parts_attr_t::not_final_parts,
57  response_connection_attr_t::connection_keepalive };
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  {
119  m_response_output_flags = flags;
120  }
121 
123  auto
125  {
127  }
128 
130  bool
131  is_complete() const noexcept
132  {
133  return m_write_groups.empty() &&
136  }
137 
138  private:
140 
143 
147  response_parts_attr_t::not_final_parts,
148  response_connection_attr_t::connection_keepalive };
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 =
218  m_contexts[
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 
300  bool closed() const noexcept { return m_connection_closed_response_occured; }
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 
425  asio_convertible_error_t::write_was_not_executed));
426 
427  for(; !m_context_table.empty();
429  {
430  const auto ec =
432  asio_convertible_error_t::write_was_not_executed );
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 */
restinio::response_parts_attr_t::not_final_parts
@ not_final_parts
Intermediate parts (more parts of response to follow).
restinio::make_asio_compaible_error
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
Definition: asio_include.hpp:210
restinio::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
restinio::impl::response_coordinator_t::append_response
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.
Definition: response_coordinator.hpp:324
buffers.hpp
restinio::impl::response_context_t::empty
bool empty() const noexcept
Is context empty.
Definition: response_coordinator.hpp:80
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:421
request_handler.hpp
restinio::impl::response_context_table_t::m_elements_exists
std::size_t m_elements_exists
Definition: response_coordinator.hpp:274
restinio::impl::response_coordinator_t::m_request_id_counter
request_id_t m_request_id_counter
Counter for asigining id to new requests.
Definition: response_coordinator.hpp:448
restinio::utils::suppress_exceptions_quietly
void suppress_exceptions_quietly(Lambda &&lambda) noexcept
Helper function for execution a block of code with suppression of any exceptions raised inside that b...
Definition: suppress_exceptions.hpp:133
restinio::impl::response_context_t::m_request_id
request_id_t m_request_id
Definition: response_coordinator.hpp:139
restinio::impl::response_context_t::response_output_flags
auto response_output_flags() const noexcept
Get flags of corrent response data flow.
Definition: response_coordinator.hpp:124
restinio::response_connection_attr_t::connection_close
@ connection_close
This response says to close connection.
restinio::impl::response_coordinator_t::empty
bool empty() const noexcept
Definition: response_coordinator.hpp:302
restinio::impl::response_coordinator_t
Coordinator for process of sending responses with respect to http pipeline technique and chunk transf...
Definition: response_coordinator.hpp:289
restinio::impl::response_context_table_t::pop_response_context_nonchecked
void pop_response_context_nonchecked() noexcept
Remove the first context from queue with the check for emptiness of the queue.
Definition: response_coordinator.hpp:252
restinio::impl::response_context_t::request_id
auto request_id() const noexcept
Get id of associated request.
Definition: response_coordinator.hpp:113
RESTINIO_STATIC_ASSERT_NOEXCEPT
#define RESTINIO_STATIC_ASSERT_NOEXCEPT(expr)
A wrapper around static_assert for checking that an expression is noexcept.
Definition: compiler_features.hpp:89
restinio::impl::response_context_t::reinit
void reinit(request_id_t request_id) noexcept
Reinitialize context.
Definition: response_coordinator.hpp:49
restinio::impl::response_context_t::utest_access
friend write_groups_container_t & utest_access(response_context_t &ctx)
Access write-groups container (used in unit tests)
Definition: response_coordinator.hpp:42
restinio::impl::response_context_table_t::is_full
bool is_full() const noexcept
If table is full.
Definition: response_coordinator.hpp:173
restinio::impl::response_context_table_t::back
response_context_t & back() noexcept
Get last context.
Definition: response_coordinator.hpp:187
restinio::impl::response_coordinator_t::register_new_request
request_id_t register_new_request()
Create a new request and reserve context for its response.
Definition: response_coordinator.hpp:315
restinio::impl::response_context_table_t::get_by_req_id
response_context_t * get_by_req_id(request_id_t req_id) noexcept
Get context of specified request.
Definition: response_coordinator.hpp:196
restinio::impl::response_context_table_t::front
response_context_t & front() noexcept
Get first context.
Definition: response_coordinator.hpp:180
restinio::impl::response_context_t::is_complete
bool is_complete() const noexcept
Is response data of a given request is complete.
Definition: response_coordinator.hpp:131
restinio::impl::response_context_table_t::empty
bool empty() const noexcept
If table is empty.
Definition: response_coordinator.hpp:166
restinio::impl::response_context_t::m_response_output_flags
response_output_flags_t m_response_output_flags
Response flags.
Definition: response_coordinator.hpp:146
restinio::impl::response_context_t::m_write_groups
write_groups_container_t m_write_groups
Unsent responses parts.
Definition: response_coordinator.hpp:142
nonstd::optional_lite::optional
class optional
Definition: optional.hpp:839
restinio::impl::response_context_table_t::push_response_context
void push_response_context(request_id_t req_id)
Insert new context into queue.
Definition: response_coordinator.hpp:210
restinio::impl::write_groups_container_t
std::vector< write_group_t > write_groups_container_t
Definition: response_coordinator.hpp:30
include_fmtlib.hpp
A special wrapper around fmtlib include files.
suppress_exceptions.hpp
Utilities for suppressing exceptions from some code block.
restinio::write_group_t::status_line_size
void status_line_size(std::size_t n)
Definition: buffers.hpp:763
restinio::impl::response_coordinator_t::m_context_table
response_context_table_t m_context_table
A storage for resp-context items.
Definition: response_coordinator.hpp:454
restinio::write_group_t
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:692
restinio
Definition: asio_include.hpp:21
restinio::impl::response_context_t::enqueue_group
void enqueue_group(write_group_t wg)
Put write group to data queue.
Definition: response_coordinator.hpp:62
restinio::response_output_flags_t
Response output flags for buffers commited to response-coordinator.
Definition: common_types.hpp:104
restinio::impl::response_context_t::response_output_flags
void response_output_flags(response_output_flags_t flags) noexcept
Get flags of corrent response data flow.
Definition: response_coordinator.hpp:117
exception.hpp
restinio::impl::response_context_table_t::get_real_index
std::size_t get_real_index(request_id_t req_id) noexcept
Definition: response_coordinator.hpp:264
restinio::impl::response_context_table_t::m_contexts
std::vector< response_context_t > m_contexts
Definition: response_coordinator.hpp:272
restinio::impl::response_coordinator_t::pop_ready_buffers
optional_t< std::pair< write_group_t, request_id_t > > pop_ready_buffers()
Extract a portion of data available for write.
Definition: response_coordinator.hpp:371
restinio::impl::response_context_table_t
Helper storage for responses' contexts.
Definition: response_coordinator.hpp:157
restinio::static_if_else
decltype(auto) static_if_else(If_Part &&if_part, Else_Part &&else_part)
An emulation of if constexpr for C++14.
Definition: compiler_features.hpp:176
restinio::impl::response_context_table_t::m_first_element_index
std::size_t m_first_element_index
Definition: response_coordinator.hpp:273
restinio::request_id_t
unsigned int request_id_t
Request id in scope of single connection.
Definition: common_types.hpp:51
restinio::impl::response_coordinator_t::response_coordinator_t
response_coordinator_t(std::size_t max_req_count)
Definition: response_coordinator.hpp:291
restinio::impl::response_coordinator_t::is_able_to_get_more_messages
bool is_able_to_get_more_messages() const noexcept
Check if it is possible to accept more requests.
Definition: response_coordinator.hpp:308
restinio::response_output_flags_t::m_response_parts
response_parts_attr_t m_response_parts
Definition: common_types.hpp:112
restinio::impl::response_coordinator_t::reset
void reset() noexcept
Remove all contexts.
Definition: response_coordinator.hpp:416
restinio::impl::response_context_t::dequeue_group
write_group_t dequeue_group() noexcept
Extract write group from data queue.
Definition: response_coordinator.hpp:84
restinio::impl::response_coordinator_t::closed
bool closed() const noexcept
Definition: response_coordinator.hpp:301
restinio::impl::response_coordinator_t::is_full
bool is_full() const noexcept
Definition: response_coordinator.hpp:303
restinio::impl::response_context_table_t::response_context_table_t
response_context_table_t(std::size_t max_elements_count)
Definition: response_coordinator.hpp:159
restinio::impl::response_context_t
A context for a single response.
Definition: response_coordinator.hpp:38
restinio::impl::response_coordinator_t::m_connection_closed_response_occured
bool m_connection_closed_response_occured
Indicate whether a response with connection close flag was emitted.
Definition: response_coordinator.hpp:451
restinio::impl::response_context_table_t::pop_response_context
void pop_response_context()
Remove the first context from queue.
Definition: response_coordinator.hpp:231
const
#define const
Definition: zconf.h:230
optional.hpp