Skip to content

Commit

Permalink
feat: implement of http proxy api
Browse files Browse the repository at this point in the history
  • Loading branch information
helintongh committed Feb 20, 2024
1 parent 82bff43 commit e850698
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 18 deletions.
11 changes: 11 additions & 0 deletions include/cinatra/coro_http_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions include/cinatra/coro_http_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,15 @@ class coro_http_router {

const auto& get_regex_handlers() { return regex_handles_; }

bool is_http_proxy_ = false;

std::function<void(coro_http_request& req, coro_http_response& resp)>
http_proxy_func_;

std::function<async_simple::coro::Lazy<void>(coro_http_request& req,
coro_http_response& resp)>
coro_http_proxy_func_;

private:
std::set<std::string> keys_;
std::unordered_map<
Expand Down
66 changes: 59 additions & 7 deletions include/cinatra/coro_http_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,26 @@ class coro_http_server {
template <http_method... method, typename Func, typename... Aspects>
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<method>(std::move(key), std::move(handler),
std::forward<Aspects>(asps)...),
...);
if (!router_.is_http_proxy_) {
if constexpr (sizeof...(method) == 1) {
(router_.set_http_handler<method>(std::move(key), std::move(handler),
std::forward<Aspects>(asps)...),
...);
}
else {
(router_.set_http_handler<method>(key, handler,
std::forward<Aspects>(asps)...),
...);
}
}
else {
(router_.set_http_handler<method>(key, handler,
std::forward<Aspects>(asps)...),
...);
using return_type = typename util::function_traits<Func>::return_type;
if constexpr (is_lazy_v<return_type>) {
router_.coro_http_proxy_func_ = handler;
}
else {
router_.http_proxy_func_ = handler;
}
}
}

Expand Down Expand Up @@ -206,6 +217,22 @@ class coro_http_server {
}
}

template <http_method... method, typename... Aspects>
void start_http_proxy(Aspects &&...aspects) {
set_http_proxy_router();
set_http_handler<method...>(
"/",
[this](coro_http_request &req,
coro_http_response &response) -> async_simple::coro::Lazy<void> {
coro_http_client client{};
co_await proxy_reply(client, std::string(req.get_url()), req,
response);
},
std::forward<Aspects>(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 :
Expand Down Expand Up @@ -747,6 +774,31 @@ class coro_http_server {
response.set_delay(true);
}

async_simple::coro::Lazy<void> proxy_reply(coro_http_client &client,
std::string url_path,
coro_http_request &req,
coro_http_response &response) {
std::unordered_map<std::string, std::string> 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<std::string_view>{.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<status_type>(result.status), result.resp_body);
co_await response.get_conn()->reply();
response.set_delay(true);
}

private:
std::unique_ptr<coro_io::io_context_pool> pool_;
asio::io_context *out_ctx_ = nullptr;
Expand Down
10 changes: 10 additions & 0 deletions include/cinatra/define.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ inline std::unordered_map<std::string, std::string> g_content_type_map = {
struct NonSSL {};
struct SSL {};

static std::unordered_map<std::string, bool> 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,
Expand Down
25 changes: 14 additions & 11 deletions tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<GET, POST>();
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<GET, POST>();
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") {
Expand Down

0 comments on commit e850698

Please sign in to comment.