2
3
6
7
14#include <restinio/impl/include_fmtlib.hpp>
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>
22#include <restinio/impl/header_helpers.hpp>
35 constexpr const char * week_days[] = {
36 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
38 constexpr const char * months[] = {
44 const auto tpoint = make_gmtime( t );
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 ],
53 months[ tpoint.tm_mon ],
54 (1900 + tpoint.tm_year),
59 return std::string{ buf.data(), format_result.size };
65 return make_date_field_value( std::chrono::system_clock::to_time_t( tp ) );
72template <
typename Response_Builder >
88 bool should_keep_alive )
93 m_header.should_keep_alive( should_keep_alive );
98 http_response_header_t &
104 const http_response_header_t &
114 std::string field_name,
115 std::string field_value ) &
118 std::move( field_name ),
119 std::move( field_value ) );
126 std::string field_name,
127 std::string field_value ) &&
129 return std::move(
this->append_header(
130 std::move( field_name ),
131 std::move( field_value ) ) );
138 m_header.add_field( std::move( http_header_field ) );
146 return std::move(
this->append_header(
147 std::move( http_header_field ) ) );
154 std::string field_value ) &
158 std::move( field_value ) );
166 std::string field_value ) &&
168 return std::move(
this->append_header(
170 std::move( field_value ) ) );
177 std::chrono::system_clock::time_point tp =
178 std::chrono::system_clock::now() ) &
180 m_header.set_field( http_field_t::date, make_date_field_value( tp ) );
187 std::chrono::system_clock::time_point tp =
188 std::chrono::system_clock::now() ) &&
190 return std::move(
this->append_header_date_field( tp ) );
197 m_header.should_keep_alive(
false );
205 return std::move(
this->connection_close() );
213 m_header.should_keep_alive();
220 return std::move(
this->connection_keep_alive() );
228 return 8 + 1 + 3 + 1 + m_header.status_line().reason_phrase().size();
246 return static_cast< Response_Builder & >( *
this );
255template <
typename Response_Output_Strategy >
266
267
268
269
270
273 :
public base_response_builder_t< response_builder_t< restinio_controlled_output_t > >
298 return std::move(
this->set_body( std::move( body ) ) );
313 return std::move(
this->append_body( std::move( body_part ) ) );
323 response_output_flags{
324 response_parts_attr_t::final_parts,
325 response_connection_attr( m_header.should_keep_alive() ) };
327 m_header.content_length( m_body_size );
331 m_response_parts[ 0 ] =
332 writable_item_t{ impl::create_header_string( m_header ) };
335 wg.status_line_size( calculate_status_line_size() );
342 auto conn = std::move( m_connection );
344 conn->write_response_parts(
346 response_output_flags,
351 throw_done_must_be_called_once();
365 m_response_parts.resize( 1 );
369 m_response_parts.emplace_back( std::move( body ) );
382 if( 0 < append_size )
384 m_response_parts.emplace_back( std::move( body_part ) );
394 if( m_response_parts.empty() )
396 m_response_parts.reserve( 2 );
397 m_response_parts.emplace_back();
410
411
412
413
414
417 :
public base_response_builder_t< response_builder_t< user_controlled_output_t > >
434 m_header.content_length( content_length );
457 return std::move(
this->set_body( std::move( body ) ) );
476 return std::move(
this->append_body( std::move( body_part ) ) );
481
482
490 response_parts_attr_t::not_final_parts,
501 return std::move(
this->flush( std::move( wscb ) ) );
513 std::move(m_connection) };
521 throw_done_must_be_called_once();
534 std::size_t status_line_size{ 0 };
538 m_should_keep_alive_when_header_was_sent =
539 m_header.should_keep_alive();
543 m_response_parts[ 0 ] =
544 writable_item_t{ impl::create_header_string( m_header ) };
547 status_line_size = calculate_status_line_size();
550 if( !m_response_parts.empty() ||
552 response_parts_attr_t::final_parts == response_parts_attr )
555 response_output_flags
{
567 conn->write_response_parts(
569 response_output_flags,
581 if( !m_header_was_sent )
582 m_response_parts.resize( 1 );
584 m_response_parts.resize( 0 );
589 m_response_parts.emplace_back( std::move( body ) );
600 m_response_parts.emplace_back( std::move( body_part ) );
607 if( !m_header_was_sent && m_response_parts.empty() )
609 m_response_parts.reserve( 2 );
610 m_response_parts.emplace_back();
621
622
623
624
629
630
631
640
641
642
645 :
public base_response_builder_t< response_builder_t< chunked_output_t > >
657 bool should_keep_alive )
664 m_chunks.reserve( 4 );
676 m_chunks.emplace_back( std::move( chunk ) );
685 return std::move(
this->append_chunk( std::move( chunk ) ) );
690
691
699 response_parts_attr_t::not_final_parts,
710 return std::move(
this->flush( std::move( wscb ) ) );
722 std::move(m_connection) };
730 throw_done_must_be_called_once();
743 std::size_t status_line_size{ 0 };
746 status_line_size = calculate_status_line_size();
750 auto bufs = create_bufs( response_parts_attr_t::final_parts == response_parts_attr );
754 response_output_flags
{
759 if( !bufs.empty() || wscb )
769 conn->write_response_parts(
771 response_output_flags,
779 m_should_keep_alive_when_header_was_sent =
780 m_header.should_keep_alive();
782 constexpr const char value[] =
"chunked";
783 if( !m_header.has_field( restinio::http_field::transfer_encoding ) )
786 restinio::http_field::transfer_encoding,
787 std::string{ value, impl::ct_string_len( value ) } );
791 auto & current_value =
792 m_header.get_field( restinio::http_field::transfer_encoding );
793 if( std::string::npos == current_value.find( value ) )
795 constexpr const char comma_value[] =
",chunked";
796 m_header.append_field(
797 restinio::http_field::transfer_encoding,
800 impl::ct_string_len( comma_value ) } );
808 writable_items_container_t bufs;
810 std::size_t reserve_size = 2 * m_chunks.size() + 1;
821 bufs.reserve( reserve_size );
826 impl::create_header_string(
828 impl::content_length_field_presence_t::skip_content_length ) );
844 auto chunk_it = m_chunks.begin();
845 const auto chunk_end = m_chunks.end();
846 if( chunk_it != chunk_end )
851 asio_ns::buffer_size( chunk_it->buf() ) ) );
852 bufs.emplace_back( std::move( *chunk_it ) );
854 for( ++chunk_it; chunk_it != chunk_end; ++chunk_it )
859 asio_ns::buffer_size( chunk_it->buf() ) ) );
860 bufs.emplace_back( std::move( *chunk_it ) );
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;
868 if( !m_chunks.empty() )
881 if( appendix_begin != appendix_end )
885 static_cast<std::size_t>(appendix_end - appendix_begin)
900
901
902
903
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.
const request_id_t m_request_id
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
http_response_header_t m_header
void throw_done_must_be_called_once() const
virtual ~base_response_builder_t()=default
Exception class for all exceptions thrown by RESTinio.
exception_t(const char *err)
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
void prepare_header_for_sending()
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)
writable_items_container_t m_response_parts
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.
void if_neccessary_reserve_first_element_for_header()
response_builder_t(response_builder_t &&)=default
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
response_builder_t(response_builder_t &&)=default
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)
void if_neccessary_reserve_first_element_for_header()
bool m_header_was_sent
Flag used by flush() function.
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.
response_builder_t()=delete
Class for storing the buffers used for streaming body (request/response).
std::size_t size() const
Get the size of the underlying buffer object.
Group of writable items transported to the context of underlying connection as one solid piece.
void status_line_size(std::size_t n)
void after_write_notificator(write_status_cb_t notificator) noexcept
Set after write notificator.
#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
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".
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.