Skip to content

Commit

Permalink
Improve reverseproxy (#520)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Feb 7, 2024
1 parent c8fe85d commit b4fa24a
Show file tree
Hide file tree
Showing 16 changed files with 8,032 additions and 32 deletions.
1 change: 1 addition & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ endif()

if (ENABLE_GZIP)
target_link_libraries(${project_name} ${ZLIB_LIBRARIES})
target_link_libraries(benchmark PRIVATE ${ZLIB_LIBRARIES})
endif()

if (ENABLE_SIMD STREQUAL "AARCH64")
Expand Down
1 change: 1 addition & 0 deletions include/cinatra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define CINATRA_CINATRA_HPP

#include "cinatra/coro_http_client.hpp"
#include "cinatra/coro_http_reverse_proxy.hpp"
#include "cinatra/coro_http_server.hpp"
#include "cinatra/smtp_client.hpp"

Expand Down
18 changes: 9 additions & 9 deletions include/cinatra/cinatra_log_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,45 @@ constexpr inline cinatra::null_logger_t NULL_LOGGER;
#ifdef CINATRA_LOG_ERROR
#else
#define CINATRA_LOG_ERROR \
cerr_logger_t {}
cinatra::cerr_logger_t {}
#endif

#ifdef CINATRA_LOG_WARNING
#else
#ifndef NDEBUG
#define CINATRA_LOG_WARNING \
cerr_logger_t {}
cinatra::cerr_logger_t {}
#else
#define CINATRA_LOG_WARNING NULL_LOGGER
#define CINATRA_LOG_WARNING cinatra::NULL_LOGGER
#endif
#endif

#ifdef CINATRA_LOG_INFO
#else
#ifndef NDEBUG
#define CINATRA_LOG_INFO \
cout_logger_t {}
cinatra::cout_logger_t {}
#else
#define CINATRA_LOG_INFO NULL_LOGGER
#define CINATRA_LOG_INFO cinatra::NULL_LOGGER
#endif
#endif

#ifdef CINATRA_LOG_DEBUG
#else
#ifndef NDEBUG
#define CINATRA_LOG_DEBUG \
cout_logger_t {}
cinatra::cout_logger_t {}
#else
#define CINATRA_LOG_DEBUG NULL_LOGGER
#define CINATRA_LOG_DEBUG cinatra::NULL_LOGGER
#endif
#endif

#ifdef CINATRA_LOG_TRACE
#else
#ifndef NDEBUG
#define CINATRA_LOG_TRACE \
cout_logger_t {}
cinatra::cout_logger_t {}
#else
#define CINATRA_LOG_TRACE NULL_LOGGER
#define CINATRA_LOG_TRACE cinatra::NULL_LOGGER
#endif
#endif
1 change: 0 additions & 1 deletion include/cinatra/coro_http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ class coro_http_request {
}

bool has_session() { return !cached_session_id_.empty(); }

void clear() { body_ = {}; }

std::unordered_map<std::string, std::string> params_;
Expand Down
66 changes: 49 additions & 17 deletions include/cinatra/coro_http_response.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,17 @@ class coro_http_response {
void set_status_and_content(
status_type status, std::string content = "",
content_encoding encoding = content_encoding::none) {
set_status_and_content_view(status, content, encoding, false);
}

void set_status_and_content_view(
status_type status, std::string_view content = "",
content_encoding encoding = content_encoding::none, bool is_view = true) {
status_ = status;
#ifdef CINATRA_ENABLE_GZIP
if (encoding == content_encoding::gzip) {
std::string encode_str;
bool r = gzip_codec::compress(
std::string_view(content.data(), content.length()), encode_str, true);
bool r = gzip_codec::compress(content, encode_str, true);
if (!r) {
set_status_and_content(status_type::internal_server_error,
"gzip compress error");
Expand All @@ -70,7 +75,12 @@ class coro_http_response {
else
#endif
{
content_ = std::move(content);
if (is_view) {
content_view_ = content;
}
else {
content_ = std::move(content);
}
}
has_set_content_ = true;
}
Expand Down Expand Up @@ -102,12 +112,10 @@ class coro_http_response {
buffers.push_back(asio::buffer(to_http_status_string(status_)));
build_resp_head(buffers);
if (!content_.empty()) {
if (fmt_type_ == format_type::chunked) {
to_chunked_buffers(buffers, content_, true);
}
else {
buffers.push_back(asio::buffer(content_));
}
handle_content(buffers, content_);
}
else if (!content_view_.empty()) {
handle_content(buffers, content_view_);
}
}

Expand All @@ -116,7 +124,7 @@ class coro_http_response {
bool has_len = false;
bool has_host = false;
for (auto &[k, v] : resp_headers_) {
if (k == "Host") {
if (k == "Server") {
has_host = true;
}
if (k == "Content-Length") {
Expand Down Expand Up @@ -180,12 +188,15 @@ class coro_http_response {
bool has_len = false;
bool has_host = false;
for (auto &[k, v] : resp_headers_) {
if (k == "Host") {
if (k == "Server") {
has_host = true;
}
if (k == "Content-Length") {
else if (k == "Content-Length") {
has_len = true;
}
else if (k == "Date") {
need_date_ = false;
}
}

if (!has_host) {
Expand All @@ -209,11 +220,12 @@ class coro_http_response {
}

if (!content_.empty()) {
auto [ptr, ec] = std::to_chars(buf_, buf_ + 32, content_.size());
buffers.emplace_back(asio::buffer(CONTENT_LENGTH_SV));
buffers.emplace_back(
asio::buffer(std::string_view(buf_, std::distance(buf_, ptr))));
buffers.emplace_back(asio::buffer(CRCF));
if (!has_len)
handle_content_len(buffers, content_);
}
else if (!content_view_.empty()) {
if (!has_len)
handle_content_len(buffers, content_view_);
}
else {
if (!has_len && boundary_.empty())
Expand Down Expand Up @@ -279,6 +291,25 @@ class coro_http_response {
}

private:
void handle_content(std::vector<asio::const_buffer> &buffers,
std::string_view content) {
if (fmt_type_ == format_type::chunked) {
to_chunked_buffers(buffers, content, true);
}
else {
buffers.push_back(asio::buffer(content));
}
}

void handle_content_len(std::vector<asio::const_buffer> &buffers,
std::string_view content) {
auto [ptr, ec] = std::to_chars(buf_, buf_ + 32, content.size());
buffers.emplace_back(asio::buffer(CONTENT_LENGTH_SV));
buffers.emplace_back(
asio::buffer(std::string_view(buf_, std::distance(buf_, ptr))));
buffers.emplace_back(asio::buffer(CRCF));
}

status_type status_;
format_type fmt_type_;
std::string content_;
Expand All @@ -293,5 +324,6 @@ class coro_http_response {
bool need_date_ = true;
std::unordered_map<std::string, cookie> cookies_;
std::string_view content_type_;
std::string_view content_view_;
};
} // namespace cinatra
108 changes: 108 additions & 0 deletions include/cinatra/coro_http_reverse_proxy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>

#include "cinatra/coro_http_client.hpp"
#include "cinatra/coro_http_server.hpp"
#include "ylt/coro_io/channel.hpp"

namespace cinatra {

class reverse_proxy {
public:
reverse_proxy(size_t thread_num, unsigned short port)
: server_(thread_num, port) {}

void add_dest_host(std::string url, int weight = 0) {
dest_hosts_.push_back(std::move(url));
weights_.push_back(weight);
}

template <http_method... method>
void start_reverse_proxy(
std::string url_path = "/", bool sync = true,
coro_io::load_blance_algorithm type =
coro_io::load_blance_algorithm::random,
std::vector<std::shared_ptr<base_aspect>> aspects = {}) {
if (dest_hosts_.empty()) {
throw std::invalid_argument("not config hosts yet!");
}

std::vector<std::string_view> hosts{dest_hosts_.begin(), dest_hosts_.end()};

channel_ = std::make_shared<coro_io::channel<coro_http_client>>(
coro_io::channel<coro_http_client>::create(hosts, {.lba = type},
weights_));

server_.set_http_handler<method...>(
url_path,
[this, type, url_path](
coro_http_request &req,
coro_http_response &response) -> async_simple::coro::Lazy<void> {
co_await channel_->send_request(
[this, &req, &response](
coro_http_client &client,
std::string_view host) -> async_simple::coro::Lazy<void> {
uri_t uri;
uri.parse_from(host.data());
co_await reply(client, uri.get_path(), req, response);
});
},
std::move(aspects));

start(sync);
}

private:
async_simple::coro::Lazy<void> reply(coro_http_client &client,
std::string url_path,
coro_http_request &req,
coro_http_response &response) {
auto req_headers = copy_request_headers(req.get_headers());
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);
}

void start(bool sync) {
if (sync) {
server_.sync_start();
}
else {
server_.async_start();
}
}

std::unordered_map<std::string, std::string> copy_request_headers(
std::span<http_header> req_headers) {
std::unordered_map<std::string, std::string> request_headers;
for (auto &[k, v] : req_headers) {
request_headers.emplace(k, v);
}

return request_headers;
}

coro_http_server server_;
std::shared_ptr<coro_io::channel<coro_http_client>> channel_;

// real dest hosts
std::vector<std::string> dest_hosts_;
std::vector<int> weights_;
};

} // namespace cinatra
16 changes: 12 additions & 4 deletions include/cinatra/define.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ using namespace std::string_view_literals;

namespace cinatra {
enum class http_method {
UNKNOW,
DEL,
NIL = 0,
GET,
HEAD,
POST,
PUT,
TRACE,
PATCH,
CONNECT,
OPTIONS,
TRACE
DEL,
};
constexpr inline auto GET = http_method::GET;
constexpr inline auto POST = http_method::POST;
Expand Down Expand Up @@ -52,10 +52,18 @@ inline constexpr std::string_view method_name(http_method mthd) {
case cinatra::http_method::TRACE:
return "TRACE"sv;
default:
return "UNKONWN"sv;
return "NIL"sv;
}
}

inline constexpr std::array<int, 20> method_table = {
3, 1, 9, 0, 0, 0, 4, 5, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 6, 7};

inline constexpr http_method method_type(std::string_view mthd) {
int index = ((mthd[0] & ~0x20) ^ ((mthd[1] + 1) & ~0x20)) % 20;
return (http_method)method_table[index];
}

enum class transfer_type { CHUNKED, ACCEPT_RANGES };

enum class content_type {
Expand Down
2 changes: 1 addition & 1 deletion include/cinatra/uri.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ struct context {
std::string path;
std::string query;
std::string body;
http_method method = http_method::UNKNOW;
http_method method = http_method::NIL;

context() = default;
context(const uri_t &u, http_method mthd)
Expand Down
Loading

0 comments on commit b4fa24a

Please sign in to comment.