RESTinio
ws_connection.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
9#pragma once
10
11#include <queue>
12
14
15#include <http_parser.h>
16
18
19#include <restinio/all.hpp>
25
27
29
30namespace restinio
31{
32
33namespace websocket
34{
35
36namespace basic
37{
38
39namespace impl
40{
41
42using write_groups_queue_t = std::queue< write_group_t >;
43
45constexpr size_t
47{
48 return 14;
49}
50
51//
52// ws_outgoing_data_t
53//
54
57{
58 public:
60 void
62 {
63 m_awaiting_write_groups.emplace( std::move( wg ) );
64 }
65
68 {
70
71 if( !m_awaiting_write_groups.empty() )
72 {
73 result = std::move( m_awaiting_write_groups.front() );
75 }
76
77 return result;
78 }
79
80 private:
83};
84
85//
86// connection_input_t
87//
88
91{
92 connection_input_t( std::size_t buffer_size )
93 : m_buf{ buffer_size }
94 {}
95
98
101
103 std::string m_payload;
104
106 void
108 {
109 m_parser.reset();
110 m_payload.clear();
111 }
112};
113
114//
115// ws_connection_t
116//
117
119template <
120 typename Traits,
121 typename WS_Message_Handler >
123 : public ws_connection_base_t
124 , public restinio::impl::executor_wrapper_t< typename Traits::strand_t >
125{
127
128 public:
129 using message_handler_t = WS_Message_Handler;
130
131 using timer_manager_t = typename Traits::timer_manager_t;
132 using timer_manager_handle_t = std::shared_ptr< timer_manager_t >;
133 using timer_guard_t = typename timer_manager_t::timer_guard_t;
134 using logger_t = typename Traits::logger_t;
135 using strand_t = typename Traits::strand_t;
136 using stream_socket_t = typename Traits::stream_socket_t;
139
140 using ws_weak_handle_t = std::weak_ptr< ws_t >;
141
144 connection_id_t conn_id,
148 stream_socket_t socket,
149 lifetime_monitor_t lifetime_monitor,
151 message_handler_t msg_handler )
152 : ws_connection_base_t{ conn_id }
154 , m_settings{ std::move( settings ) }
155 , m_socket{ std::move( socket ) }
156 , m_lifetime_monitor{ std::move( lifetime_monitor ) }
157 , m_timer_guard{ m_settings->create_timer_guard() }
159 , m_msg_handler{ std::move( msg_handler ) }
160 , m_logger{ *( m_settings->m_logger ) }
161 {
162 // Notify of a new connection instance.
163 m_logger.trace( [&]{
164 return fmt::format(
165 "[connection:{}] move socket to [ws_connection:{}]",
167 connection_id() );
168 } );
169
170 m_logger.trace( [&]{
171 return fmt::format(
172 "[ws_connection:{}] start connection with {}",
174 m_socket.remote_endpoint() );
175 } );
176
177 // Inform state listener if it used.
178 m_settings->call_state_listener( [this]() noexcept {
181 m_socket.remote_endpoint(),
183 };
184 } );
185 }
186
187 ws_connection_t( const ws_connection_t & ) = delete;
191
193 {
194 try
195 {
196 // Notify of a new connection instance.
197 m_logger.trace( [&]{
198 return fmt::format(
199 "[ws_connection:{}] destructor called",
200 connection_id() );
201 } );
202 }
203 catch( ... )
204 {}
205 }
206
208 virtual void
209 shutdown() override
210 {
211 asio_ns::dispatch(
212 this->get_executor(),
213 [ this, ctx = shared_from_this() ]
214 // NOTE: this lambda is noexcept since v.0.6.0.
215 () noexcept {
216 try
217 {
218 // An exception from logger shouldn't prevent
219 // main shutdown actions.
221 [&]{
222 return fmt::format(
223 "[ws_connection:{}] shutdown",
224 connection_id() );
225 } );
226
229 }
230 catch( const std::exception & ex )
231 {
233 [&]{
234 return fmt::format(
235 "[ws_connection:{}] shutdown operation error: {}",
237 ex.what() );
238 } );
239 }
240 } );
241 }
242
244 virtual void
245 kill() override
246 {
247 asio_ns::dispatch(
248 this->get_executor(),
249 [ this, ctx = shared_from_this() ]
250 // NOTE: this lambda is noexcept since v.0.6.0.
251 () noexcept
252 {
253 try
254 {
255 // An exception from logger shouldn't prevent
256 // main kill actions.
258 [&]{
259 return fmt::format(
260 "[ws_connection:{}] kill",
261 connection_id() );
262 } );
263
266
267 close_impl();
268 }
269 catch( const std::exception & ex )
270 {
272 [&]{
273 return fmt::format(
274 "[ws_connection:{}] kill operation error: {}",
276 ex.what() );
277 } );
278 }
279 } );
280 }
281
283 void
284 init_read( ws_handle_t wsh ) override
285 {
286 ws_weak_handle_t wswh{ wsh };
287
288 // Run write message on io_context loop (direct invocation if possible).
289 asio_ns::dispatch(
290 this->get_executor(),
291 [ this, ctx = shared_from_this(), wswh = std::move( wswh ) ]
292 // NOTE: this lambda is noexcept since v.0.6.0.
293 () noexcept
294 {
295 try
296 {
297 // Start timeout checking.
298 m_prepared_weak_ctx = shared_from_this();
300
304 }
305 catch( const std::exception & ex )
306 {
309 [&]{
310 return fmt::format(
311 "[ws_connection:{}] unable to init read: {}",
313 ex.what() );
314 } );
315 }
316 } );
317 }
318
320 virtual void
322 write_group_t wg,
323 bool is_close_frame ) override
324 {
326 asio_ns::dispatch(
327 this->get_executor(),
328 [ this,
329 actual_wg = std::move( wg ),
330 ctx = shared_from_this(),
331 is_close_frame ]
332 // NOTE: this lambda is noexcept since v.0.6.0.
333 () mutable noexcept
334 {
335 try
336 {
339 std::move( actual_wg ),
340 is_close_frame );
341 else
342 {
343 m_logger.warn( [&]{
344 return fmt::format(
345 "[ws_connection:{}] cannot write to websocket: "
346 "write operations disabled",
347 connection_id() );
348 } );
349 }
350 }
351 catch( const std::exception & ex )
352 {
355 [&]{
356 return fmt::format(
357 "[ws_connection:{}] unable to write data: {}",
359 ex.what() );
360 } );
361 }
362 } );
363 }
364 private:
366
370 void
371 close_impl() noexcept
372 {
374 [&]() noexcept {
376 [&]{
377 return fmt::format(
378 "[ws_connection:{}] close socket",
379 connection_id() );
380 } );
381
382 // This actions can throw and because of that we have
383 // to wrap them...
385 m_logger,
386 "ws_connection.close_impl.socket.shutdown",
387 [&] {
388 asio_ns::error_code ignored_ec;
389 m_socket.shutdown(
390 asio_ns::ip::tcp::socket::shutdown_both,
391 ignored_ec );
392 } );
393
395 m_logger,
396 "ws_connection.close_impl.socket.close",
397 [&] {
398 m_socket.close();
399 } );
400 } );
401 }
402
404 void
406 {
409 }
410
412 void
414 {
416 [&]{
419 } );
420 }
421
423 void
424 send_close_frame_to_peer( std::string payload )
425 {
427 bufs.reserve( 2 );
428
429 bufs.emplace_back(
432 opcode_t::connection_close_frame,
433 payload.size() ) );
434
435 bufs.emplace_back( std::move( payload ) );
437
439
440 // No more data must be written.
442 }
443
445 void
448 std::string desc = std::string{} )
449 {
450 send_close_frame_to_peer( std::string{ status_code_to_bin( code ) + desc } );
451 }
452
454
462 template< typename MSG_BUILDER >
463 void
465 status_code_t status,
466 MSG_BUILDER msg_builder ) noexcept
467 {
468 // An exception in logger shouldn't prevent the main actions.
470 m_logger, std::move( msg_builder ) );
471
472 // This can throw but we have to suppress any exceptions.
474 m_logger, "ws_connection.call_close_handler_if_necessary",
475 [this, status] {
477 } );
478
480 }
481
482
484 void
486 {
487 m_logger.trace( [&]{
488 return fmt::format(
489 "[ws_connection:{}] start reading header",
490 connection_id() );
491 } );
492
493 // Prepare parser for consuming new message.
495
496 if( 0 == m_input.m_buf.length() )
497 {
499 }
500 else
501 {
502 // Has something to read from m_input.m_buf.
505 }
506 }
507
509 void
511 {
512 m_logger.trace( [&]{
513 return fmt::format(
514 "[ws_connection:{}] continue reading message",
515 connection_id() );
516 } );
517
518 m_socket.async_read_some(
520 asio_ns::bind_executor(
521 this->get_executor(),
522 [ this, ctx = shared_from_this() ]
523 // NOTE: this lambda is noexcept since v.0.6.0.
524 ( const asio_ns::error_code & ec, std::size_t length ) noexcept
525 {
526 try
527 {
528 after_read_header( ec, length );
529 }
530 catch( const std::exception & ex )
531 {
534 [&]{
535 return fmt::format(
536 "[ws_connection:{}] after read header callback error: {}",
538 ex.what() );
539 } );
540 }
541 } ) );
542 }
543
545 void
546 handle_read_error( const char * desc, const asio_ns::error_code & ec )
547 {
548 // Assume that connection is lost.
551 [&]{
552 return fmt::format(
553 "[ws_connection:{}] {}: {}",
555 desc,
556 ec.message() );
557 } );
558 }
559
561 void
563 const asio_ns::error_code & ec,
564 std::size_t length )
565 {
566 if( !ec )
567 {
568 m_logger.trace( [&]{
569 return fmt::format(
570 "[ws_connection:{}] received {} bytes",
571 this->connection_id(),
572 length );
573 } );
574
575 m_input.m_buf.obtained_bytes( length );
577 }
578 else
579 {
580 handle_read_error( "reading message header error", ec );
581 }
582 }
583
585 void
586 consume_header_from_buffer( const char * data, std::size_t length )
587 {
588 const auto nparsed = m_input.m_parser.parser_execute( data, length );
589
590 m_input.m_buf.consumed_bytes( nparsed );
591
593 {
595 }
596 else
597 {
598 assert( nparsed == length );
600 }
601 }
602
604 void
606 {
607 m_logger.trace( [&]{
608 return fmt::format(
609 "[ws_connection:{}] start handling {} ({:#x})",
612 static_cast<std::uint16_t>(md.m_opcode) );
613 } );
614
615 const auto validation_result =
617
618 if( validation_state_t::frame_header_is_valid != validation_result )
619 {
620 m_logger.error( [&]{
621 return fmt::format(
622 "[ws_connection:{}] invalid header",
623 connection_id() );
624 } );
625
627 {
629 [&]{
631 // Do not wait anything in return, because
632 // protocol is violated.
633 } );
634
636 }
638 {
639 // Wait for close frame cannot be done.
640 close_impl();
641 }
642
643 return;
644 }
645
647 }
648
650 void
652 {
653 const auto payload_length =
655
656 m_input.m_payload.resize( payload_length );
657
658 if( payload_length == 0 )
659 {
660 // Callback for message with 0-size payload.
662 }
663 else
664 {
665 const auto payload_part_size =
666 std::min( m_input.m_buf.length(), payload_length );
667
668 std::memcpy(
669 &m_input.m_payload.front(),
671 payload_part_size );
672
673 m_input.m_buf.consumed_bytes( payload_part_size );
674
675 const std::size_t length_remaining =
676 payload_length - payload_part_size;
677
679 &m_input.m_payload.front(),
680 payload_part_size,
681 length_remaining ) )
682 {
683 if( 0 == length_remaining )
684 {
685 // All message is obtained.
687 }
688 else
689 {
690 // Read the rest of payload:
692 &m_input.m_payload.front() + payload_part_size,
693 length_remaining );
694 }
695 }
696 // Else payload is invalid and validate_payload_part()
697 // has handled the case so do nothing.
698 }
699 }
700
702 void
705 char * payload_data,
707 std::size_t length_remaining,
709 bool do_validate_payload_and_call_msg_handler = true )
710 {
711 m_socket.async_read_some(
712 asio_ns::buffer( payload_data, length_remaining ),
713 asio_ns::bind_executor(
714 this->get_executor(),
715 [ this,
716 ctx = shared_from_this(),
717 payload_data,
718 length_remaining,
719 do_validate_payload_and_call_msg_handler ]
720 // NOTE: this lambda is noexcept since v.0.6.0.
721 ( const asio_ns::error_code & ec, std::size_t length ) noexcept
722 {
723 try
724 {
726 payload_data,
727 length_remaining,
728 ec,
729 length,
730 do_validate_payload_and_call_msg_handler );
731 }
732 catch( const std::exception & ex )
733 {
736 [&]{
737 return fmt::format(
738 "[ws_connection:{}] after read payload callback error: {}",
740 ex.what() );
741 } );
742 }
743 } ) );
744 }
745
747 void
749 char * payload_data,
750 std::size_t length_remaining,
751 const asio_ns::error_code & ec,
752 std::size_t length,
753 bool do_validate_payload_and_call_msg_handler = true )
754 {
755 if( !ec )
756 {
757 m_logger.trace( [&]{
758 return fmt::format(
759 "[ws_connection:{}] received {} bytes",
760 this->connection_id(),
761 length );
762 } );
763
764 assert( length <= length_remaining );
765
766 const std::size_t next_length_remaining =
767 length_remaining - length;
768
769 if( do_validate_payload_and_call_msg_handler )
770 {
771 if( validate_payload_part( payload_data, length, next_length_remaining ) )
772 {
773 if( 0 == next_length_remaining )
774 {
775 // Here: all the payload is ready.
776
777 // All message is obtained.
779 }
780 else
781 {
782 //Here: not all payload is obtained,
783 // so inintiate read once again:
785 payload_data + length,
786 next_length_remaining,
787 do_validate_payload_and_call_msg_handler );
788 }
789 }
790 // Else payload is invalid and validate_payload_part()
791 // has handled the case so do nothing.
792 }
793 else
794 {
795 if( 0 == next_length_remaining )
796 {
798 }
799 else
800 {
802 payload_data + length,
803 length_remaining - length,
804 do_validate_payload_and_call_msg_handler );
805 }
806 }
807 }
808 else
809 {
810 handle_read_error( "reading message payload error", ec );
811 }
812 }
813
815 void
817 {
818 if( auto wsh = m_websocket_weak_handle.lock() )
819 {
820 try
821 {
823 std::move( wsh ),
824 std::move( close_frame ) );
825 }
826 catch( const std::exception & ex )
827 {
828 m_logger.error( [&]{
829 return fmt::format(
830 "[ws_connection:{}] execute handler error: {}",
832 ex.what() );
833 } );
834 }
835 }
836 }
837
839 bool
841 char * payload_data,
842 std::size_t length,
843 std::size_t next_length_remaining )
844 {
845 const auto validation_result =
847
848 if( validation_state_t::payload_part_is_valid != validation_result )
849 {
850 handle_invalid_payload( validation_result );
851
852 if( validation_state_t::incorrect_utf8_data == validation_result )
853 {
854 // Can skip this payload because it was not a bad close frame.
855
856 // It is the case we are expecting close frame
857 // so validator must be ready to receive more headers
858 // and payloads after this frame.
860
861 if( 0 == next_length_remaining )
862 {
864 }
865 else
866 {
867 // Skip checking payload for this frame:
868 const bool do_validate_payload_and_call_msg_handler = false;
870 payload_data + length,
871 next_length_remaining,
872 do_validate_payload_and_call_msg_handler );
873 }
874 }
875 return false;
876 }
877
878 return true;
879 }
880
882 void
884 {
885 m_logger.error( [&]{
886 return fmt::format(
887 "[ws_connection:{}] invalid paload",
888 connection_id() );
889 } );
890
891 if( validation_state_t::invalid_close_code == validation_result )
892 {
893 // A corner case: invalid payload in close frame.
894
896 {
897 // Case: close frame was not expected.
898
899 // This actually must be executed:
901 [&]{
903 // Do not wait anything in return, because
904 // protocol is violated.
905 } );
906
907 // Notify user of a close but use a correct close code.
909 }
911 {
912 // Case: close frame was expected.
913
914 // We got a close frame but it is incorrect,
915 // so just close (there is not too much we can do).
916 close_impl();
917 }
918 }
919 else
920 {
922 {
924 [&]{
927 } );
928
930 }
931 }
932 }
933
934 void
936 {
937 auto & md = m_input.m_parser.current_message();
938
939 const auto validation_result = m_protocol_validator.finish_frame();
940 if( validation_state_t::frame_is_valid == validation_result )
941 {
943 {
944 if( opcode_t::connection_close_frame == md.m_opcode )
945 {
946 m_logger.trace( [&]{
947 return fmt::format(
948 "[ws_connection:{}] got close frame from peer, status: {}",
950 static_cast<std::uint16_t>(
952 } );
953
956 [&]{
958 } );
959
961 }
962
964 std::make_shared< message_t >(
965 md.m_final_flag ? final_frame : not_final_frame,
966 md.m_opcode,
968
971 }
972 else
973 {
975
976 if( opcode_t::connection_close_frame == md.m_opcode )
977 {
978 // Got it!
979 m_timer_guard.cancel();
980
981 close_impl();
982
983 m_logger.trace( [&]{
984 return fmt::format(
985 "[ws_connection:{}] expected close frame came",
986 connection_id() );
987 } );
988 }
989 else
990 {
991 // Wait for next frame.
993 }
994 }
995 }
996 else
997 {
998 handle_invalid_payload( validation_result );
999 }
1000 }
1001
1002 void
1004 {
1006 [&]{
1008 std::make_shared< message_t >(
1010 opcode_t::connection_close_frame,
1011 status_code_to_bin( status ) ) );
1012 } );
1013 }
1014
1016 void
1017 write_data_impl( write_group_t wg, bool is_close_frame )
1018 {
1019 if( m_socket.is_open() )
1020 {
1021 if( is_close_frame )
1022 {
1023 m_logger.trace( [&]{
1024 return fmt::format(
1025 "[ws_connection:{}] user sends close frame",
1026 connection_id() );
1027 } );
1028
1029 m_close_frame_to_peer.disable(); // It is formed and sent by user
1030 m_close_frame_to_user.disable(); // And user knows that websocket is closed.
1031 // No more writes.
1033
1034 // Start waiting only close-frame.
1036 }
1037
1038 // Push write_group to queue.
1040
1042 }
1043 else
1044 {
1045 m_logger.warn( [&]{
1046 return fmt::format(
1047 "[ws_connection:{}] try to write while socket is closed",
1048 connection_id() );
1049 } );
1050
1051 try
1052 {
1056 }
1057 catch( ... )
1058 {}
1059 }
1060 }
1061
1064 void
1066 {
1068 {
1069 init_write();
1070 }
1071 }
1072
1074 void
1076 {
1077 // Here: not writing anything to socket, so
1078 // write operation can be initiated.
1079 auto next_write_group = m_outgoing_data.pop_ready_buffers();
1080
1081 if( next_write_group )
1082 {
1083 m_logger.trace( [&]{
1084 return fmt::format(
1085 "[ws_connection:{}] start next write group, "
1086 "size: {}",
1087 this->connection_id(),
1088 next_write_group->items_count() );
1089 } );
1090
1091 // Initialize write context with a new write group.
1093 std::move( next_write_group ) );
1094
1095 // Start the loop of sending data from current write group.
1097 }
1098 }
1099
1100 // Use aliases for shorter names.
1104
1105 void
1107 {
1108 try
1109 {
1111
1112 if( holds_alternative< trivial_write_operation_t >( wo ) )
1113 {
1114 handle_trivial_write_operation( get< trivial_write_operation_t >( wo ) );
1115 }
1116 else if( holds_alternative< none_write_operation_t >( wo ) )
1117 {
1119 }
1120 else
1121 {
1122 assert( holds_alternative< file_write_operation_t >( wo ) );
1123 throw exception_t{ "sendfile write operation not implemented" };
1124 }
1125 }
1126 catch( const std::exception & ex )
1127 {
1130 [&]{
1131 return fmt::format(
1132 "[ws_connection:{}] handle_current_write_ctx failed: {}",
1133 connection_id(),
1134 ex.what() );
1135 } );
1136 }
1137 }
1138
1139 void
1141 {
1142 // Asio buffers (param for async write):
1143 auto & bufs = op.get_trivial_bufs();
1144
1145 m_logger.trace( [&]{
1146 return fmt::format(
1147 "[ws_connection:{}] sending data with "
1148 "buf count: {}, "
1149 "total size: {}",
1150 connection_id(),
1151 bufs.size(),
1152 op.size() ); } );
1153
1155
1156 // There is somethig to write.
1157 asio_ns::async_write(
1158 m_socket,
1159 bufs,
1160 asio_ns::bind_executor(
1161 this->get_executor(),
1162 [ this,
1163 ctx = shared_from_this() ]
1164 // NOTE: this lambda is noexcept since v.0.6.0.
1165 ( const asio_ns::error_code & ec, std::size_t written ) noexcept
1166 {
1167 try
1168 {
1169 if( !ec )
1170 {
1171 m_logger.trace( [&]{
1172 return fmt::format(
1173 "[ws_connection:{}] outgoing data was sent: {} bytes",
1174 connection_id(),
1175 written );
1176 } );
1177 }
1178
1179 after_write( ec );
1180 }
1181 catch( const std::exception & ex )
1182 {
1185 [&]{
1186 return fmt::format(
1187 "[ws_connection:{}] after write callback error: {}",
1188 connection_id(),
1189 ex.what() );
1190 } );
1191 }
1192 } ) );
1193 }
1194
1196 void
1198 {
1199 // Finishing writing this group.
1200 m_logger.trace( [&]{
1201 return fmt::format(
1202 "[ws_connection:{}] finishing current write group",
1203 this->connection_id() );
1204 } );
1205
1206 // Group notificators are called from here (if exist):
1208
1209 // Start another write opertion
1210 // if there is something to send.
1212 }
1213
1215 void
1216 after_write( const asio_ns::error_code & ec )
1217 {
1218 if( !ec )
1219 {
1221 }
1222 else
1223 {
1226 [&]{
1227 return fmt::format(
1228 "[ws_connection:{}] unable to write: {}",
1229 connection_id(),
1230 ec.message() );
1231 } );
1232
1233 try
1234 {
1236 }
1237 catch( const std::exception & ex )
1238 {
1239 m_logger.error( [&]{
1240 return fmt::format(
1241 "[ws_connection:{}] notificator error: {}",
1242 connection_id(),
1243 ex.what() );
1244 } );
1245 }
1246 }
1247 }
1248
1251
1254
1261
1264 static ws_connection_t &
1266 {
1267 return static_cast< ws_connection_t & >( base );
1268 }
1269
1270 virtual void
1272 {
1273 asio_ns::dispatch(
1274 this->get_executor(),
1275 [ ctx = std::move( self ) ]
1276 // NOTE: this lambda is noexcept since v.0.6.0.
1277 () noexcept
1278 {
1279 auto & conn_object = cast_to_self( *ctx );
1280 // If an exception will be thrown we can only
1281 // close the connection.
1282 try
1283 {
1284 conn_object.check_timeout_impl();
1285 }
1286 catch( const std::exception & x )
1287 {
1288 conn_object.trigger_error_and_close(
1290 [&] {
1291 return fmt::format( "[connection: {}] unexpected "
1292 "error during timeout handling: {}",
1293 conn_object.connection_id(),
1294 x.what() );
1295 } );
1296 }
1297 } );
1298 }
1299
1300 std::chrono::steady_clock::time_point m_write_operation_timeout_after;
1301 std::chrono::steady_clock::time_point m_close_frame_from_peer_timeout_after =
1302 std::chrono::steady_clock::time_point::max();
1305
1306 void
1308 {
1309 const auto now = std::chrono::steady_clock::now();
1311 {
1312 m_logger.trace( [&]{
1313 return fmt::format(
1314 "[wd_connection:{}] write operation timed out",
1315 connection_id() );
1316 } );
1319 close_impl();
1320 }
1322 {
1323 m_logger.trace( [&]{
1324 return fmt::format(
1325 "[wd_connection:{}] waiting for close-frame from peer timed out",
1326 connection_id() );
1327 } );
1328 close_impl();
1329 }
1330 else
1331 {
1333 }
1334 }
1335
1337 void
1339 {
1341 }
1342
1344 void
1346 {
1348 std::chrono::steady_clock::now() + m_settings->m_write_http_response_timelimit;
1349 }
1350
1351 void
1353 {
1355 std::chrono::steady_clock::now() + m_settings->m_read_next_http_message_timelimit;
1356 }
1358
1361
1364
1367
1370
1373
1376
1379
1381 enum class write_state_t
1382 {
1384 write_enabled,
1386 write_disabled
1387 };
1388
1391
1393 enum class read_state_t
1394 {
1396 read_any_frame,
1398 read_only_close_frame,
1400 read_nothing
1401 };
1402
1405
1409 {
1410 public:
1411 template < typename Action >
1412 void
1413 run_if_first( Action && action ) noexcept(noexcept(action()))
1414 {
1415 if( m_not_executed_yet )
1416 {
1417 m_not_executed_yet = false;
1418 action();
1419 }
1420 }
1421
1423 void
1425 {
1426 m_not_executed_yet = false;
1427 }
1428
1429 private:
1431 };
1432
1436};
1437
1438} /* namespace impl */
1439
1440} /* namespace basic */
1441
1442} /* namespace websocket */
1443
1444} /* namespace restinio */
Helper type for controlling the lifetime of the connection.
An object with info about connection to be passed to state listener.
Type of object that tells that the connection has been upgraded to WebSocket.
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
Wrapper for an executor (strand) used by connections.
Traits::strand_t & get_executor() noexcept
An executor for callbacks on async operations.
Helper class for reading bytes and feeding them to parser.
std::size_t length() const noexcept
How many unconsumed bytes are there in buffer.
void consumed_bytes(std::size_t length) noexcept
Mark how many bytes were obtained.
void obtained_bytes(std::size_t length) noexcept
Mark how many bytes were obtained.
auto make_asio_buffer() noexcept
Make asio buffer for reading bytes from socket.
const char * bytes() const noexcept
Get pointer to unconsumed bytes.
auto size() const noexcept
The size of data within this operation.
const std::vector< asio_ns::const_buffer > & get_trivial_bufs() const noexcept
Get buffer "iovec" for performing gather write.
Helper class for writting response data.
void fail_write_group(const asio_ns::error_code &ec)
Handle current group write process failed.
solid_write_operation_variant_t extract_next_write_operation()
et an object with next write operation to perform.
void finish_write_group()
Finish writing group normally.
void start_next_write_group(optional_t< write_group_t > next_wg) noexcept
Start handlong next write group.
bool transmitting() const noexcept
Check if data is trunsmitting now.
connection_id_t connection_id() const noexcept
Get connection id.
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
std::uint64_t payload_len() const
Get payload len.
Definition: ws_parser.hpp:92
A helper class for running exclusive action. Only a first action will run.
void run_if_first(Action &&action) noexcept(noexcept(action()))
void disable()
Disable ation: action will not be executed even on a first shot.
Context for handling websocket connections.
void finish_handling_current_write_ctx()
Do post write actions for current write group.
void after_read_header(const asio_ns::error_code &ec, std::size_t length)
Handle read operation result, when reading header.
restinio::impl::connection_settings_handle_t< Traits > m_settings
Common paramaters of a connection.
void consume_header_from_socket()
Initiate read operation on socket to receive bytes for header.
message_handler_t m_msg_handler
Websocket message handler provided by user.
void handle_parsed_header(const message_details_t &md)
Handle parsed header.
bool validate_payload_part(char *payload_data, std::size_t length, std::size_t next_length_remaining)
Validates a part of received payload.
ws_weak_handle_t m_websocket_weak_handle
A waek handler for owning ws_t to use it when call message handler.
virtual void shutdown() override
Shutdown websocket.
typename connection_count_limit_types< Traits >::lifetime_monitor_t lifetime_monitor_t
void send_close_frame_to_peer(std::string payload)
Send close frame to peer.
void write_data_impl(write_group_t wg, bool is_close_frame)
Implementation of writing data performed on the asio_ns::io_context.
@ read_only_close_frame
Reads only close frame: skip all frames until close-frame.
@ read_nothing
Do not read anything (before activation).
@ read_any_frame
Reads any type of frame and serve it to user.
@ write_disabled
No more outgoing data can be added (e.g. close-frame was sent).
std::chrono::steady_clock::time_point m_close_frame_from_peer_timeout_after
typename timer_manager_t::timer_guard_t timer_guard_t
void init_write_if_necessary()
Checks if there is something to write, and if so starts write operation.
void trigger_error_and_close(status_code_t status, MSG_BUILDER msg_builder) noexcept
Trigger an error.
void start_read_header()
Start the process of reading ws messages from socket.
void start_read_payload(char *payload_data, std::size_t length_remaining, bool do_validate_payload_and_call_msg_handler=true)
Start reading message payload.
tcp_connection_ctx_weak_handle_t m_prepared_weak_ctx
void handle_trivial_write_operation(const trivial_write_operation_t &op)
void init_next_timeout_checking()
schedule next timeout checking.
void handle_invalid_payload(validation_state_t validation_result)
Handle payload errors.
void after_read_payload(char *payload_data, std::size_t length_remaining, const asio_ns::error_code &ec, std::size_t length, bool do_validate_payload_and_call_msg_handler=true)
Handle read operation result, when reading payload.
void call_close_handler_if_necessary(status_code_t status)
void consume_header_from_buffer(const char *data, std::size_t length)
Parse header from internal buffer.
void send_close_frame_to_peer(status_code_t code, std::string desc=std::string{})
Send close frame to peer.
ws_connection_t(const ws_connection_t &)=delete
virtual void check_timeout(tcp_connection_ctx_handle_t &self) override
void handle_read_error(const char *desc, const asio_ns::error_code &ec)
Handle read error (reading header or payload)
write_state_t m_write_state
A state of a websocket output.
ws_protocol_validator_t m_protocol_validator
Helper for validating protocol.
void handle_parsed_and_valid_header(const message_details_t &md)
Handle parsed and valid header.
ws_connection_t & operator=(const ws_connection_t &)=delete
void start_waiting_close_frame_only()
Start waiting for close-frame.
std::chrono::steady_clock::time_point m_write_operation_timeout_after
virtual void kill() override
Kill websocket.
void guard_write_operation()
Start guard write operation if necessary.
ws_outgoing_data_t m_outgoing_data
Output buffers queue.
void init_read(ws_handle_t wsh) override
Start reading ws-messages.
std::shared_ptr< timer_manager_t > timer_manager_handle_t
void call_message_handler(message_handle_t close_frame)
Call user message handler with current message.
static ws_connection_t & cast_to_self(tcp_connection_ctx_base_t &base)
Timers.
lifetime_monitor_t m_lifetime_monitor
Monitor of the connection lifetime.
void after_write(const asio_ns::error_code &ec)
Handle write response finished.
restinio::impl::write_group_output_ctx_t m_write_output_ctx
Write to socket operation context.
void close_impl() noexcept
Standard close routine.
typename Traits::stream_socket_t stream_socket_t
virtual void write_data(write_group_t wg, bool is_close_frame) override
Write pieces of outgoing data.
void graceful_close()
Close WebSocket connection in a graceful manner.
read_state_t m_read_state
A state of a websocket input.
typename Traits::timer_manager_t timer_manager_t
write_groups_queue_t m_awaiting_write_groups
A queue of buffers.
void append(write_group_t wg)
Add buffers to queue.
const message_details_t & current_message() const
Get current mesasge details.
Definition: ws_parser.hpp:303
size_t parser_execute(const char *data, size_t size)
Parse piece of data from buffer.
Definition: ws_parser.hpp:265
bool header_parsed() const
Check header of current websocket message is parsed.
Definition: ws_parser.hpp:284
validation_state_t process_and_unmask_next_payload_part(char *data, size_t size)
Validate next part of current frame and reset source part to unmasked data.
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:692
void invoke_after_write_notificator_if_exists(const asio_ns::error_code &ec)
Get after write notificator.
Definition: buffers.hpp:815
Detection of compiler version and absence of various features.
#define RESTINIO_ENSURE_NOEXCEPT_CALL(expr)
A wrapper around static_assert for checking that an expression is noexcept and execution of that expr...
A special wrapper around fmtlib include files.
std::shared_ptr< connection_settings_t< Traits > > connection_settings_handle_t
std::size_t uint64_to_size_t(std::uint64_t v)
Helper function for truncating uint64 to std::size_t with exception if that truncation will lead to d...
void suppress_exceptions(Logger &&logger, const char *block_description, Lambda &&lambda) noexcept
Helper function for execution a block of code with suppression of any exceptions raised inside that b...
void log_error_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
void log_trace_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
raw_data_t write_message_details(const message_details_t &message)
Serialize websocket message details into bytes buffer.
Definition: ws_parser.hpp:515
constexpr size_t websocket_header_max_size()
Max possible size of websocket frame header (a part before payload).
std::queue< write_group_t > write_groups_queue_t
validation_state_t
States of validated frame.
std::shared_ptr< message_t > message_handle_t
Request handler, that is the type for calling request handlers.
Definition: message.hpp:227
constexpr final_frame_flag_t final_frame
Definition: message.hpp:135
constexpr final_frame_flag_t not_final_frame
Definition: message.hpp:136
const char * opcode_to_string(opcode_t opcode)
Helper sunction to get method string name.
Definition: message.hpp:46
status_code_t status_code_from_bin(string_view_t data)
Definition: message.hpp:112
std::string status_code_to_bin(status_code_t code)
Definition: message.hpp:101
std::shared_ptr< ws_t > ws_handle_t
Alias for ws_t handle.
std::shared_ptr< tcp_connection_ctx_base_t > tcp_connection_ctx_handle_t
Alias for http connection handle.
std::vector< writable_item_t > writable_items_container_t
Definition: buffers.hpp:668
std::weak_ptr< tcp_connection_ctx_base_t > tcp_connection_ctx_weak_handle_t
Alias for http connection weak handle.
@ write_was_not_executed
After write notificator error: data was not sent, connection closed (or aborted) before a given piece...
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
std::uint64_t connection_id_t
Type for ID of connection.
STL namespace.
Helpers for safe truncation of unsigned integers.
Definition: inftrees.h:24
restinio::impl::fixed_buffer_t m_buf
Input buffer.
void reset_parser_and_payload()
Prepare parser for reading new http-message.