/* * 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) */ #ifndef __ASIO2_HTTP_EXECUTE_HPP__ #define __ASIO2_HTTP_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 http_execute_impl_bridge; template struct http_execute_impl_bridge { }; template struct http_execute_impl_bridge { protected: template static void _execute_with_socks5( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, 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; } http::async_write(socket, req, [&](const error_code & ec3, std::size_t) mutable { if (ec3) { set_last_error(ec3); return; } // Then start asynchronous reading http::async_read(socket, buffer, parser, [&](const error_code& ec4, 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(ec4); }); }); } ); }); }); } template static void _execute_trivially( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, 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; } http::async_write(socket, req, [&](const error_code & ec3, std::size_t) mutable { if (ec3) { set_last_error(ec3); return; } // Then start asynchronous reading http::async_read(socket, buffer, parser, [&](const error_code& ec4, 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(ec4); }); }); }); }); } template static void _execute_impl( asio::io_context& ioc, asio::ip::tcp::resolver& resolver, asio::ip::tcp::socket& socket, 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, parser, buffer , std::forward(host), std::forward(port) , req , std::forward(proxy) ); } else { detail::ignore_unused(proxy); derived_t::_execute_trivially(ioc, resolver, socket, 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(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 }; // 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 != "80") { 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, 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(String&& host, StrOrInt&& port, http::request& req, std::chrono::duration timeout) { return derived_t::execute( 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( http::web_request& req, std::chrono::duration timeout) { return derived_t::execute(req.url().host(), req.url().port(), req.base(), timeout, std::in_place); } 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(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( 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(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(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( 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(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(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(http::web_request& req, std::chrono::duration timeout, Proxy&& proxy) { return derived_t::execute(req.url().host(), req.url().port(), req.base(), 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(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(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(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(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( 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(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 http_execute_impl : public http_execute_impl_bridge {}; } #include #endif // !__ASIO2_HTTP_EXECUTE_HPP__