RESTinio
Loading...
Searching...
No Matches
message_builders.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
5/*!
6 Builders for messages.
7*/
8
9#pragma once
10
11#include <ctime>
12#include <chrono>
13
14#include <restinio/impl/include_fmtlib.hpp>
15
16#include <restinio/common_types.hpp>
17#include <restinio/http_headers.hpp>
18#include <restinio/os.hpp>
19#include <restinio/sendfile.hpp>
20#include <restinio/impl/connection_base.hpp>
21
22#include <restinio/impl/header_helpers.hpp>
23
24namespace restinio
25{
26
27//
28// make_date_field_value()
29//
30
31//! Format a timepoint to a string of a propper format.
32inline std::string
33make_date_field_value( std::time_t t )
34{
35 constexpr const char * week_days[] = {
36 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
37 };
38 constexpr const char * months[] = {
39 "Jan", "Feb", "Mar",
40 "Apr", "May", "Jun",
41 "Jul", "Aug", "Sep",
42 "Oct", "Nov", "Dec"
43 };
44 const auto tpoint = make_gmtime( t );
45
46 std::array< char, 64 > buf;
47 const auto format_result = fmt::format_to_n(
48 buf.data(), buf.size(),
50 "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT" ),
51 week_days[ tpoint.tm_wday ],
52 tpoint.tm_mday,
53 months[ tpoint.tm_mon ],
54 (1900 + tpoint.tm_year),
55 tpoint.tm_hour,
56 tpoint.tm_min,
57 tpoint.tm_sec);
58
59 return std::string{ buf.data(), format_result.size };
60}
61
62inline std::string
63make_date_field_value( std::chrono::system_clock::time_point tp )
64{
65 return make_date_field_value( std::chrono::system_clock::to_time_t( tp ) );
66}
67
68//
69// base_response_builder_t
70//
71
72template < typename Response_Builder >
74{
75 public:
78
81
82 virtual ~base_response_builder_t() = default;
83
85 http_status_line_t status_line,
86 impl::connection_handle_t connection,
87 request_id_t request_id,
88 bool should_keep_alive )
91 , m_request_id{ request_id }
92 {
93 m_header.should_keep_alive( should_keep_alive );
94 }
95
96 //! Accessors for header.
97 //! \{
98 http_response_header_t &
99 header() noexcept
100 {
101 return m_header;
102 }
103
104 const http_response_header_t &
105 header() const noexcept
106 {
107 return m_header;
108 }
109 //! \}
110
111 //! Add header field.
112 Response_Builder &
114 std::string field_name,
115 std::string field_value ) &
116 {
117 m_header.add_field(
118 std::move( field_name ),
119 std::move( field_value ) );
120 return upcast_reference();
121 }
122
123 //! Add header field.
124 Response_Builder &&
126 std::string field_name,
127 std::string field_value ) &&
128 {
129 return std::move( this->append_header(
130 std::move( field_name ),
131 std::move( field_value ) ) );
132 }
133
134 //! Add header field.
135 Response_Builder &
136 append_header( http_header_field_t http_header_field ) &
137 {
138 m_header.add_field( std::move( http_header_field ) );
139 return upcast_reference();
140 }
141
142 //! Add header field.
143 Response_Builder &&
144 append_header( http_header_field_t http_header_field ) &&
145 {
146 return std::move( this->append_header(
147 std::move( http_header_field ) ) );
148 }
149
150 //! Add header field.
151 Response_Builder &
153 http_field_t field_id,
154 std::string field_value ) &
155 {
156 m_header.add_field(
157 field_id,
158 std::move( field_value ) );
159 return upcast_reference();
160 }
161
162 //! Add header field.
163 Response_Builder &&
165 http_field_t field_id,
166 std::string field_value ) &&
167 {
168 return std::move( this->append_header(
169 field_id,
170 std::move( field_value ) ) );
171 }
172
173
174 //! Add header `Date` field.
175 Response_Builder &
177 std::chrono::system_clock::time_point tp =
178 std::chrono::system_clock::now() ) &
179 {
180 m_header.set_field( http_field_t::date, make_date_field_value( tp ) );
181 return upcast_reference();
182 }
183
184 //! Add header `Date` field.
185 Response_Builder &&
187 std::chrono::system_clock::time_point tp =
188 std::chrono::system_clock::now() ) &&
189 {
190 return std::move( this->append_header_date_field( tp ) );
191 }
192
193 //! Set connection close.
194 Response_Builder &
195 connection_close() & noexcept
196 {
197 m_header.should_keep_alive( false );
198 return upcast_reference();
199 }
200
201 //! Set connection close.
202 Response_Builder &&
203 connection_close() && noexcept
204 {
205 return std::move( this->connection_close() );
206 }
207
208
209 //! Set connection keep-alive.
210 Response_Builder &
212 {
213 m_header.should_keep_alive();
214 return upcast_reference();
215 }
216
217 Response_Builder &&
219 {
220 return std::move( this->connection_keep_alive() );
221 }
222
223 protected:
224 std::size_t
226 {
227 // "HTTP/1.1 *** <reason-phrase>"
228 return 8 + 1 + 3 + 1 + m_header.status_line().reason_phrase().size();
229 }
230
231 http_response_header_t m_header;
232
235
236 void
238 {
239 throw exception_t{ "done() cannot be called twice" };
240 }
241
242 private:
243 Response_Builder &
245 {
246 return static_cast< Response_Builder & >( *this );
247 }
248};
249
250//
251// response_builder_t
252//
253
254//! Forbid arbitrary response_builder_t instantiations.
255template < typename Response_Output_Strategy >
257{
259};
260
261//! Tag type for RESTinio controlled output response builder.
263
264//! Simple standard response builder.
265/*!
266 Requires user to set header and body.
267 Content length is automatically calculated.
268 Once the data is ready, the user calls done() method
269 and the resulting response is scheduled for sending.
270*/
271template <>
272class response_builder_t< restinio_controlled_output_t > final
273 : public base_response_builder_t< response_builder_t< restinio_controlled_output_t > >
274{
275 public:
280
282
283 // Reuse construstors from base.
284 using base_type_t::base_type_t;
285
286 //! Set body.
289 {
290 auto size = body.size();
291 return set_body_impl( body, size );
292 }
293
294 //! Set body.
295 self_type_t &&
297 {
298 return std::move( this->set_body( std::move( body ) ) );
299 }
300
301 //! Append body.
304 {
305 auto size = body_part.size();
306 return append_body_impl( body_part, size );
307 }
308
309 //! Append body.
310 self_type_t &&
312 {
313 return std::move( this->append_body( std::move( body_part ) ) );
314 }
315
316 //! Complete response.
318 done( write_status_cb_t wscb = write_status_cb_t{} )
319 {
320 if( m_connection )
321 {
323 response_output_flags{
324 response_parts_attr_t::final_parts,
325 response_connection_attr( m_header.should_keep_alive() ) };
326
327 m_header.content_length( m_body_size );
328
330
331 m_response_parts[ 0 ] =
332 writable_item_t{ impl::create_header_string( m_header ) };
333
334 write_group_t wg{ std::move( m_response_parts ) };
335 wg.status_line_size( calculate_status_line_size() );
336
337 if( wscb )
338 {
339 wg.after_write_notificator( std::move( wscb ) );
340 }
341
342 auto conn = std::move( m_connection );
343
344 conn->write_response_parts(
345 m_request_id,
346 response_output_flags,
347 std::move( wg ) );
348 }
349 else
350 {
351 throw_done_must_be_called_once();
352 }
353
355 }
356
357 private:
359 set_body_impl( writable_item_t & body, std::size_t body_size )
360 {
362
363 // Leave only buf that is reserved for header,
364 // so forget all the previous data.
365 m_response_parts.resize( 1 );
366
367 if( 0 < body_size )
368 {
369 m_response_parts.emplace_back( std::move( body ) );
370 }
371
372 m_body_size = body_size;
373
374 return *this;
375 }
376
378 append_body_impl( writable_item_t & body_part, std::size_t append_size )
379 {
381
382 if( 0 < append_size )
383 {
384 m_response_parts.emplace_back( std::move( body_part ) );
385 m_body_size += append_size;
386 }
387
388 return *this;
389 }
390
391 void
393 {
394 if( m_response_parts.empty() )
395 {
396 m_response_parts.reserve( 2 );
397 m_response_parts.emplace_back();
398 }
399 }
400
401 std::size_t m_body_size{ 0 };
403};
404
405//! Tag type for user controlled output response builder.
407
408//! User controlled response output builder.
409/*!
410 This type of output allows user
411 to send body divided into parts.
412 But it is up to user to set the correct
413 Content-Length field.
414*/
415template <>
416class response_builder_t< user_controlled_output_t > final
417 : public base_response_builder_t< response_builder_t< user_controlled_output_t > >
418{
419 public:
424
426
427 // Reuse construstors from base.
428 using base_type_t::base_type_t;
429
430 //! Manualy set content length.
432 set_content_length( std::size_t content_length ) &
433 {
434 m_header.content_length( content_length );
435 return *this;
436 }
437
438 //! Manualy set content length.
439 self_type_t &&
440 set_content_length( std::size_t content_length ) &&
441 {
442 return std::move( this->set_content_length( content_length ) );
443 }
444
445 //! Set body (part).
448 {
449 auto size = body.size();
450 return set_body_impl( body, size );
451 }
452
453 //! Set body (part).
454 self_type_t &&
456 {
457 return std::move( this->set_body( std::move( body ) ) );
458 }
459
460 //! Append body.
463 {
464 auto size = body_part.size();
465
466 if( 0 == size )
467 return *this;
468
469 return append_body_impl( body_part );
470 }
471
472 //! Append body.
473 self_type_t &&
475 {
476 return std::move( this->append_body( std::move( body_part ) ) );
477 }
478
479 //! Flush ready outgoing data.
480 /*!
481 Schedules for sending currently ready data.
482 */
484 flush( write_status_cb_t wscb = write_status_cb_t{} ) &
485 {
486 if( m_connection )
487 {
488 send_ready_data(
489 m_connection,
490 response_parts_attr_t::not_final_parts,
491 std::move( wscb ) );
492 }
493
494 return *this;
495 }
496
497 //! Flush ready outgoing data.
498 self_type_t &&
499 flush( write_status_cb_t wscb = write_status_cb_t{} ) &&
500 {
501 return std::move( this->flush( std::move( wscb ) ) );
502 }
503
504 //! Complete response.
506 done( write_status_cb_t wscb = write_status_cb_t{} )
507 {
508 if( m_connection )
509 {
510 // Note: m_connection should become empty after return
511 // from that method.
512 impl::connection_handle_t old_conn_handle{
513 std::move(m_connection) };
515 old_conn_handle,
517 std::move( wscb ) );
518 }
519 else
520 {
521 throw_done_must_be_called_once();
522 }
523
525 }
526
527 private:
528 void
530 const impl::connection_handle_t & conn,
531 response_parts_attr_t response_parts_attr,
532 write_status_cb_t wscb )
533 {
534 std::size_t status_line_size{ 0 };
535
536 if( !m_header_was_sent )
537 {
538 m_should_keep_alive_when_header_was_sent =
539 m_header.should_keep_alive();
540
542
543 m_response_parts[ 0 ] =
544 writable_item_t{ impl::create_header_string( m_header ) };
545
546 m_header_was_sent = true;
547 status_line_size = calculate_status_line_size();
548 }
549
550 if( !m_response_parts.empty() ||
551 wscb ||
552 response_parts_attr_t::final_parts == response_parts_attr )
553 {
555 response_output_flags{
556 response_parts_attr,
558
559 write_group_t wg{ std::move( m_response_parts ) };
560 wg.status_line_size( status_line_size );
561
562 if( wscb )
563 {
564 wg.after_write_notificator( std::move( wscb ) );
565 }
566
567 conn->write_response_parts(
568 m_request_id,
569 response_output_flags,
570 std::move( wg ) );
571 }
572 }
573
575 set_body_impl( writable_item_t & body, std::size_t body_size )
576 {
578
579 // Leave only buf that is reserved for header,
580 // so forget all the previous data.
581 if( !m_header_was_sent )
582 m_response_parts.resize( 1 );
583 else
584 m_response_parts.resize( 0 );
585
586 if( 0 < body_size )
587 {
588 // if body is not empty:
589 m_response_parts.emplace_back( std::move( body ) );
590 }
591
592 return *this;
593 }
594
597 {
599
600 m_response_parts.emplace_back( std::move( body_part ) );
601 return *this;
602 }
603
604 void
606 {
607 if( !m_header_was_sent && m_response_parts.empty() )
608 {
609 m_response_parts.reserve( 2 );
610 m_response_parts.emplace_back();
611 }
612 }
613
614
615 //! Flag used by flush() function.
616 bool m_header_was_sent{ false };
617
618 //! Saved keep_alive attr actual at the point
619 //! a header data was sent.
620 /*!
621 It is neccessary to guarantee that all parts of response
622 will have the same response-connection-attr
623 (keep-alive or close);
624 */
626
627 //! Body accumulator.
628 /*!
629 For this type of output it contains a part of a body.
630 On each flush it is cleared.
631 */
633};
634
635//! Tag type for chunked output response builder.
637
638//! Chunked transfer encoding output builder.
639/*!
640 This type of output sets transfer-encoding to chunked
641 and expects user to set body using chunks of data.
642*/
643template <>
644class response_builder_t< chunked_output_t > final
645 : public base_response_builder_t< response_builder_t< chunked_output_t > >
646{
647 public:
652
654 http_status_line_t status_line,
655 impl::connection_handle_t connection,
656 request_id_t request_id,
657 bool should_keep_alive )
658 : base_type_t{
659 std::move( status_line ),
660 std::move( connection ),
663 {
664 m_chunks.reserve( 4 );
665 }
666
668
669 //! Append current chunk.
672 {
673 auto size = chunk.size();
674
675 if( 0 != size )
676 m_chunks.emplace_back( std::move( chunk ) );
677
678 return *this;
679 }
680
681 //! Append current chunk.
682 self_type_t &&
684 {
685 return std::move( this->append_chunk( std::move( chunk ) ) );
686 }
687
688 //! Flush ready outgoing data.
689 /*!
690 Schedules for sending currently ready data.
691 */
693 flush( write_status_cb_t wscb = write_status_cb_t{} ) &
694 {
695 if( m_connection )
696 {
697 send_ready_data(
698 m_connection,
699 response_parts_attr_t::not_final_parts,
700 std::move( wscb ) );
701 }
702
703 return *this;
704 }
705
706 //! Flush ready outgoing data.
707 self_type_t &&
708 flush( write_status_cb_t wscb = write_status_cb_t{} ) &&
709 {
710 return std::move( this->flush( std::move( wscb ) ) );
711 }
712
713 //! Complete response.
715 done( write_status_cb_t wscb = write_status_cb_t{} )
716 {
717 if( m_connection )
718 {
719 // Note: m_connection should become empty after return
720 // from that method.
721 impl::connection_handle_t old_conn_handle{
722 std::move(m_connection) };
724 old_conn_handle,
726 std::move( wscb ) );
727 }
728 else
729 {
730 throw_done_must_be_called_once();
731 }
732
734 }
735
736 private:
737 void
739 const impl::connection_handle_t & conn,
740 response_parts_attr_t response_parts_attr,
741 write_status_cb_t wscb )
742 {
743 std::size_t status_line_size{ 0 };
744 if( !m_header_was_sent )
745 {
746 status_line_size = calculate_status_line_size();
748 }
749
750 auto bufs = create_bufs( response_parts_attr_t::final_parts == response_parts_attr );
751 m_header_was_sent = true;
752
754 response_output_flags{
755 response_parts_attr,
757
758 // We have buffers or at least we have after-write notificator.
759 if( !bufs.empty() || wscb )
760 {
761 write_group_t wg{ std::move( bufs ) };
762 wg.status_line_size( status_line_size );
763
764 if( wscb )
765 {
766 wg.after_write_notificator( std::move( wscb ) );
767 }
768
769 conn->write_response_parts(
770 m_request_id,
771 response_output_flags,
772 std::move( wg ) );
773 }
774 }
775
776 void
778 {
779 m_should_keep_alive_when_header_was_sent =
780 m_header.should_keep_alive();
781
782 constexpr const char value[] = "chunked";
783 if( !m_header.has_field( restinio::http_field::transfer_encoding ) )
784 {
785 m_header.add_field(
786 restinio::http_field::transfer_encoding,
787 std::string{ value, impl::ct_string_len( value ) } );
788 }
789 else
790 {
791 auto & current_value =
792 m_header.get_field( restinio::http_field::transfer_encoding );
793 if( std::string::npos == current_value.find( value ) )
794 {
795 constexpr const char comma_value[] = ",chunked";
796 m_header.append_field(
797 restinio::http_field::transfer_encoding,
798 std::string{
799 comma_value,
800 impl::ct_string_len( comma_value ) } );
801 }
802 }
803 }
804
806 create_bufs( bool add_zero_chunk )
807 {
808 writable_items_container_t bufs;
809
810 std::size_t reserve_size = 2 * m_chunks.size() + 1;
811
812 if( !m_header_was_sent )
813 {
814 ++reserve_size;
815 }
816 if( add_zero_chunk )
817 {
818 ++reserve_size;
819 }
820
821 bufs.reserve( reserve_size );
822
823 if( !m_header_was_sent )
824 {
825 bufs.emplace_back(
826 impl::create_header_string(
827 m_header,
828 impl::content_length_field_presence_t::skip_content_length ) );
829 }
830
831 // Since fmtlib-8.0.0 compile-time checks of format strings
832 // is enabled by default. If non-constexpr string has to be
833 // passed as format_string then fmt::runtime() function should
834 // be used. But there are some drawbacks:
835 // - fmt::runtime is added only in fmtlib-8.0.0, there is no
836 // such a function in previous versions of fmtlib;
837 // - using fmt::runtime we lack a possibility to check
838 // format_string in compile-time and can have some performance
839 // penalty in run-time.
840 //
841 // Because of that, the following code was rewritten to have different
842 // code fragments for the first item in m_chunks and all successive
843 // items.
844 auto chunk_it = m_chunks.begin();
845 const auto chunk_end = m_chunks.end();
846 if( chunk_it != chunk_end )
847 {
848 bufs.emplace_back(
849 fmt::format(
850 RESTINIO_FMT_FORMAT_STRING( "{:X}\r\n" ),
851 asio_ns::buffer_size( chunk_it->buf() ) ) );
852 bufs.emplace_back( std::move( *chunk_it ) );
853
854 for( ++chunk_it; chunk_it != chunk_end; ++chunk_it )
855 {
856 bufs.emplace_back(
857 fmt::format(
858 RESTINIO_FMT_FORMAT_STRING( "\r\n{:X}\r\n" ),
859 asio_ns::buffer_size( chunk_it->buf() ) ) );
860 bufs.emplace_back( std::move( *chunk_it ) );
861 }
862 }
863
864 const char * const ending_representation = "\r\n" "0\r\n\r\n";
865 const char * appendix_begin = ending_representation + 2;
866 const char * appendix_end = appendix_begin;
867
868 if( !m_chunks.empty() )
869 {
870 // Add "\r\n"part to appendix.
871 appendix_begin -= 2;
872 // bufs.emplace_back( const_buffer( rn_ending, 2 ) );
873 }
874
875 if( add_zero_chunk )
876 {
877 // Add "0\r\n\r\n"part to appendix.
878 appendix_end += 5;
879 }
880
881 if( appendix_begin != appendix_end )
882 {
883 bufs.emplace_back( const_buffer(
884 appendix_begin,
885 static_cast<std::size_t>(appendix_end - appendix_begin)
886 ) );
887 }
888
889 m_chunks.clear();
890
891 return bufs;
892 }
893
894 //! Flag used by flush() function.
895 bool m_header_was_sent{ false };
896
897 //! Saved keep_alive attr actual at the point
898 //! a header data was sent.
899 /*!
900 It is neccessary to guarantee that all parts of response
901 will have the same response-connection-attr
902 (keep-alive or close);
903 */
905
906 //! Chunks accumulator.
908};
909
910} /* namespace restinio */
base_response_builder_t & operator=(base_response_builder_t &&) noexcept=default
Response_Builder & upcast_reference() noexcept
Response_Builder && append_header(http_field_t field_id, std::string field_value) &&
Add header field.
Response_Builder && append_header(http_header_field_t http_header_field) &&
Add header field.
impl::connection_handle_t m_connection
Response_Builder && append_header(std::string field_name, std::string field_value) &&
Add header field.
Response_Builder & append_header(std::string field_name, std::string field_value) &
Add header field.
Response_Builder & connection_close() &noexcept
Set connection close.
std::size_t calculate_status_line_size() const noexcept
base_response_builder_t(base_response_builder_t &&) noexcept=default
Response_Builder & append_header(http_header_field_t http_header_field) &
Add header field.
Response_Builder & append_header(http_field_t field_id, std::string field_value) &
Add header field.
Response_Builder & connection_keep_alive() &noexcept
Set connection keep-alive.
const http_response_header_t & header() const noexcept
Response_Builder & append_header_date_field(std::chrono::system_clock::time_point tp=std::chrono::system_clock::now()) &
Add header Date field.
Response_Builder && append_header_date_field(std::chrono::system_clock::time_point tp=std::chrono::system_clock::now()) &&
Add header Date field.
base_response_builder_t(http_status_line_t status_line, impl::connection_handle_t connection, request_id_t request_id, bool should_keep_alive)
base_response_builder_t & operator=(const base_response_builder_t &)=delete
base_response_builder_t(const base_response_builder_t &)=delete
http_response_header_t & header() noexcept
Accessors for header.
Response_Builder && connection_close() &&noexcept
Set connection close.
Response_Builder && connection_keep_alive() &&noexcept
virtual ~base_response_builder_t()=default
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
A single header field.
HTTP response header status line.
self_type_t & flush(write_status_cb_t wscb=write_status_cb_t{}) &
Flush ready outgoing data.
self_type_t && flush(write_status_cb_t wscb=write_status_cb_t{}) &&
Flush ready outgoing data.
response_builder_t< chunked_output_t > self_type_t
writable_items_container_t m_chunks
Chunks accumulator.
bool m_should_keep_alive_when_header_was_sent
Saved keep_alive attr actual at the point a header data was sent.
writable_items_container_t create_bufs(bool add_zero_chunk)
void send_ready_data(const impl::connection_handle_t &conn, response_parts_attr_t response_parts_attr, write_status_cb_t wscb)
response_builder_t(response_builder_t &&)=default
base_response_builder_t< response_builder_t< chunked_output_t > > base_type_t
self_type_t && append_chunk(writable_item_t chunk) &&
Append current chunk.
self_type_t & append_chunk(writable_item_t chunk) &
Append current chunk.
request_handling_status_t done(write_status_cb_t wscb=write_status_cb_t{})
Complete response.
bool m_header_was_sent
Flag used by flush() function.
response_builder_t(http_status_line_t status_line, impl::connection_handle_t connection, request_id_t request_id, bool should_keep_alive)
self_type_t & append_body_impl(writable_item_t &body_part, std::size_t append_size)
self_type_t && append_body(writable_item_t body_part) &&
Append body.
response_builder_t< restinio_controlled_output_t > self_type_t
self_type_t & append_body(writable_item_t body_part) &
Append body.
base_response_builder_t< response_builder_t< restinio_controlled_output_t > > base_type_t
request_handling_status_t done(write_status_cb_t wscb=write_status_cb_t{})
Complete response.
self_type_t && set_body(writable_item_t body) &&
Set body.
self_type_t & set_body_impl(writable_item_t &body, std::size_t body_size)
self_type_t & set_body(writable_item_t body) &
Set body.
self_type_t & flush(write_status_cb_t wscb=write_status_cb_t{}) &
Flush ready outgoing data.
response_builder_t< user_controlled_output_t > self_type_t
writable_items_container_t m_response_parts
Body accumulator.
self_type_t & append_body_impl(writable_item_t &body_part)
self_type_t & set_body(writable_item_t body) &
Set body (part).
base_response_builder_t< response_builder_t< user_controlled_output_t > > base_type_t
void send_ready_data(const impl::connection_handle_t &conn, response_parts_attr_t response_parts_attr, write_status_cb_t wscb)
bool m_should_keep_alive_when_header_was_sent
Saved keep_alive attr actual at the point a header data was sent.
self_type_t & set_content_length(std::size_t content_length) &
Manualy set content length.
self_type_t && set_content_length(std::size_t content_length) &&
Manualy set content length.
self_type_t && set_body(writable_item_t body) &&
Set body (part).
self_type_t && flush(write_status_cb_t wscb=write_status_cb_t{}) &&
Flush ready outgoing data.
request_handling_status_t done(write_status_cb_t wscb=write_status_cb_t{})
Complete response.
self_type_t & set_body_impl(writable_item_t &body, std::size_t body_size)
self_type_t && append_body(writable_item_t body_part) &&
Append body.
self_type_t & append_body(writable_item_t body_part) &
Append body.
Forbid arbitrary response_builder_t instantiations.
Class for storing the buffers used for streaming body (request/response).
Definition buffers.hpp:524
std::size_t size() const
Get the size of the underlying buffer object.
Definition buffers.hpp:616
Group of writable items transported to the context of underlying connection as one solid piece.
Definition buffers.hpp:727
void status_line_size(std::size_t n)
Definition buffers.hpp:798
void after_write_notificator(write_status_cb_t notificator) noexcept
Set after write notificator.
Definition buffers.hpp:836
#define RESTINIO_FMT_FORMAT_STRING(s)
std::shared_ptr< connection_base_t > connection_handle_t
Alias for http connection handle.
unsigned int request_id_t
Request id in scope of single connection.
response_connection_attr_t response_connection_attr(bool should_keep_alive)
constexpr request_handling_status_t request_accepted() noexcept
request_handling_status_t
Request handling status.
http_field_t
C++ enum that repeats nodejs c-style enum.
std::string make_date_field_value(std::time_t t)
Format a timepoint to a string of a propper format.
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition buffers.hpp:425
std::string make_date_field_value(std::chrono::system_clock::time_point tp)
std::function< void(const asio_ns::error_code &ec) > write_status_cb_t
An alias for a callback to be invoked after the write operation of a particular group of "buffers".
Definition buffers.hpp:714
response_parts_attr_t
Attribute for parts.
@ final_parts
Final parts (response ands with these parts).
Tag type for chunked output response builder.
Response output flags for buffers commited to response-coordinator.
response_output_flags_t(response_parts_attr_t response_parts, response_connection_attr_t response_connection) noexcept
Tag type for RESTinio controlled output response builder.
Tag type for user controlled output response builder.