diff --git a/include/cinatra/coro_http_connection.hpp b/include/cinatra/coro_http_connection.hpp index 92aeaeb2..43a2e42d 100644 --- a/include/cinatra/coro_http_connection.hpp +++ b/include/cinatra/coro_http_connection.hpp @@ -261,6 +261,17 @@ class coro_http_connection } } } + + if (router_.is_http_proxy_) { + if (router_.coro_http_proxy_func_) { + co_await (router_.coro_http_proxy_func_)(request_, response_); + } + else { + (router_.http_proxy_func_)(request_, response_); + } + is_matched_regex_router = true; + } + // not found if (!is_matched_regex_router) response_.set_status(status_type::not_found); diff --git a/include/cinatra/coro_http_router.hpp b/include/cinatra/coro_http_router.hpp index a620ab2c..4a8c3844 100644 --- a/include/cinatra/coro_http_router.hpp +++ b/include/cinatra/coro_http_router.hpp @@ -249,6 +249,15 @@ class coro_http_router { const auto& get_regex_handlers() { return regex_handles_; } + bool is_http_proxy_ = false; + + std::function + http_proxy_func_; + + std::function(coro_http_request& req, + coro_http_response& resp)> + coro_http_proxy_func_; + private: std::set keys_; std::unordered_map< diff --git a/include/cinatra/coro_http_server.hpp b/include/cinatra/coro_http_server.hpp index 01d57e62..7a696881 100644 --- a/include/cinatra/coro_http_server.hpp +++ b/include/cinatra/coro_http_server.hpp @@ -130,15 +130,26 @@ class coro_http_server { template void set_http_handler(std::string key, Func handler, Aspects &&...asps) { static_assert(sizeof...(method) >= 1, "must set http_method"); - if constexpr (sizeof...(method) == 1) { - (router_.set_http_handler(std::move(key), std::move(handler), - std::forward(asps)...), - ...); + if (!router_.is_http_proxy_) { + if constexpr (sizeof...(method) == 1) { + (router_.set_http_handler(std::move(key), std::move(handler), + std::forward(asps)...), + ...); + } + else { + (router_.set_http_handler(key, handler, + std::forward(asps)...), + ...); + } } else { - (router_.set_http_handler(key, handler, - std::forward(asps)...), - ...); + using return_type = typename util::function_traits::return_type; + if constexpr (is_lazy_v) { + router_.coro_http_proxy_func_ = handler; + } + else { + router_.http_proxy_func_ = handler; + } } } @@ -206,6 +217,22 @@ class coro_http_server { } } + template + void start_http_proxy(Aspects &&...aspects) { + set_http_proxy_router(); + set_http_handler( + "/", + [this](coro_http_request &req, + coro_http_response &response) -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await proxy_reply(client, std::string(req.get_url()), req, + response); + }, + std::forward(aspects)...); + } + + void set_http_proxy_router() { router_.is_http_proxy_ = true; } + void set_max_size_of_cache_files(size_t max_size = 3 * 1024 * 1024) { std::error_code ec; for (const auto &file : @@ -747,6 +774,31 @@ class coro_http_server { response.set_delay(true); } + async_simple::coro::Lazy proxy_reply(coro_http_client &client, + std::string url_path, + coro_http_request &req, + coro_http_response &response) { + std::unordered_map req_headers; + for (auto &[k, v] : req_headers) { + if (g_hop_headers.find(std::string(k)) == g_hop_headers.end()) + req_headers.emplace(k, v); + } + + auto ctx = req_context{.content = req.get_body()}; + auto result = co_await client.async_request( + std::move(url_path), method_type(req.get_method()), std::move(ctx), + std::move(req_headers)); + + for (auto &[k, v] : result.resp_headers) { + response.add_header(std::string(k), std::string(v)); + } + + response.set_status_and_content_view( + static_cast(result.status), result.resp_body); + co_await response.get_conn()->reply(); + response.set_delay(true); + } + private: std::unique_ptr pool_; asio::io_context *out_ctx_ = nullptr; diff --git a/include/cinatra/define.h b/include/cinatra/define.h index d663c406..71b37d46 100644 --- a/include/cinatra/define.h +++ b/include/cinatra/define.h @@ -278,6 +278,16 @@ inline std::unordered_map g_content_type_map = { struct NonSSL {}; struct SSL {}; +static std::unordered_map g_hop_headers = { + {"Connection", true}, + {"Keep-Alive", true}, + {"Proxy-Authenticate", true}, + {"Proxy-Authorization", true}, + {"Te", true}, + {"Trailers", true}, + {"Transfer-Encoding", true}, + {"Upgrade", true}}; + enum class time_format { http_format, utc_format, diff --git a/tests/test_cinatra.cpp b/tests/test_cinatra.cpp index 998d5f3a..18909f5c 100644 --- a/tests/test_cinatra.cpp +++ b/tests/test_cinatra.cpp @@ -992,30 +992,33 @@ TEST_CASE("test inject failed") { #endif TEST_CASE("test coro http proxy request") { + coro_http_server http_proxy(10, 7890); + http_proxy.start_http_proxy(); + http_proxy.async_start(); + std::this_thread::sleep_for(200ms); + coro_http_client client{}; client.set_req_timeout(8s); std::string uri = "http://www.baidu.com"; // Make sure the host and port are matching with your proxy server - client.set_proxy("106.14.255.124", "80"); + client.set_proxy("127.0.0.1", "7890"); resp_data result = async_simple::coro::syncAwait(client.async_get(uri)); - if (!result.net_err) - CHECK(result.status >= 200); - - client.set_proxy("106.14.255.124", "80"); - result = async_simple::coro::syncAwait(client.async_get(uri)); - if (!result.net_err) - CHECK(result.status >= 200); + CHECK(result.status >= 200); } TEST_CASE("test coro http proxy request with port") { + coro_http_server http_proxy(10, 7890); + http_proxy.start_http_proxy(); + http_proxy.async_start(); + std::this_thread::sleep_for(200ms); + coro_http_client client{}; client.set_req_timeout(8s); std::string uri = "http://www.baidu.com:80"; // Make sure the host and port are matching with your proxy server - client.set_proxy("106.14.255.124", "80"); + client.set_proxy("127.0.0.1", "7890"); resp_data result = async_simple::coro::syncAwait(client.async_get(uri)); - if (!result.net_err) - CHECK(result.status >= 200); // maybe return 500 from that host. + CHECK(result.status >= 200); } // TEST_CASE("test coro http basic auth request") {