14 #include <fmt/format.h> 17 #include <restinio/common_types.hpp> 18 #include <restinio/http_headers.hpp> 19 #include <restinio/os.hpp> 20 #include <restinio/sendfile.hpp> 21 #include <restinio/impl/connection_base.hpp> 23 #include <restinio/impl/header_helpers.hpp> 36 const auto tpoint = make_gmtime( t );
38 std::array<
char, 64 > buf;
43 "%a, %d %b %Y %H:%M:%S GMT",
46 return std::string{ buf.data() };
52 return make_date_field_value( std::chrono::system_clock::to_time_t( tp ) );
59 template <
typename Response_Builder >
73 http_status_line_t status_line,
74 impl::connection_handle_t connection,
75 request_id_t request_id,
76 bool should_keep_alive )
102 std::string field_name,
103 std::string field_value ) &
114 std::string field_name,
115 std::string field_value ) &&
141 http_field_t field_id,
142 std::string field_value ) &
153 http_field_t field_id,
154 std::string field_value ) &&
165 std::chrono::system_clock::time_point tp =
175 std::chrono::system_clock::time_point tp =
227 throw exception_t{
"done() cannot be called twice" };
243 template <
typename Response_Output_Strategy >
278 auto size = body.size();
279 return set_body_impl( body, size );
286 return std::move(
this->set_body( std::move( body ) ) );
293 auto size = body_part.size();
294 return append_body_impl( body_part, size );
301 return std::move(
this->append_body( std::move( body_part ) ) );
310 const response_output_flags_t
311 response_output_flags{
312 response_parts_attr_t::final_parts,
313 response_connection_attr( m_header.should_keep_alive() ) };
315 m_header.content_length( m_body_size );
319 m_response_parts[ 0 ] =
320 writable_item_t{ impl::create_header_string( m_header ) };
322 write_group_t wg{ std::move( m_response_parts ) };
323 wg.status_line_size( calculate_status_line_size() );
327 wg.after_write_notificator( std::move( wscb ) );
330 auto conn = std::move( m_connection );
332 conn->write_response_parts(
334 response_output_flags,
339 throw_done_must_be_called_once();
342 return restinio::request_accepted();
353 m_response_parts.resize( 1 );
357 m_response_parts.emplace_back( std::move( body ) );
360 m_body_size = body_size;
370 if( 0 < append_size )
372 m_response_parts.emplace_back( std::move( body_part ) );
373 m_body_size += append_size;
382 if( 0 == m_response_parts.size() )
384 m_response_parts.reserve( 2 );
385 m_response_parts.emplace_back();
422 m_header.content_length( content_length );
430 return std::move(
this->set_content_length( content_length ) );
437 auto size = body.size();
438 return set_body_impl( body, size );
445 return std::move(
this->set_body( std::move( body ) ) );
452 auto size = body_part.size();
457 return append_body_impl( body_part );
464 return std::move(
this->append_body( std::move( body_part ) ) );
478 response_parts_attr_t::not_final_parts,
489 return std::move(
this->flush( std::move( wscb ) ) );
499 std::move( m_connection ),
500 response_parts_attr_t::final_parts,
505 throw_done_must_be_called_once();
508 return restinio::request_accepted();
514 impl::connection_handle_t conn,
515 response_parts_attr_t response_parts_attr,
516 write_status_cb_t wscb )
518 std::size_t status_line_size{ 0 };
522 m_should_keep_alive_when_header_was_sent =
523 m_header.should_keep_alive();
527 m_response_parts[ 0 ] =
528 writable_item_t{ impl::create_header_string( m_header ) };
531 status_line_size = calculate_status_line_size();
534 if( !m_response_parts.empty() ||
536 response_parts_attr_t::final_parts == response_parts_attr )
538 const response_output_flags_t
539 response_output_flags{
541 response_connection_attr( m_should_keep_alive_when_header_was_sent ) };
543 write_group_t wg{ std::move( m_response_parts ) };
544 wg.status_line_size( status_line_size );
548 wg.after_write_notificator( std::move( wscb ) );
551 conn->write_response_parts(
553 response_output_flags,
565 if( !m_header_was_sent )
566 m_response_parts.resize( 1 );
568 m_response_parts.resize( 0 );
573 m_response_parts.emplace_back( std::move( body ) );
584 m_response_parts.emplace_back( std::move( body_part ) );
591 if( !m_header_was_sent && 0 == m_response_parts.size() )
593 m_response_parts.reserve( 2 );
594 m_response_parts.emplace_back();
638 http_status_line_t status_line,
639 impl::connection_handle_t connection,
640 request_id_t request_id,
641 bool should_keep_alive )
648 m_chunks.reserve( 4 );
657 auto size = chunk.size();
660 m_chunks.emplace_back( std::move( chunk ) );
669 return std::move(
this->append_chunk( std::move( chunk ) ) );
683 response_parts_attr_t::not_final_parts,
694 return std::move(
this->flush( std::move( wscb ) ) );
704 std::move( m_connection ),
705 response_parts_attr_t::final_parts,
710 throw_done_must_be_called_once();
713 return restinio::request_accepted();
719 impl::connection_handle_t conn,
720 response_parts_attr_t response_parts_attr,
721 write_status_cb_t wscb )
723 std::size_t status_line_size{ 0 };
726 status_line_size = calculate_status_line_size();
730 auto bufs = create_bufs( response_parts_attr_t::final_parts == response_parts_attr );
733 const response_output_flags_t
734 response_output_flags{
736 response_connection_attr( m_should_keep_alive_when_header_was_sent ) };
739 if( !bufs.empty() || wscb )
741 write_group_t wg{ std::move( bufs ) };
742 wg.status_line_size( status_line_size );
746 wg.after_write_notificator( std::move( wscb ) );
749 conn->write_response_parts(
751 response_output_flags,
759 m_should_keep_alive_when_header_was_sent =
760 m_header.should_keep_alive();
762 constexpr const char value[] =
"chunked";
763 if( !m_header.has_field( restinio::http_field::transfer_encoding ) )
766 restinio::http_field::transfer_encoding,
767 std::string{ value, impl::ct_string_len( value ) } );
771 auto & current_value =
772 m_header.get_field( restinio::http_field::transfer_encoding );
773 if( std::string::npos == current_value.find( value ) )
775 constexpr const char comma_value[] =
",chunked";
776 m_header.append_field(
777 restinio::http_field::transfer_encoding,
780 impl::ct_string_len( comma_value ) } );
788 writable_items_container_t bufs;
790 std::size_t reserve_size = 2 * m_chunks.size() + 1;
801 bufs.reserve( reserve_size );
806 impl::create_header_string(
808 impl::content_length_field_presence_t::skip_content_length ) );
811 const char * format_string =
"{:X}\r\n";
812 for(
auto & chunk : m_chunks )
817 asio_ns::buffer_size( chunk.buf() ) ) );
820 format_string =
"\r\n{:X}\r\n";
822 bufs.emplace_back( std::move( chunk ) );
826 const char *
const ending_representation =
"\r\n" "0\r\n\r\n";
827 const char * appendix_begin = ending_representation + 2;
828 const char * appendix_end = appendix_begin;
830 if( !m_chunks.empty() )
843 if( appendix_begin != appendix_end )
845 bufs.emplace_back( const_buffer( appendix_begin, appendix_end - appendix_begin ) );
Response_Builder && append_header(http_field_t field_id, std::string field_value) &&
Add header field.
std::size_t calculate_status_line_size() const noexcept
Response_Builder & append_header(http_field_t field_id, std::string field_value) &
Add header field.
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 && set_body(writable_item_t body) &&
Set body.
self_type_t & set_body_impl(writable_item_t &body, std::size_t body_size)
writable_items_container_t create_bufs(bool add_zero_chunk)
Response_Builder && connection_close() &&noexcept
Set connection close.
Response_Builder & connection_keep_alive() &noexcept
Set connection keep-alive.
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 && append_chunk(writable_item_t chunk) &&
Append current chunk.
self_type_t && flush(write_status_cb_t wscb=write_status_cb_t{}) &&
Flush ready outgoing data.
self_type_t && set_content_length(std::size_t content_length) &&
Manualy set content length.
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.
base_response_builder_t(http_status_line_t status_line, impl::connection_handle_t connection, request_id_t request_id, bool should_keep_alive)
writable_items_container_t m_response_parts
Body accumulator.
Response_Builder && append_header_date_field(std::chrono::system_clock::time_point tp=std::chrono::system_clock::now()) &&
Add header Date field.
self_type_t & flush(write_status_cb_t wscb=write_status_cb_t{}) &
Flush ready outgoing data.
self_type_t & append_body(writable_item_t body_part) &
Append body.
self_type_t && append_body(writable_item_t body_part) &&
Append body.
self_type_t && append_body(writable_item_t body_part) &&
Append body.
writable_items_container_t m_response_parts
Response_Builder & append_header_date_field(std::chrono::system_clock::time_point tp=std::chrono::system_clock::now()) &
Add header Date field.
bool m_should_keep_alive_when_header_was_sent
Saved keep_alive attr actual at the point a header data was sent.
http_response_header_t & header() noexcept
Accessors for header.
base_response_builder_t(base_response_builder_t &&)=default
base_response_builder_t & operator=(base_response_builder_t &&)=default
bool m_header_was_sent
Flag used by flush() function.
self_type_t & set_body(writable_item_t body) &
Set body.
self_type_t & set_content_length(std::size_t content_length) &
Manualy set content length.
void send_ready_data(impl::connection_handle_t conn, response_parts_attr_t response_parts_attr, write_status_cb_t wscb)
http_response_header_t m_header
Forbid arbitrary response_builder_t instantiations.
self_type_t & append_body_impl(writable_item_t &body_part, std::size_t append_size)
response_builder_t(response_builder_t &&)=default
self_type_t & append_body_impl(writable_item_t &body_part)
Tag type for user controlled output response builder.
self_type_t & set_body_impl(writable_item_t &body, std::size_t body_size)
self_type_t && flush(write_status_cb_t wscb=write_status_cb_t{}) &&
Flush ready outgoing data.
void if_neccessary_reserve_first_element_for_header()
Response_Builder & connection_close() &noexcept
Set connection close.
Tag type for RESTinio controlled output response builder.
const request_id_t m_request_id
Response_Builder & append_header(http_header_field_t http_header_field) &
Add header field.
impl::connection_handle_t m_connection
Response_Builder & upcast_reference() noexcept
virtual ~base_response_builder_t()
request_handling_status_t done(write_status_cb_t wscb=write_status_cb_t{})
Complete response.
Response_Builder && connection_keep_alive() &&noexcept
self_type_t && set_body(writable_item_t body) &&
Set body (part).
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 (part).
writable_items_container_t m_chunks
Chunks accumulator.
base_response_builder_t & operator=(const base_response_builder_t &)=delete
response_builder_t(response_builder_t &&)=default
Tag type for chunked output response builder.
Response_Builder && append_header(http_header_field_t http_header_field) &&
Add header field.
void prepare_header_for_sending()
void throw_done_must_be_called_once() const
std::string make_date_field_value(std::chrono::system_clock::time_point tp)
void send_ready_data(impl::connection_handle_t conn, response_parts_attr_t response_parts_attr, write_status_cb_t wscb)
bool m_header_was_sent
Flag used by flush() function.
self_type_t & flush(write_status_cb_t wscb=write_status_cb_t{}) &
Flush ready outgoing data.
response_builder_t(response_builder_t &&)=default
void if_neccessary_reserve_first_element_for_header()
base_response_builder_t(const base_response_builder_t &)=delete
const http_response_header_t & header() const noexcept
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container ¶ms, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
self_type_t & append_body(writable_item_t body_part) &
Append body.
response_builder_t()=delete