/* * 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 #include #include #include #include #include #include namespace asio2::detail { template::value()> struct https_execute_impl_bridge; template struct https_execute_impl_bridge { }; template struct https_execute_impl_bridge { protected: template static void _execute_with_socks5( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, asio::ssl::stream& stream, http::parser& parser, Buffer& buffer, String&& host, StrOrInt&& port, http::request& req, Proxy&& proxy) { auto sk5 = detail::to_shared_ptr(std::forward(proxy)); std::string_view h{ sk5->host() }; std::string_view p{ sk5->port() }; if (static_cast(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(host)), detail::to_string(std::forward(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 static void _execute_trivially( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, asio::ssl::stream& stream, http::parser& parser, Buffer& buffer, String&& host, StrOrInt&& port, http::request& req) { detail::ignore_unused(ioc); // Look up the domain name resolver.async_resolve(std::forward(host), detail::to_string(std::forward(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 static void _execute_impl( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, asio::ssl::stream& stream, http::parser& parser, Buffer& buffer, String&& host, StrOrInt&& port, http::request& req, Proxy&& proxy) { // if has socks5 proxy if constexpr (std::is_base_of_v>::type>) { derived_t::_execute_with_socks5(ioc, resolver, socket, stream, parser, buffer , std::forward(host), std::forward(port) , req , std::forward(proxy) ); } else { detail::ignore_unused(proxy); derived_t::_execute_trivially(ioc, resolver, socket, stream, parser, buffer , std::forward(host), std::forward(port) , req ); } } public: template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute(const asio::ssl::context& ctx, String&& host, StrOrInt&& port, http::request& req, std::chrono::duration timeout, Proxy&& proxy) { http::parser 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 stream(socket, const_cast(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(host), std::forward(port) , req , std::forward(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 std::enable_if_t> , http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, http::request& req, std::chrono::duration timeout) { return derived_t::execute(ctx, std::forward(host), std::forward(port), req, timeout, std::in_place); } template typename std::enable_if_t> , http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, http::request& req) { return derived_t::execute(ctx, std::forward(host), std::forward(port), req, std::chrono::milliseconds(http_execute_timeout), std::in_place); } // ---------------------------------------------------------------------------------------- template typename std::enable_if_t> , http::response> static inline execute(String&& host, StrOrInt&& port, http::request& req, std::chrono::duration timeout) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, std::forward(host), std::forward(port), req, timeout, std::in_place); } template typename std::enable_if_t> , http::response> static inline execute(String&& host, StrOrInt&& port, http::request& req) { return derived_t::execute( std::forward(host), std::forward(port), req, std::chrono::milliseconds(http_execute_timeout)); } // ---------------------------------------------------------------------------------------- template static inline http::response execute( const asio::ssl::context& ctx, http::web_request& req, std::chrono::duration timeout) { return derived_t::execute(ctx, req.url().host(), req.url().port(), req.base(), timeout, std::in_place); } template static inline http::response execute( const asio::ssl::context& ctx, http::web_request& req) { return derived_t::execute(ctx, req, std::chrono::milliseconds(http_execute_timeout)); } template static inline http::response execute( http::web_request& req, std::chrono::duration timeout) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, req, timeout); } template static inline http::response 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 static inline http::response execute(const asio::ssl::context& ctx, std::string_view url, std::chrono::duration timeout) { http::web_request req = http::make_request(url); if (get_last_error()) { return http::response{ 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 static inline http::response 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 static inline http::response execute(std::string_view url, std::chrono::duration 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 static inline http::response 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 std::enable_if_t> , http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, std::string_view target, std::chrono::duration timeout) { http::web_request req = http::make_request(host, port, target); if (get_last_error()) { return http::response{ http::status::unknown, 11}; } return derived_t::execute(ctx, std::forward(host), std::forward(port), req.base(), timeout, std::in_place); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t> , http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, std::string_view target) { return derived_t::execute(ctx, std::forward(host), std::forward(port), target, std::chrono::milliseconds(http_execute_timeout)); } // ---------------------------------------------------------------------------------------- /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t>, http::response> static inline execute(String&& host, StrOrInt&& port, std::string_view target, std::chrono::duration timeout) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, std::forward(host), std::forward(port), target, timeout); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t>, http::response> static inline execute(String&& host, StrOrInt&& port, std::string_view target) { return derived_t::execute( std::forward(host), std::forward(port), target, std::chrono::milliseconds(http_execute_timeout)); } // ---------------------------------------------------------------------------------------- template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, http::request& req, Proxy&& proxy) { return derived_t::execute(ctx, std::forward(host), std::forward(port), req, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } // ---------------------------------------------------------------------------------------- template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute(String&& host, StrOrInt&& port, http::request& req, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, std::forward(host), std::forward(port), req, timeout, std::forward(proxy)); } template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute(String&& host, StrOrInt&& port, http::request& req, Proxy&& proxy) { return derived_t::execute( std::forward(host), std::forward(port), req, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } // ---------------------------------------------------------------------------------------- template typename std::enable_if_t, http::response> static inline execute( const asio::ssl::context& ctx, http::web_request& req, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(ctx, req.url().host(), req.url().port(), req.base(), timeout, std::forward(proxy)); } template typename std::enable_if_t, http::response> 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)); } template typename std::enable_if_t, http::response> static inline execute( http::web_request& req, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, req, timeout, std::forward(proxy)); } template typename std::enable_if_t, http::response> static inline execute(http::web_request& req, Proxy&& proxy) { return derived_t::execute(req, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } // ---------------------------------------------------------------------------------------- /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t, http::response> static inline execute( const asio::ssl::context& ctx, std::string_view url, std::chrono::duration timeout, Proxy&& proxy) { http::web_request req = http::make_request(url); if (get_last_error()) { return http::response{ http::status::unknown, 11}; } return derived_t::execute(ctx, req.host(), req.port(), req.base(), timeout, std::forward(proxy)); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t, http::response> 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)); } // ---------------------------------------------------------------------------------------- /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t, http::response> static inline execute(std::string_view url, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, url, timeout, std::forward(proxy)); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t, http::response> static inline execute(std::string_view url, Proxy&& proxy) { return derived_t::execute(url, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } // ---------------------------------------------------------------------------------------- /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute( const asio::ssl::context& ctx, String&& host, StrOrInt&& port, std::string_view target, std::chrono::duration timeout, Proxy&& proxy) { http::web_request req = http::make_request(host, port, target); if (get_last_error()) { return http::response{ http::status::unknown, 11}; } return derived_t::execute(ctx, std::forward(host), std::forward(port), req.base(), timeout, std::forward(proxy)); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> 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(host), std::forward(port), target, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } // ---------------------------------------------------------------------------------------- /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute(String&& host, StrOrInt&& port, std::string_view target, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(asio::ssl::context{ ASIO2_DEFAULT_SSL_METHOD }, std::forward(host), std::forward(port), target, timeout, std::forward(proxy)); } /** * @brief blocking execute the http request until it is returned on success or failure */ template typename std::enable_if_t> && detail::http_proxy_checker_v, http::response> static inline execute(String&& host, StrOrInt&& port, std::string_view target, Proxy&& proxy) { return derived_t::execute( std::forward(host), std::forward(port), target, std::chrono::milliseconds(http_execute_timeout), std::forward(proxy)); } }; template struct https_execute_impl : public https_execute_impl_bridge {}; } #include #endif // !__ASIO2_HTTPS_EXECUTE_HPP__ #endif