/*
 * Copyright (c) 2017-2023 zhllxt
 *
 * author   : zhllxt
 * email    : 37792738@qq.com
 * 
 * Distributed under the Boost Software License, Version 1.0. (See accompanying
 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#if defined(ASIO2_ENABLE_SSL) || defined(ASIO2_USE_SSL)

#ifndef __ASIO2_HTTPS_EXECUTE_HPP__
#define __ASIO2_HTTPS_EXECUTE_HPP__

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <asio2/base/detail/push_options.hpp>

#include <asio2/base/detail/function_traits.hpp>
#include <asio2/util/string.hpp>

#include <asio2/http/detail/http_util.hpp>
#include <asio2/http/detail/http_make.hpp>
#include <asio2/http/detail/http_traits.hpp>

#include <asio2/component/socks/socks5_client_cp.hpp>

namespace asio2::detail
{
	template<class derived_t, class args_t, bool Enable = is_https_execute_download_enabled<args_t>::value()>
	struct https_execute_impl_bridge;

	template<class derived_t, class args_t>
	struct https_execute_impl_bridge<derived_t, args_t, false>
	{
	};

	template<class derived_t, class args_t>
	struct https_execute_impl_bridge<derived_t, args_t, true>
	{
	protected:
		template<typename String, typename StrOrInt, class Proxy, class Body, class Fields, class Buffer>
		static void _execute_with_socks5(
			asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket,
			asio::ssl::stream<asio::ip::tcp::socket&>& stream,
			http::parser<false, Body, typename Fields::allocator_type>& parser,
			Buffer& buffer,
			String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, Proxy&& proxy)
		{
			auto sk5 = detail::to_shared_ptr(std::forward<Proxy>(proxy));

			std::string_view h{ sk5->host() };
			std::string_view p{ sk5->port() };
			
			if (static_cast<int>(sk5->command()) == 0)
			{
				sk5->command(socks5::command::connect);
			}

			// Look up the domain name
			resolver.async_resolve(h, p, [&, s5 = std::move(sk5)]
			(const error_code& ec1, const asio::ip::tcp::resolver::results_type& endpoints) mutable
			{
				if (ec1) { set_last_error(ec1); return; }

				// Make the connection on the IP address we get from a lookup
				asio::async_connect(socket, endpoints,
				[&, s5 = std::move(s5)](const error_code& ec2, const asio::ip::tcp::endpoint&) mutable
				{
					if (ec2) { set_last_error(ec2); return; }

					socks5_async_handshake
					(
						detail::to_string(std::forward<String  >(host)),
						detail::to_string(std::forward<StrOrInt>(port)),
						socket,
						std::move(s5),
						[&](error_code ecs5, std::string, std::string) mutable
						{
							if (ecs5) { set_last_error(ecs5); return; }

							// https://github.com/djarek/certify
							if (auto it = req.find(http::field::host); it != req.end())
							{
								std::string hostname(it->value());
								SSL_set_tlsext_host_name(stream.native_handle(), hostname.data());
							}

							stream.async_handshake(asio::ssl::stream_base::client,
							[&](const error_code& ec3) mutable
							{
								if (ec3) { set_last_error(ec3); return; }

								http::async_write(stream, req, [&](const error_code& ec4, std::size_t) mutable
								{
									// can't use stream.shutdown(),in some case the shutdowm will blocking forever.
									if (ec4) { set_last_error(ec4); stream.async_shutdown([](const error_code&) {}); return; }

									// Then start asynchronous reading
									http::async_read(stream, buffer, parser,
									[&](const error_code& ec5, std::size_t) mutable
									{
										// Reading completed, assign the read the result to last error
										// If the code does not execute into here, the last error
										// is the default value timed_out.
										set_last_error(ec5);

										stream.async_shutdown([](const error_code&) mutable {});
									});
								});
							});
						}
					);
				});
			});
		}

		template<typename String, typename StrOrInt, class Body, class Fields, class Buffer>
		static void _execute_trivially(
			asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket,
			asio::ssl::stream<asio::ip::tcp::socket&>& stream,
			http::parser<false, Body, typename Fields::allocator_type>& parser,
			Buffer& buffer,
			String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req)
		{
			detail::ignore_unused(ioc);

			// Look up the domain name
			resolver.async_resolve(std::forward<String>(host), detail::to_string(std::forward<StrOrInt>(port)),
			[&](const error_code& ec1, const asio::ip::tcp::resolver::results_type& endpoints) mutable
			{
				if (ec1) { set_last_error(ec1); return; }

				// Make the connection on the IP address we get from a lookup
				asio::async_connect(socket, endpoints,
				[&](const error_code& ec2, const asio::ip::tcp::endpoint&) mutable
				{
					if (ec2) { set_last_error(ec2); return; }

					// https://github.com/djarek/certify
					if (auto it = req.find(http::field::host); it != req.end())
					{
						std::string hostname(it->value());
						SSL_set_tlsext_host_name(stream.native_handle(), hostname.data());
					}

					stream.async_handshake(asio::ssl::stream_base::client,
					[&](const error_code& ec3) mutable
					{
						if (ec3) { set_last_error(ec3); return; }

						http::async_write(stream, req, [&](const error_code& ec4, std::size_t) mutable
						{
							// can't use stream.shutdown(),in some case the shutdowm will blocking forever.
							if (ec4) { set_last_error(ec4); stream.async_shutdown([](const error_code&) {}); return; }

							// Then start asynchronous reading
							http::async_read(stream, buffer, parser,
							[&](const error_code& ec5, std::size_t) mutable
							{
								// Reading completed, assign the read the result to last error
								// If the code does not execute into here, the last error
								// is the default value timed_out.
								set_last_error(ec5);

								stream.async_shutdown([](const error_code&) mutable {});
							});
						});
					});
				});
			});
		}

		template<typename String, typename StrOrInt, class Proxy, class Body, class Fields, class Buffer>
		static void _execute_impl(
			asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket,
			asio::ssl::stream<asio::ip::tcp::socket&>& stream,
			http::parser<false, Body, typename Fields::allocator_type>& parser,
			Buffer& buffer,
			String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, Proxy&& proxy)
		{
			// if has socks5 proxy
			if constexpr (std::is_base_of_v<asio2::socks5::option_base,
				typename detail::element_type_adapter<detail::remove_cvref_t<Proxy>>::type>)
			{
				derived_t::_execute_with_socks5(ioc, resolver, socket, stream, parser, buffer
					, std::forward<String>(host), std::forward<StrOrInt>(port)
					, req
					, std::forward<Proxy>(proxy)
				);
			}
			else
			{
				detail::ignore_unused(proxy);

				derived_t::_execute_trivially(ioc, resolver, socket, stream, parser, buffer
					, std::forward<String>(host), std::forward<StrOrInt>(port)
					, req
				);
			}
		}

	public:
		template<typename String, typename StrOrInt, class Rep, class Period, class Proxy,
			class Body = http::string_body, class Fields = http::fields, class Buffer = beast::flat_buffer>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			http::parser<false, Body, typename Fields::allocator_type> parser;

			// First assign default value timed_out to last error
			set_last_error(asio::error::timed_out);

			// set default result to unknown
			parser.get().result(http::status::unknown);
			parser.eager(true);

			// The io_context is required for all I/O
			asio::io_context ioc;

			// These objects perform our I/O
			asio::ip::tcp::resolver resolver{ ioc };
			asio::ip::tcp::socket socket{ ioc };
			asio::ssl::stream<asio::ip::tcp::socket&> stream(socket, const_cast<asio::ssl::context&>(ctx));

			// This buffer is used for reading and must be persisted
			Buffer buffer;

			// Some sites must set the http::field::host
			if (req.find(http::field::host) == req.end())
			{
				std::string strhost = asio2::to_string(host);
				std::string strport = asio2::to_string(port);
				if (strport != "443")
				{
					strhost += ":";
					strhost += strport;
				}
				req.set(http::field::host, strhost);
			}
			// Some sites must set the http::field::user_agent
			if (req.find(http::field::user_agent) == req.end())
			{
				req.set(http::field::user_agent,
					"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36");
			}

			// do work
			derived_t::_execute_impl(ioc, resolver, socket, stream, parser, buffer
				, std::forward<String>(host), std::forward<StrOrInt>(port)
				, req
				, std::forward<Proxy>(proxy)
			);

			// timedout run
			ioc.run_for(timeout);

			error_code ec_ignore{};

			// Gracefully close the socket
			socket.shutdown(asio::ip::tcp::socket::shutdown_both, ec_ignore);
			socket.cancel(ec_ignore);
			socket.close(ec_ignore);

			return parser.release();
		}

		// ----------------------------------------------------------------------------------------

		template<typename String, typename StrOrInt, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(ctx, std::forward<String>(host), std::forward<StrOrInt>(port),
				req, timeout, std::in_place);
		}

		template<typename String, typename StrOrInt,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req)
		{
			return derived_t::execute(ctx, std::forward<String>(host), std::forward<StrOrInt>(port),
				req, std::chrono::milliseconds(http_execute_timeout), std::in_place);
		}

		// ----------------------------------------------------------------------------------------

		template<typename String, typename StrOrInt, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD },
				std::forward<String>(host), std::forward<StrOrInt>(port), req, timeout, std::in_place);
		}

		template<typename String, typename StrOrInt, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, http::request<Body, Fields>& req)
		{
			return derived_t::execute(
				std::forward<String>(host), std::forward<StrOrInt>(port),
				req, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		template<class Rep, class Period, class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(
			const asio::ssl::context& ctx, http::web_request& req, std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(ctx, req.url().host(), req.url().port(), req.base(), timeout, std::in_place);
		}

		template<class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(
			const asio::ssl::context& ctx, http::web_request& req)
		{
			return derived_t::execute(ctx, req, std::chrono::milliseconds(http_execute_timeout));
		}

		template<class Rep, class Period, class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(
			http::web_request& req, std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, req, timeout);
		}

		template<class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(http::web_request& req)
		{
			return derived_t::execute(req, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Rep, class Period, class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(const asio::ssl::context& ctx, std::string_view url,
			std::chrono::duration<Rep, Period> timeout)
		{
			http::web_request req = http::make_request(url);
			if (get_last_error())
			{
				return http::response<Body, Fields>{ http::status::unknown, 11};
			}
			return derived_t::execute(ctx, req.host(), req.port(), req.base(), timeout, std::in_place);
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(const asio::ssl::context& ctx, std::string_view url)
		{
			return derived_t::execute(ctx, url, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Rep, class Period, class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(std::string_view url,
			std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, url, timeout);
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Body = http::string_body, class Fields = http::fields>
		static inline http::response<Body, Fields> execute(std::string_view url)
		{
			return derived_t::execute(url, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			std::string_view target, std::chrono::duration<Rep, Period> timeout)
		{
			http::web_request req = http::make_request(host, port, target);
			if (get_last_error())
			{
				return http::response<Body, Fields>{ http::status::unknown, 11};
			}
			return derived_t::execute(ctx,
				std::forward<String>(host), std::forward<StrOrInt>(port),
				req.base(), timeout, std::in_place);
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			std::string_view target)
		{
			return derived_t::execute(ctx,
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::can_convert_to_string_v<detail::remove_cvref_t<String>>,
			http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, std::string_view target,
			std::chrono::duration<Rep, Period> timeout)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD },
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, timeout);
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::can_convert_to_string_v<detail::remove_cvref_t<String>>,
			http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, std::string_view target)
		{
			return derived_t::execute(
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, std::chrono::milliseconds(http_execute_timeout));
		}

		// ----------------------------------------------------------------------------------------

		template<typename String, typename StrOrInt, class Proxy,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, Proxy&& proxy)
		{
			return derived_t::execute(ctx, std::forward<String>(host), std::forward<StrOrInt>(port),
				req, std::chrono::milliseconds(http_execute_timeout), std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		template<typename String, typename StrOrInt, class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port,
			http::request<Body, Fields>& req, std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD },
				std::forward<String>(host), std::forward<StrOrInt>(port),
				req, timeout, std::forward<Proxy>(proxy));
		}

		template<typename String, typename StrOrInt, class Proxy,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, http::request<Body, Fields>& req, Proxy&& proxy)
		{
			return derived_t::execute(
				std::forward<String>(host), std::forward<StrOrInt>(port),
				req, std::chrono::milliseconds(http_execute_timeout), std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		template<class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, http::web_request& req,
			std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			return derived_t::execute(ctx, req.url().host(), req.url().port(), req.base(), timeout,
				std::forward<Proxy>(proxy));
		}

		template<class Proxy, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(const asio::ssl::context& ctx, http::web_request& req, Proxy&& proxy)
		{
			return derived_t::execute(ctx, req, std::chrono::milliseconds(http_execute_timeout),
				std::forward<Proxy>(proxy));
		}

		template<class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			http::web_request& req, std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, req, timeout,
				std::forward<Proxy>(proxy));
		}

		template<class Proxy, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(http::web_request& req, Proxy&& proxy)
		{
			return derived_t::execute(req, std::chrono::milliseconds(http_execute_timeout),
				std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, std::string_view url,
			std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			http::web_request req = http::make_request(url);
			if (get_last_error())
			{
				return http::response<Body, Fields>{ http::status::unknown, 11};
			}
			return derived_t::execute(ctx, req.host(), req.port(), req.base(), timeout, std::forward<Proxy>(proxy));
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Proxy, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(const asio::ssl::context& ctx, std::string_view url, Proxy&& proxy)
		{
			return derived_t::execute(ctx, url, std::chrono::milliseconds(http_execute_timeout),
				std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(std::string_view url, std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, url, timeout,
				std::forward<Proxy>(proxy));
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<class Proxy, class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(std::string_view url, Proxy&& proxy)
		{
			return derived_t::execute(url, std::chrono::milliseconds(http_execute_timeout),
				std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			std::string_view target, std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			http::web_request req = http::make_request(host, port, target);
			if (get_last_error())
			{
				return http::response<Body, Fields>{ http::status::unknown, 11};
			}
			return derived_t::execute(ctx,
				std::forward<String>(host), std::forward<StrOrInt>(port),
				req.base(), timeout, std::forward<Proxy>(proxy));
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Proxy,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(
			const asio::ssl::context& ctx, String&& host, StrOrInt&& port,
			std::string_view target, Proxy&& proxy)
		{
			return derived_t::execute(ctx,
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, std::chrono::milliseconds(http_execute_timeout), std::forward<Proxy>(proxy));
		}

		// ----------------------------------------------------------------------------------------

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Proxy, class Rep, class Period,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, std::string_view target,
			std::chrono::duration<Rep, Period> timeout, Proxy&& proxy)
		{
			return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD },
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, timeout, std::forward<Proxy>(proxy));
		}

		/**
		 * @brief blocking execute the http request until it is returned on success or failure
		 */
		template<typename String, typename StrOrInt, class Proxy,
			class Body = http::string_body, class Fields = http::fields>
		typename std::enable_if_t<detail::is_character_string_v<detail::remove_cvref_t<String>>
			&& detail::http_proxy_checker_v<Proxy>, http::response<Body, Fields>>
		static inline execute(String&& host, StrOrInt&& port, std::string_view target, Proxy&& proxy)
		{
			return derived_t::execute(
				std::forward<String>(host), std::forward<StrOrInt>(port),
				target, std::chrono::milliseconds(http_execute_timeout), std::forward<Proxy>(proxy));
		}
	};

	template<class derived_t, class args_t = void>
	struct https_execute_impl : public https_execute_impl_bridge<derived_t, args_t> {};
}

#include <asio2/base/detail/pop_options.hpp>

#endif // !__ASIO2_HTTPS_EXECUTE_HPP__

#endif