Skip to content

Commit

Permalink
fix server (#545)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Apr 7, 2024
1 parent 2ced3a1 commit 51494a2
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 52 deletions.
2 changes: 2 additions & 0 deletions include/cinatra/coro_http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class coro_http_request {

const auto &get_queries() const { return parser_.queries(); }

std::string_view full_url() { return parser_.full_url(); }

void set_body(std::string &body) {
body_ = body;
auto type = get_content_type();
Expand Down
63 changes: 39 additions & 24 deletions include/cinatra/coro_http_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,20 @@ class coro_http_server {
#endif

// only call once, not thread safe.
std::errc sync_start() noexcept {
std::error_code sync_start() noexcept {
auto ret = async_start();
ret.wait();
return ret.value();
}

// only call once, not thread safe.
async_simple::Future<std::errc> async_start() {
async_simple::Future<std::error_code> async_start() {
errc_ = listen();

async_simple::Promise<std::errc> promise;
async_simple::Promise<std::error_code> promise;
auto future = promise.getFuture();

if (errc_ == std::errc{}) {
if (!errc_) {
if (out_ctx_ == nullptr) {
thd_ = std::thread([this] {
pool_->run();
Expand All @@ -91,7 +91,7 @@ class coro_http_server {

accept().start([p = std::move(promise), this](auto &&res) mutable {
if (res.hasError()) {
errc_ = std::errc::io_error;
errc_ = std::make_error_code(std::errc::io_error);
p.setValue(errc_);
}
else {
Expand Down Expand Up @@ -196,16 +196,14 @@ class coro_http_server {
coro_io::channel<coro_http_client>::create(hosts, {.lba = type},
weights));
auto handler =
[this, channel, type, url_path](
[this, channel, type](
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);
co_await reply(client, host, req, response);
});
};

Expand Down Expand Up @@ -503,10 +501,10 @@ class coro_http_server {
}

std::string_view address() { return address_; }
std::errc get_errc() { return errc_; }
std::error_code get_errc() { return errc_; }

private:
std::errc listen() {
std::error_code listen() {
CINATRA_LOG_INFO << "begin to listen";
using asio::ip::tcp;
asio::error_code ec;
Expand All @@ -519,25 +517,29 @@ class coro_http_server {
if (ec || it == it_end) {
CINATRA_LOG_ERROR << "bad address: " << address_
<< " error: " << ec.message();
return std::errc::bad_address;
if (ec) {
return ec;
}
return std::make_error_code(std::errc::address_not_available);
}

auto endpoint = it->endpoint();
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
CINATRA_LOG_ERROR << "acceptor open failed"
<< " error: " << ec.message();
return std::errc::io_error;
return ec;
}
#ifdef __GNUC__
acceptor_.set_option(tcp::acceptor::reuse_address(true), ec);
#endif
acceptor_.bind(endpoint, ec);
if (ec) {
CINATRA_LOG_ERROR << "bind port: " << port_ << " error: " << ec.message();
acceptor_.cancel(ec);
acceptor_.close(ec);
return std::errc::address_in_use;
std::error_code ignore_ec;
acceptor_.cancel(ignore_ec);
acceptor_.close(ignore_ec);
return ec;
}
#ifdef _MSC_VER
acceptor_.set_option(tcp::acceptor::reuse_address(true));
Expand All @@ -546,22 +548,22 @@ class coro_http_server {
if (ec) {
CINATRA_LOG_ERROR << "get local endpoint port: " << port_
<< " listen error: " << ec.message();
return std::errc::io_error;
return ec;
}

auto end_point = acceptor_.local_endpoint(ec);
if (ec) {
CINATRA_LOG_ERROR << "get local endpoint port: " << port_
<< " error: " << ec.message();
return std::errc::address_in_use;
return ec;
}
port_ = end_point.port();

CINATRA_LOG_INFO << "listen port " << port_ << " successfully";
return {};
}

async_simple::coro::Lazy<std::errc> accept() {
async_simple::coro::Lazy<std::error_code> accept() {
for (;;) {
coro_io::ExecutorWrapper<> *executor;
if (out_ctx_ == nullptr) {
Expand All @@ -580,7 +582,7 @@ class coro_http_server {
if (error == asio::error::operation_aborted ||
error == asio::error::bad_descriptor) {
acceptor_close_waiter_.set_value();
co_return std::errc::operation_canceled;
co_return error;
}
continue;
}
Expand Down Expand Up @@ -765,17 +767,28 @@ class coro_http_server {
}

async_simple::coro::Lazy<void> reply(coro_http_client &client,
std::string url_path,
std::string_view host,
coro_http_request &req,
coro_http_response &response) {
uri_t uri;
std::string proxy_host;

if (host.find("//") == std::string_view::npos) {
proxy_host.append("http://").append(host);
uri.parse_from(proxy_host.data());
}
else {
uri.parse_from(host.data());
}
std::unordered_map<std::string, std::string> req_headers;
for (auto &[k, v] : req_headers) {
for (auto &[k, v] : req.get_headers()) {
req_headers.emplace(k, v);
}
req_headers["Host"] = uri.host;

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),
req.full_url(), method_type(req.get_method()), std::move(ctx),
std::move(req_headers));

for (auto &[k, v] : result.resp_headers) {
Expand All @@ -789,6 +802,8 @@ class coro_http_server {
}

void init_address(std::string address) {
CINATRA_LOG_ERROR << "init log"; // init easylog singleton to make sure
// server destruct before easylog.
if (size_t pos = address.find(':'); pos != std::string::npos) {
auto port_sv = std::string_view(address).substr(pos + 1);

Expand All @@ -813,7 +828,7 @@ class coro_http_server {
std::unique_ptr<coro_io::ExecutorWrapper<>> out_executor_ = nullptr;
uint16_t port_;
std::string address_;
std::errc errc_ = {};
std::error_code errc_ = {};
asio::ip::tcp::acceptor acceptor_;
std::thread thd_;
std::promise<void> acceptor_close_waiter_;
Expand Down
4 changes: 4 additions & 0 deletions include/cinatra/http_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class http_parser {
}
}

full_url_ = url_;
if (has_query) {
size_t pos = url_.find('?');
parse_query(url_.substr(pos + 1, url_len - pos - 1));
Expand All @@ -125,6 +126,8 @@ class http_parser {

const auto &queries() const { return queries_; }

std::string_view full_url() { return full_url_; }

std::string_view get_query_value(std::string_view key) {
if (auto it = queries_.find(key); it != queries_.end()) {
return it->second;
Expand Down Expand Up @@ -270,6 +273,7 @@ class http_parser {
std::array<http_header, CINATRA_MAX_HTTP_HEADER_FIELD_SIZE> headers_;
std::string_view method_;
std::string_view url_;
std::string_view full_url_;
std::unordered_map<std::string_view, std::string_view> queries_;
};
} // namespace cinatra
18 changes: 9 additions & 9 deletions tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,56 +322,56 @@ TEST_CASE("test bad address") {
coro_http_server server(1, 9001, "127.0.0.1");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, 9001, "localhost");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, 9001, "0.0.0.0");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, 9001);
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, "0.0.0.0:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, "127.0.0.1:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}
{
coro_http_server server(1, "localhost:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
CHECK(!ec);
}

{
coro_http_server server(1, 9001, "x.x.x.x");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc::bad_address);
CHECK(ec);
}
{
coro_http_server server(1, "localhost:aaa");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc::bad_address);
CHECK(ec);
}
}

Expand Down
38 changes: 19 additions & 19 deletions tests/test_coro_http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,14 +391,14 @@ TEST_CASE("test server start and stop") {
auto future2 = server2.async_start();
future2.wait();
auto ec = future2.value();
CHECK(ec == std::errc::address_in_use);
CHECK(ec == asio::error::address_in_use);
}

TEST_CASE("test server sync_start and stop") {
cinatra::coro_http_server server(1, 0);

std::promise<void> promise;
std::errc ec;
std::error_code ec;
std::thread thd([&] {
promise.set_value();
ec = server.sync_start();
Expand All @@ -408,7 +408,7 @@ TEST_CASE("test server sync_start and stop") {
server.stop();
thd.join();
CHECK(server.port() > 0);
CHECK(ec == std::errc::operation_canceled);
CHECK(ec == asio::error::operation_aborted);
}

TEST_CASE("get post") {
Expand Down Expand Up @@ -1532,21 +1532,21 @@ TEST_CASE("test reverse proxy") {

coro_http_server proxy_wrr(2, 8090);
proxy_wrr.set_http_proxy_handler<GET, POST>(
"/wrr", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"},
"/", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"},
coro_io::load_blance_algorithm::WRR, {10, 5, 5}, log_t{}, check_t{});

coro_http_server proxy_rr(2, 8091);
proxy_rr.set_http_proxy_handler<GET, POST>(
"/rr", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"},
"/", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"},
coro_io::load_blance_algorithm::RR, {}, log_t{});

coro_http_server proxy_random(2, 8092);
proxy_random.set_http_proxy_handler<GET, POST>(
"/random", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"});
"/", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"});

coro_http_server proxy_all(2, 8093);
proxy_all.set_http_proxy_handler(
"/all", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"});
"/", {"127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"});

proxy_wrr.async_start();
proxy_rr.async_start();
Expand All @@ -1556,37 +1556,37 @@ TEST_CASE("test reverse proxy") {
std::this_thread::sleep_for(200ms);

coro_http_client client_rr;
resp_data resp_rr = client_rr.get("http://127.0.0.1:8091/rr");
resp_data resp_rr = client_rr.get("http://127.0.0.1:8091/");
CHECK(resp_rr.resp_body == "web1");
resp_rr = client_rr.get("http://127.0.0.1:8091/rr");
resp_rr = client_rr.get("http://127.0.0.1:8091/");
CHECK(resp_rr.resp_body == "web2");
resp_rr = client_rr.get("http://127.0.0.1:8091/rr");
resp_rr = client_rr.get("http://127.0.0.1:8091/");
CHECK(resp_rr.resp_body == "web3");
resp_rr = client_rr.get("http://127.0.0.1:8091/rr");
resp_rr = client_rr.get("http://127.0.0.1:8091/");
CHECK(resp_rr.resp_body == "web1");
resp_rr = client_rr.get("http://127.0.0.1:8091/rr");
resp_rr = client_rr.get("http://127.0.0.1:8091/");
CHECK(resp_rr.resp_body == "web2");
resp_rr = client_rr.post("http://127.0.0.1:8091/rr", "test content",
resp_rr = client_rr.post("http://127.0.0.1:8091/", "test content",
req_content_type::text);
CHECK(resp_rr.resp_body == "web3");

coro_http_client client_wrr;
resp_data resp = client_wrr.get("http://127.0.0.1:8090/wrr");
resp_data resp = client_wrr.get("http://127.0.0.1:8090/");
CHECK(resp.resp_body == "web1");
resp = client_wrr.get("http://127.0.0.1:8090/wrr");
resp = client_wrr.get("http://127.0.0.1:8090/");
CHECK(resp.resp_body == "web1");
resp = client_wrr.get("http://127.0.0.1:8090/wrr");
resp = client_wrr.get("http://127.0.0.1:8090/");
CHECK(resp.resp_body == "web2");
resp = client_wrr.get("http://127.0.0.1:8090/wrr");
resp = client_wrr.get("http://127.0.0.1:8090/");
CHECK(resp.resp_body == "web3");

coro_http_client client_random;
resp_data resp_random = client_random.get("http://127.0.0.1:8092/random");
resp_data resp_random = client_random.get("http://127.0.0.1:8092/");
std::cout << resp_random.resp_body << "\n";
CHECK(!resp_random.resp_body.empty());

coro_http_client client_all;
resp_random = client_all.post("http://127.0.0.1:8093/all", "test content",
resp_random = client_all.post("http://127.0.0.1:8093/", "test content",
req_content_type::text);
std::cout << resp_random.resp_body << "\n";
CHECK(!resp_random.resp_body.empty());
Expand Down

0 comments on commit 51494a2

Please sign in to comment.