1 /*!
2  * @file
3  * @brief Interfaces for connection_handlers.
4  */
5 
6 #pragma once
7 
8 #include <arataga/acl_handler/sequence_number.hpp>
9 
10 #include <arataga/utils/can_throw.hpp>
11 #include <arataga/utils/string_literal.hpp>
12 
13 #include <arataga/config.hpp>
14 
15 #include <arataga/logging/wrap_logging.hpp>
16 
17 #include <arataga/nothrow_block/macros.hpp>
18 
19 #include <so_5/all.hpp>
20 
21 #include <asio/ip/tcp.hpp>
22 #include <asio/write.hpp>
23 
24 #include <fmt/format.h>
25 #include <fmt/ostream.h>
26 
27 #include <spdlog/spdlog.h>
28 
29 #include <noexcept_ctcheck/pub.hpp>
30 
31 #include <chrono>
32 #include <cstddef>
33 #include <functional>
34 #include <memory>
35 #include <string_view>
36 
37 namespace arataga::acl_handler
38 {
39 
40 //
41 // config_t
42 //
43 /*!
44  * @brief An interface of object for accessing the config.
45  */
46 class config_t
47 {
48 public:
49 	virtual ~config_t();
50 
51 	[[nodiscard]]
52 	virtual acl_protocol_t
53 	acl_protocol() const noexcept = 0;
54 
55 	[[nodiscard]]
56 	virtual const asio::ip::address &
57 	out_addr() const noexcept = 0;
58 
59 	[[nodiscard]]
60 	virtual std::size_t
61 	io_chunk_size() const noexcept = 0;
62 
63 	[[nodiscard]]
64 	virtual std::size_t
65 	io_chunk_count() const noexcept = 0;
66 
67 	[[nodiscard]]
68 	virtual std::chrono::milliseconds
69 	protocol_detection_timeout() const noexcept = 0;
70 
71 	[[nodiscard]]
72 	virtual std::chrono::milliseconds
73 	socks_handshake_phase_timeout() const noexcept = 0;
74 
75 	[[nodiscard]]
76 	virtual std::chrono::milliseconds
77 	dns_resolving_timeout() const noexcept = 0;
78 
79 	[[nodiscard]]
80 	virtual std::chrono::milliseconds
81 	authentification_timeout() const noexcept = 0;
82 
83 	[[nodiscard]]
84 	virtual std::chrono::milliseconds
85 	connect_target_timeout() const noexcept = 0;
86 
87 	[[nodiscard]]
88 	virtual std::chrono::milliseconds
89 	socks_bind_timeout() const noexcept = 0;
90 
91 	[[nodiscard]]
92 	virtual std::chrono::milliseconds
93 	idle_connection_timeout() const noexcept = 0;
94 
95 	[[nodiscard]]
96 	virtual std::chrono::milliseconds
97 	http_headers_complete_timeout() const noexcept = 0;
98 
99 	[[nodiscard]]
100 	virtual std::chrono::milliseconds
101 	http_negative_response_timeout() const noexcept = 0;
102 
103 	[[nodiscard]]
104 	virtual const http_message_value_limits_t &
105 	http_message_limits() const noexcept = 0;
106 };
107 
108 //
109 // remove_reason_t
110 //
111 //! Enumeration for connection-handler removal reason.
112 enum remove_reason_t
113 {
114 	//! Normal completion of connection serving.
115 	normal_completion,
116 	//! I/O error detected.
117 	io_error,
118 	//! The current operation timed-out.
119 	current_operation_timed_out,
120 	//! Unsupported protocol detected.
121 	unsupported_protocol,
122 	//! Some protocol-related error. For example, an unsupported protocol
123 	//! version detected.
124 	protocol_error,
125 	//! Some unexpected case that can't be correctly handled.
126 	unexpected_and_unsupported_case,
127 	//! There is no activity in the connection for too long time.
128 	no_activity_for_too_long,
129 	//! The current operation was cancelled from outside.
130 	current_operation_canceled,
131 	//! A uncaught exception fron connection-handled detected.
132 	unhandled_exception,
133 	//! The required IP-version can't be used.
134 	//! For example, an attempt to connect IPv6 address from IPv4 address.
135 	ip_version_mismatch,
136 	//! The user has no required permissions.
137 	access_denied,
138 	//! The failure of target domain name resolution.
139 	unresolved_target,
140 	//! The connection to the target host was broken.
141 	target_end_broken,
142 	//! The connection from the user was broken.
143 	user_end_broken,
144 	//! HTTP-reposnse was received before the completion of outgoing
145 	//! HTTP-request.
146 	http_response_before_completion_of_http_request,
147 	//! The connection from the user was closed by the user-end.
148 	user_end_closed_by_client,
149 	//! The user didn't send a new incoming HTTP-request.
150 	http_no_incoming_request
151 };
152 
153 [[nodiscard]]
154 inline constexpr arataga::utils::string_literal_t
to_string_literal(remove_reason_t reason)155 to_string_literal( remove_reason_t reason )
156 {
157 	using namespace arataga::utils::string_literals;
158 
159 	auto result = "<unknown>"_static_str;
160 	switch( reason )
161 	{
162 	case remove_reason_t::normal_completion:
163 		result = "normal_completion"_static_str;
164 	break;
165 
166 	case remove_reason_t::io_error:
167 		result = "io_error"_static_str;
168 	break;
169 
170 	case remove_reason_t::current_operation_timed_out:
171 		result = "current_operation_timed_out"_static_str;
172 	break;
173 
174 	case remove_reason_t::unsupported_protocol:
175 		result = "unsupported_protocol"_static_str;
176 	break;
177 
178 	case remove_reason_t::protocol_error:
179 		result = "protocol_error"_static_str;
180 	break;
181 
182 	case remove_reason_t::unexpected_and_unsupported_case:
183 		result = "unexpected_and_unsupported_case"_static_str;
184 	break;
185 
186 	case remove_reason_t::no_activity_for_too_long:
187 		result = "no_activity_for_too_long"_static_str;
188 	break;
189 
190 	case remove_reason_t::current_operation_canceled:
191 		result = "current_operation_canceled"_static_str;
192 	break;
193 
194 	case remove_reason_t::unhandled_exception:
195 		result = "unhandled_exception"_static_str;
196 	break;
197 
198 	case remove_reason_t::ip_version_mismatch:
199 		result = "ip_version_mismatch"_static_str;
200 	break;
201 
202 	case remove_reason_t::access_denied:
203 		result = "access_denied"_static_str;
204 	break;
205 
206 	case remove_reason_t::unresolved_target:
207 		result = "unresolved_target"_static_str;
208 	break;
209 
210 	case remove_reason_t::target_end_broken:
211 		result = "target_end_broken"_static_str;
212 	break;
213 
214 	case remove_reason_t::user_end_broken:
215 		result = "user_end_broken"_static_str;
216 	break;
217 
218 	case remove_reason_t::http_response_before_completion_of_http_request:
219 		result = "http_response_before_completion_of_http_request"_static_str;
220 	break;
221 
222 	case remove_reason_t::user_end_closed_by_client:
223 		result = "user_end_closed_by_client"_static_str;
224 	break;
225 
226 	case remove_reason_t::http_no_incoming_request:
227 		result = "http_no_incoming_request"_static_str;
228 	break;
229 	}
230 
231 	return result;
232 }
233 
234 inline std::ostream &
operator <<(std::ostream & to,remove_reason_t reason)235 operator<<( std::ostream & to, remove_reason_t reason )
236 {
237 	return (to << to_string_literal(reason));
238 }
239 
240 // The definition is going below.
241 class connection_handler_t;
242 
243 //
244 // connection_handler_shptr_t
245 //
246 /*!
247  * @brief An alias for shared_ptr to connection_handler.
248  */
249 using connection_handler_shptr_t = std::shared_ptr< connection_handler_t >;
250 
251 //
252 // traffic_limiter_t
253 //
254 /*!
255  * @brief An interface for object that limits the bandwidth.
256  *
257  * An implementation of that interface should clean all resources
258  * in its destructor.
259  */
260 class traffic_limiter_t
261 {
262 public:
263 	enum class direction_t { from_user, from_target };
264 
265 	/*!
266 	 * @brief The result of asking a quote for reading incoming
267 	 * data on the current turn.
268 	 *
269 	 * If the direction can be read then m_capacity will contain
270 	 * an enabled amount of data to be read. After the completion
271 	 * of the read operation method release() should be called
272 	 * for reserved_capacity_t object.
273 	 */
274 	struct reserved_capacity_t
275 	{
276 		std::size_t m_capacity;
277 		sequence_number_t m_sequence_number;
278 
279 		//! The method for registering the result of I/O operation
280 		//! in traffic_limiter.
281 		/*!
282 		 * This method analyses the error code and, if that code is not 0,
283 		 * assumes that 0 bytes are read.
284 		 *
285 		 * @attention
286 		 * This method must be called after the completion of I/O operation.
287 		 * Otherwise the reserved capacity will remain reserved till the
288 		 * end of the current turn.
289 		 */
290 		void
291 		release(
292 			traffic_limiter_t & limiter,
293 			direction_t dir,
294 			const asio::error_code & ec,
295 			std::size_t bytes_transferred ) const noexcept;
296 	};
297 
298 	traffic_limiter_t( const traffic_limiter_t & ) = delete;
299 	traffic_limiter_t( traffic_limiter_t && ) = delete;
300 
301 	traffic_limiter_t();
302 	virtual ~traffic_limiter_t();
303 
304 	// Can return 0.
305 	// In that case attempts of reading data should be suspended
306 	// until the next turn.
307 	[[nodiscard]]
308 	virtual reserved_capacity_t
309 	reserve_read_portion(
310 		direction_t dir,
311 		std::size_t buffer_size ) noexcept = 0;
312 
313 	virtual void
314 	release_reserved_capacity(
315 		direction_t dir,
316 		reserved_capacity_t reserved_capacity,
317 		std::size_t actual_bytes ) noexcept = 0;
318 };
319 
320 //
321 // traffic_limiter_unique_ptr_t
322 //
323 /*!
324  * @brief An alias for unique_ptr to traffic_limiter.
325  */
326 using traffic_limiter_unique_ptr_t =
327 	std::unique_ptr< traffic_limiter_t >;
328 
329 namespace dns_resolving
330 {
331 
332 //! The result of successful DNS-resolving.
333 struct hostname_found_t
334 {
335 	//! IP-address for the domain name.
336 	asio::ip::address m_ip;
337 };
338 
339 //! The result of failed DNS-resolving.
340 struct hostname_not_found_t
341 {
342 	//! Textual description of the failure.
343 	std::string m_error_desc;
344 };
345 
346 //! Type for DNS-resolving result.
347 using hostname_result_t = std::variant<
348 		hostname_found_t,
349 		hostname_not_found_t
350 	>;
351 
352 //! Type of a functor that should be called after the completion
353 //! of DNS-resolving.
354 using hostname_result_handler_t =
355 	std::function< void(const hostname_result_t &) >;
356 
357 } /* namespace dns_resolving */
358 
359 namespace authentification
360 {
361 
362 //! Parameters for an authentification request.
363 struct request_params_t
364 {
365 	asio::ip::address_v4 m_user_ip;
366 	std::optional< std::string > m_username;
367 	std::optional< std::string > m_password;
368 	std::string m_target_host;
369 	std::uint16_t m_target_port;
370 };
371 
372 //! Enumeration of reasons for failed authentification.
373 enum class failure_reason_t
374 {
375 	unknown_user,
376 	target_blocked
377 };
378 
379 [[nodiscard]]
380 inline constexpr arataga::utils::string_literal_t
to_string_literal(failure_reason_t reason)381 to_string_literal( failure_reason_t reason )
382 {
383 	using namespace arataga::utils::string_literals;
384 
385 	switch( reason )
386 	{
387 	case failure_reason_t::unknown_user:
388 		return "user unknown"_static_str;
389 	case failure_reason_t::target_blocked:
390 		return "target is blocked for user"_static_str;
391 	}
392 
393 	return "<unknown>"_static_str;
394 }
395 
396 //! Type of negative authentification result.
397 struct failure_t
398 {
399 	failure_reason_t m_reason;
400 };
401 
402 //! Type of positive authentification result.
403 struct success_t
404 {
405 	//! Actual traffic limiter for the new connection.
406 	traffic_limiter_unique_ptr_t m_traffic_limiter;
407 };
408 
409 //! Type for authentification result.
410 using result_t = std::variant< failure_t, success_t >;
411 
412 //! Type of a functor to be called after the completion of
413 //! user authentification.
414 using result_handler_t =
415 	// The result is passed by value to enable to borrow
416 	// moveable only values.
417 	std::function< void(result_t) >;
418 
419 } /* namespace dns_resolving */
420 
421 //
422 // connection_type_t
423 //
424 /*!
425  * @brief Enumeration of various types of connections.
426  */
427 enum class connection_type_t
428 {
429 	//! Type of the connection is not known yet.
430 	/*!
431 	 * This type should be used for stats of total number of connections.
432 	 */
433 	generic,
434 	//! The connection uses SOCKS5 protocol.
435 	socks5,
436 	//! The connection uses HTTP protocol.
437 	http
438 };
439 
440 namespace details {
441 
442 class delete_protector_maker_t;
443 
444 } /* namespace details */
445 
446 //
447 // delete_protector_t
448 //
449 /*!
450  * @brief A special marker that tells that connection_handler is
451  * protected from the deletion and it's safe to replace the current
452  * handler by a new one.
453  *
454  * An instance of delete_protector does nothing. But it presence
455  * tells that there is an instance of
456  * details::delete_protector_maker_t somewhere at the stack. And
457  * that delete_protector_maker_t defends the connection_handler
458  * from early deletion.
459  *
460  * The necessity of that class goes from the fact that connection_handler calls
461  * remove_connection_handler and replace_connection_handler from inside of own
462  * methods. As result of remove_connection_handler and
463  * replace_connection_handler the current handler can be deleted and this
464  * invalidates the `this` value. This can lead to use-after-free errors (for
465  * example if some non-static method will be acidentially called after the
466  * return from remove_connection_handler/replace_connection_handler).
467  *
468  * For protection from use-after-free error a scheme with additional
469  * connection_handler_shptr_t is used. This additional instance is created on
470  * the stack and only then methods like
471  * remove_connection_handler/replace_connection_handler are called.  This
472  * additional instance protects the current handler from the deletion.
473  */
474 class delete_protector_t
475 {
476 	friend class delete_protector_maker_t;
477 
478 	delete_protector_t() noexcept = default;
479 
480 public:
481 	~delete_protector_t() noexcept = default;
482 
483 	delete_protector_t( const delete_protector_t & ) noexcept = default;
484 	delete_protector_t( delete_protector_t && ) noexcept = default;
485 
486 	delete_protector_t &
487 	operator=( const delete_protector_t & ) noexcept = default;
488 	delete_protector_t &
489 	operator=( delete_protector_t && ) noexcept = default;
490 };
491 
492 namespace details {
493 
494 class delete_protector_maker_t
495 {
496 public:
delete_protector_maker_t(connection_handler_shptr_t &)497 	delete_protector_maker_t( connection_handler_shptr_t & ) {}
498 
499 	[[nodiscard]]
500 	delete_protector_t
make() const501 	make() const noexcept { return {}; }
502 };
503 
504 } /* namespace details */
505 
506 //
507 // handler_context_t
508 //
509 /*!
510  * @brief An interface of objects that holds a context in that
511  * user's connections are handled.
512  */
513 class handler_context_t
514 {
515 public:
516 	virtual ~handler_context_t();
517 
518 	//! Type of connection ID inside that context.
519 	using connection_id_t = std::uint_fast64_t;
520 
521 	virtual void
522 	replace_connection_handler(
523 		delete_protector_t,
524 		connection_id_t id,
525 		connection_handler_shptr_t handler ) = 0;
526 
527 	virtual void
528 	remove_connection_handler(
529 		delete_protector_t,
530 		connection_id_t id,
531 		remove_reason_t reason ) noexcept = 0;
532 
533 	// NOTE: this method should be called inside logging::wrap_logging!
534 	virtual void
535 	log_message_for_connection(
536 		connection_id_t id,
537 		::arataga::logging::processed_log_level_t level,
538 		std::string_view message ) = 0;
539 
540 	[[nodiscard]]
541 	virtual const config_t &
542 	config() const noexcept = 0;
543 
544 	virtual void
545 	async_resolve_hostname(
546 		connection_id_t id,
547 		const std::string & hostname,
548 		dns_resolving::hostname_result_handler_t result_handler ) = 0;
549 
550 	virtual void
551 	async_authentificate(
552 		connection_id_t id,
553 		authentification::request_params_t request,
554 		authentification::result_handler_t result_handler ) = 0;
555 
556 	virtual void
557 	stats_inc_connection_count(
558 		connection_type_t connection_type ) = 0;
559 };
560 
561 //
562 // handler_context_holder_t
563 //
564 /*!
565  * @brief A special class that guarantees that handler_context
566  * will exist until someone holds a smart reference to it.
567  *
568  * There a danger in the use of Asio's async operations: completion
569  * handler for an operation can be called after the destruction of
570  * handler_context. In that case connection_handler will hold a
571  * dangled reference to handler_context. Any attempt to use
572  * that reference (a call to log_message_for_connection for example)
573  * will lead to undefined behaviour.
574  *
575  * To avoid that class handler_context_holder_t plays a role of
576  * a smart reference (or smart pointer) to handler_context.
577  * If a connection_handler holds an instance of
578  * handler_context_holder_t then it guarantees that handler_context
579  * will live even if handler_context itself has finished its work.
580  */
581 class handler_context_holder_t
582 {
583 	//! A smart pointer to the object that holds handler_context.
584 	so_5::agent_ref_t m_holder_agent;
585 
586 	//! A reference to the actual handler_context.
587 	std::reference_wrapper< handler_context_t > m_context;
588 
589 public:
handler_context_holder_t(so_5::agent_ref_t holder_agent,handler_context_t & context)590 	handler_context_holder_t(
591 		so_5::agent_ref_t holder_agent,
592 		handler_context_t & context ) noexcept
593 		:	m_holder_agent{ std::move(holder_agent) }
594 		,	m_context{ context }
595 	{}
596 
597 	[[nodiscard]]
598 	handler_context_t &
ctx() const599 	ctx() const noexcept { return m_context.get(); }
600 };
601 
602 //
603 // connection_handler_t
604 //
605 /*!
606  * @brief A base type for connection_handler.
607  *
608  * This type not only defines an interface of connection_handler
609  * but also contains a basic functionality necessary for all
610  * connection_handler implementations.
611  */
612 class connection_handler_t
613 	: public std::enable_shared_from_this< connection_handler_t >
614 {
615 public:
616 	//! Handler status.
617 	enum class status_t
618 	{
619 		//! Handler is active. It can handle the results of I/O operations.
620 		active,
621 		//! Handler is released. It was removed or replaced by another
622 		//! handler. Because of that it can't handle the results
623 		//! of I/O operations.
624 		released
625 	};
626 
627 protected:
628 	/*!
629 	 * @brief A special indicator that tells that exceptions can go out.
630 	 *
631 	 * This indicator tells a method/lambda that it's invoked inside
632 	 * try/catch block and throwing of an exception is permited.
633 	 *
634 	 * An instance is created inside wrap_action_and_handle_exceptions()
635 	 * and passed as a parameter to lambda-argument of
636 	 * wrap_action_and_handle_exceptions().
637 	 */
638 	using can_throw_t = ::arataga::utils::can_throw_t;
639 
640 	//! Context for connection handler.
641 	handler_context_holder_t m_ctx;
642 
643 	//! ID for the connection.
644 	handler_context_t::connection_id_t m_id;
645 
646 	//! The connection with the client.
647 	/*!
648 	 * That is the socked accepted by ACL.
649 	 */
650 	asio::ip::tcp::socket m_connection;
651 
652 	//! Handler status.
653 	status_t m_status;
654 
655 	/*!
656 	 * @name Methods inside those the handler can be removed/replaced.
657 	 * @{
658 	 */
659 	virtual void
660 	on_start_impl( delete_protector_t ) = 0;
661 
662 	virtual void
663 	on_timer_impl( delete_protector_t ) = 0;
664 	/*!
665 	 * @}
666 	 */
667 
668 	[[nodiscard]]
669 	handler_context_t &
context()670 	context() noexcept { return m_ctx.ctx(); }
671 
672 	/*!
673 	 * @brief Replace the handler to a new one.
674 	 */
675 	template< typename New_Handler_Factory >
676 	void
replace_handler(delete_protector_t delete_protector,can_throw_t can_throw,New_Handler_Factory && new_handler_factory)677 	replace_handler(
678 		delete_protector_t delete_protector,
679 		can_throw_t can_throw,
680 		New_Handler_Factory && new_handler_factory )
681 	{
682 		// If there will be an exception we'll have no choice except
683 		// the removement of the old handler.
684 		//
685 		// The same picture is also for problems with the exceptions
686 		// during the creation of a new handler. The old handler can
687 		// be in invalid state when new_handler_factory throws. So we can
688 		// only delete the old handler.
689 		//
690 		// Will catch only exceptions derived from std::exception.
691 		// All other types of exception will crash the whole app.
692 		[&]() noexcept {
693 			NOEXCEPT_CTCHECK_STATIC_ASSERT_NOEXCEPT(
694 					handler_context_holder_t{ m_ctx } );
695 
696 			// Make a copy of handler_context_holder because as
697 			// the result of new_handler_factory invocation the m_ctx
698 			// can become empty.
699 			handler_context_holder_t ctx_holder = m_ctx;
700 			try
701 			{
702 				connection_handler_shptr_t new_handler = new_handler_factory(
703 						can_throw );
704 
705 				ctx_holder.ctx().replace_connection_handler(
706 						delete_protector,
707 						m_id,
708 						std::move(new_handler) );
709 			}
710 			catch( const std::exception & x )
711 			{
712 				// Ignore exceptions that can be thrown during the logging.
713 				ARATAGA_NOTHROW_BLOCK_BEGIN()
714 					ARATAGA_NOTHROW_BLOCK_STAGE(log_exception)
715 
716 					::arataga::logging::wrap_logging(
717 							proxy_logging_mode,
718 							spdlog::level::err,
719 							[this, &ctx_holder, &x]( auto level )
720 							{
721 								ctx_holder.ctx().log_message_for_connection(
722 										m_id,
723 										level,
724 										x.what() );
725 							} );
726 				ARATAGA_NOTHROW_BLOCK_END(LOG_THEN_IGNORE)
727 
728 				NOEXCEPT_CTCHECK_ENSURE_NOEXCEPT_STATEMENT(
729 						ctx_holder.ctx().remove_connection_handler(
730 								delete_protector,
731 								m_id,
732 								remove_reason_t::unexpected_and_unsupported_case )
733 				);
734 			}
735 		}();
736 	}
737 
738 	//! Remove the handler.
739 	void
remove_handler(delete_protector_t delete_protector,remove_reason_t remove_reason)740 	remove_handler(
741 		delete_protector_t delete_protector,
742 		remove_reason_t remove_reason )
743 	{
744 		context().remove_connection_handler(
745 				delete_protector, m_id, remove_reason );
746 	}
747 
748 	// NOTE: this method should be called from inside logging::wrap_logging.
749 	void
log_message_for_connection(can_throw_t,::arataga::logging::processed_log_level_t level,std::string_view message)750 	log_message_for_connection(
751 		can_throw_t /*can_throw*/,
752 		::arataga::logging::processed_log_level_t level,
753 		std::string_view message )
754 	{
755 		context().log_message_for_connection( m_id, level, message );
756 	}
757 
758 	void
log_and_remove_connection_on_io_error(delete_protector_t delete_protector,can_throw_t can_throw,const asio::error_code & ec,std::string_view operation_description)759 	log_and_remove_connection_on_io_error(
760 		delete_protector_t delete_protector,
761 		can_throw_t can_throw,
762 		const asio::error_code & ec,
763 		std::string_view operation_description )
764 	{
765 		// Should log the error except operation_aborted (this error is
766 		// expected).
767 		if( asio::error::operation_aborted != ec )
768 		{
769 			::arataga::logging::wrap_logging(
770 					proxy_logging_mode,
771 					spdlog::level::warn,
772 					[&]( auto level )
773 					{
774 						log_message_for_connection(
775 								can_throw,
776 								level,
777 								fmt::format( "IO-error on {}: {}",
778 										operation_description, ec.message() ) );
779 					} );
780 		}
781 
782 		remove_handler( delete_protector, remove_reason_t::io_error );
783 	}
784 
785 	void
log_and_remove_connection(delete_protector_t delete_protector,can_throw_t can_throw,remove_reason_t reason,spdlog::level::level_enum level,std::string_view description)786 	log_and_remove_connection(
787 		delete_protector_t delete_protector,
788 		can_throw_t can_throw,
789 		remove_reason_t reason,
790 		spdlog::level::level_enum level,
791 		std::string_view description )
792 	{
793 		::arataga::logging::wrap_logging(
794 				proxy_logging_mode,
795 				level,
796 				[this, can_throw, description]( auto level )
797 				{
798 					log_message_for_connection(
799 							can_throw,
800 							level,
801 							description );
802 				} );
803 
804 		remove_handler( delete_protector, reason );
805 	}
806 
807 	template< typename Action >
808 	void
wrap_action_and_handle_exceptions(delete_protector_t delete_protector,Action && action)809 	wrap_action_and_handle_exceptions(
810 		delete_protector_t delete_protector,
811 		Action && action )
812 	{
813 		try
814 		{
815 			::arataga::utils::exception_handling_context_t ctx;
816 
817 			action( delete_protector, ctx.make_can_throw_marker() );
818 		}
819 		catch( const std::exception & x )
820 		{
821 			// We have to catch and suppress exceptions from here.
822 			ARATAGA_NOTHROW_BLOCK_BEGIN()
823 				ARATAGA_NOTHROW_BLOCK_STAGE(log_and_remove_connection)
824 
825 				::arataga::utils::exception_handling_context_t ctx;
826 
827 				//FIXME: what if fmt::format throws?
828 				log_and_remove_connection(
829 						delete_protector,
830 						ctx.make_can_throw_marker(),
831 						remove_reason_t::unhandled_exception,
832 						spdlog::level::err,
833 						fmt::format( "exception caught: {}", x.what() )
834 					);
835 			ARATAGA_NOTHROW_BLOCK_END(LOG_THEN_IGNORE)
836 		}
837 		catch( ... )
838 		{
839 			// We have to catch and suppress exceptions from here.
840 			ARATAGA_NOTHROW_BLOCK_BEGIN()
841 				ARATAGA_NOTHROW_BLOCK_STAGE(
842 						log_and_remove_connection_on_unknow_exception)
843 
844 				::arataga::utils::exception_handling_context_t ctx;
845 
846 				log_and_remove_connection(
847 						delete_protector,
848 						ctx.make_can_throw_marker(),
849 						remove_reason_t::unhandled_exception,
850 						spdlog::level::err,
851 						"unknown exception caught" );
852 			ARATAGA_NOTHROW_BLOCK_END(LOG_THEN_IGNORE);
853 		}
854 	}
855 
856 	template< typename... Args >
857 	friend struct completion_handler_maker_t;
858 
859 	/*!
860 	 * @brief A helper class for the creation of a callback
861 	 * for completion handler of an I/O operation.
862 	 *
863 	 * There is a tricky moment related to completion-handlers:
864 	 * they can be called after the removal of the coresponding
865 	 * connection_handler. In that case a completion-handler
866 	 * shouldn't do its work.
867 	 *
868 	 * The connection_handler's status should be checked at the
869 	 * beginning of completion-handler. The work should be done only
870 	 * if the connection_handler is still active (the status is
871 	 * status_t::active).
872 	 *
873 	 * Because there ara many different completion-handlers it's
874 	 * boring task to write that check in every of handlers. Another
875 	 * approach is used here: a programmer writes completion-handler
876 	 * in the form of a lambda. This lambda then wrapped into another
877 	 * lambda-function that is passed to Asio. This wrapper perform
878 	 * necessary status check and calls programmer's lambda if
879 	 * the status allows process the result of I/O operation.
880 	 *
881 	 * The class completion_handler_maker_t is a part of process
882 	 * of creation of the wrapped mentioned above.
883 	 *
884 	 * This is two-step process, because we have to create
885 	 * completion-handlers with different signatures.
886 	 *
887 	 * An instance of completion_handler_maker_t is created on the first step.
888 	 * The template parameters for it will be types of arguments for
889 	 * the completion-handler.
890 	 *
891 	 * Method make_handler is called on the second step. That method
892 	 * gets an actual completion-handler lambda as an argument and
893 	 * returns a proper wrapper.
894 	 *
895 	 * @attention
896 	 * This first parameter for user-provided completion-handler will be
897 	 * a value of delete_protector_t type. Then there will be a value
898 	 * of type can_throw_t. Then there will be parameters of types @a Args.
899 	 *
900 	 * @note
901 	 * Initialy a wrapper, created inside make_handler() method, doesn't
902 	 * catch exceptions. But then wrap_action_and_handle_exceptions() was
903 	 * used inside so now user-provided completion-handler is called
904 	 * from try...catch block.
905 	 */
906 	template< typename... Args >
907 	struct completion_handler_maker_t
908 	{
909 		connection_handler_shptr_t m_handler;
910 
completion_handler_maker_tarataga::acl_handler::connection_handler_t::completion_handler_maker_t911 		completion_handler_maker_t(
912 			connection_handler_shptr_t handler )
913 			:	m_handler{ std::move(handler) }
914 		{}
915 
916 		template< typename Completion >
917 		[[nodiscard]]
918 		auto
make_handlerarataga::acl_handler::connection_handler_t::completion_handler_maker_t919 		make_handler( Completion && completion ) &&
920 		{
921 			return
922 				[handler = std::move(m_handler),
923 					completion_func = std::move(completion)]
924 				( Args ...args ) mutable
925 				{
926 					if( status_t::active == handler->m_status )
927 					{
928 						details::delete_protector_maker_t protector{ handler };
929 						handler->wrap_action_and_handle_exceptions(
930 								protector.make(),
931 								[&](
932 									delete_protector_t delete_protector,
933 									can_throw_t can_throw )
934 								{
935 									completion_func(
936 											delete_protector,
937 											can_throw,
938 											std::forward<Args>(args)... );
939 								} );
940 					}
941 				};
942 		}
943 	};
944 
945 	//! Do the first step of completion-handler creation.
946 	/*!
947 	 * Usage example:
948 	 * @code
949 	 * with<const asio::error_code &, std::size_t>().make_handler(
950 	 * 	[this]( delete_protector_t delete_protector,
951 	 * 		can_throw_t can_throw,
952 	 * 		const asio::error_code & ec,
953 	 * 		std::size_t bytes_transferred )
954 	 * 	{
955 	 * 		... // Handler code.
956 	 * 	});
957 	 * @endcode
958 	 */
959 	template< typename... Args >
960 	completion_handler_maker_t< Args... >
with()961 	with() noexcept
962 	{
963 		return { shared_from_this() };
964 	}
965 
966 	template< typename Completion >
967 	[[nodiscard]]
968 	auto
make_read_write_completion_handler(arataga::utils::string_literal_t op_name,Completion && completion)969 	make_read_write_completion_handler(
970 		// Require string-literal because this value has to be valid
971 		// until the end of completion-handler work.
972 		arataga::utils::string_literal_t op_name,
973 		Completion && completion )
974 	{
975 		return with<const asio::error_code &, std::size_t>()
976 			.make_handler(
977 				// There is no sense to call shared_from_this because
978 				// it's already done by make_io_completion_handler.
979 				[this, op_name, completion_func = std::move(completion)](
980 				delete_protector_t delete_protector,
981 				can_throw_t can_throw,
982 				const asio::error_code & ec,
983 				std::size_t bytes_transferred ) mutable
984 				{
985 					if( ec )
986 						log_and_remove_connection_on_io_error(
987 								delete_protector,
988 								can_throw, ec, op_name );
989 					else
990 						completion_func(
991 								delete_protector,
992 								can_throw, bytes_transferred );
993 				} );
994 	}
995 
996 	template<
997 		typename Buffer,
998 		typename Completion >
999 	void
read_some(can_throw_t,asio::ip::tcp::socket & connection,Buffer & buffer,Completion && completion)1000 	read_some(
1001 		can_throw_t /*can_throw*/,
1002 		asio::ip::tcp::socket & connection,
1003 		Buffer & buffer,
1004 		Completion && completion )
1005 	{
1006 		using namespace arataga::utils::string_literals;
1007 
1008 		connection.async_read_some(
1009 				buffer.asio_buffer(),
1010 				make_read_write_completion_handler(
1011 					"read"_static_str,
1012 					[completion_func = std::move(completion), &buffer](
1013 						delete_protector_t delete_protector,
1014 						can_throw_t can_throw,
1015 						std::size_t bytes_transferred )
1016 					{
1017 						buffer.increment_bytes_read( bytes_transferred );
1018 						completion_func( delete_protector, can_throw );
1019 					} )
1020 		);
1021 	}
1022 
1023 	template<
1024 		typename Buffer,
1025 		typename Completion >
1026 	void
write_whole(can_throw_t,asio::ip::tcp::socket & connection,Buffer & buffer,Completion && completion)1027 	write_whole(
1028 		can_throw_t /*can_throw*/,
1029 		asio::ip::tcp::socket & connection,
1030 		Buffer & buffer,
1031 		Completion && completion )
1032 	{
1033 		using namespace arataga::utils::string_literals;
1034 
1035 		asio::async_write(
1036 				connection,
1037 				buffer.asio_buffer(),
1038 				make_read_write_completion_handler(
1039 					"write"_static_str,
1040 					[&buffer, completion_func = std::move(completion)](
1041 						delete_protector_t delete_protector,
1042 						can_throw_t can_throw,
1043 						std::size_t bytes_transferred ) mutable
1044 					{
1045 						buffer.increment_bytes_written( bytes_transferred );
1046 						completion_func( delete_protector, can_throw );
1047 					} )
1048 		);
1049 	}
1050 
1051 public:
1052 	connection_handler_t(
1053 		handler_context_holder_t ctx,
1054 		handler_context_t::connection_id_t id,
1055 		asio::ip::tcp::socket connection );
1056 
1057 	virtual ~connection_handler_t();
1058 
1059 	void
1060 	on_start();
1061 
1062 	void
1063 	on_timer();
1064 
1065 	[[nodiscard]]
1066 	virtual arataga::utils::string_literal_t
1067 	name() const noexcept = 0;
1068 
1069 	// The default implementation closes m_connection if it is not closed yet.
1070 	// The status is changed to status_t::released.
1071 	virtual void
1072 	release() noexcept;
1073 };
1074 
1075 } /* namespace arataga::acl_handler */
1076 
1077