Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

From address #540

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions cmake/develop.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ int main()
endmacro()

# Enable address sanitizer
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" OFF)
else()
option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" ON)
endif()
option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" ON)
if(ENABLE_SANITIZER AND NOT MSVC)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
check_asan(HAS_ASAN)
Expand Down
3 changes: 2 additions & 1 deletion example/benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ using namespace cinatra;
using namespace std::chrono_literals;

int main() {
coro_http_server server(std::thread::hardware_concurrency(), 8090, true);
coro_http_server server(std::thread::hardware_concurrency(), 8090,
"127.0.0.1", true);
server.set_http_handler<GET>(
"/plaintext", [](coro_http_request& req, coro_http_response& resp) {
resp.get_conn()->set_multi_buf(false);
Expand Down
95 changes: 81 additions & 14 deletions include/cinatra/coro_http_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,37 @@ enum class file_resp_format_type {
};
class coro_http_server {
public:
coro_http_server(asio::io_context &ctx, unsigned short port)
: out_ctx_(&ctx), port_(port), acceptor_(ctx), check_timer_(ctx) {}
coro_http_server(asio::io_context &ctx, unsigned short port,
std::string address = "0.0.0.0")
: out_ctx_(&ctx), port_(port), acceptor_(ctx), check_timer_(ctx) {
init_address(std::move(address));
}

coro_http_server(asio::io_context &ctx,
std::string address /* = "0.0.0.0:9001" */)
: out_ctx_(&ctx), acceptor_(ctx), check_timer_(ctx) {
init_address(std::move(address));
}

coro_http_server(size_t thread_num, unsigned short port,
bool cpu_affinity = false)
std::string address = "0.0.0.0", bool cpu_affinity = false)
: pool_(std::make_unique<coro_io::io_context_pool>(thread_num,
cpu_affinity)),
port_(port),
acceptor_(pool_->get_executor()->get_asio_executor()),
check_timer_(pool_->get_executor()->get_asio_executor()) {}
check_timer_(pool_->get_executor()->get_asio_executor()) {
init_address(std::move(address));
}

coro_http_server(size_t thread_num,
std::string address /* = "0.0.0.0:9001" */,
bool cpu_affinity = false)
: pool_(std::make_unique<coro_io::io_context_pool>(thread_num,
cpu_affinity)),
acceptor_(pool_->get_executor()->get_asio_executor()),
check_timer_(pool_->get_executor()->get_asio_executor()) {
init_address(std::move(address));
}

~coro_http_server() {
CINATRA_LOG_INFO << "coro_http_server will quit";
Expand Down Expand Up @@ -64,29 +85,30 @@ class coro_http_server {

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

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

if (ec == std::errc{}) {
if (errc_ == std::errc{}) {
if (out_ctx_ == nullptr) {
thd_ = std::thread([this] {
pool_->run();
});
}

accept().start([p = std::move(promise)](auto &&res) mutable {
accept().start([p = std::move(promise), this](auto &&res) mutable {
if (res.hasError()) {
p.setValue(std::errc::io_error);
errc_ = std::errc::io_error;
p.setValue(errc_);
}
else {
p.setValue(res.value());
}
});
}
else {
promise.setValue(ec);
promise.setValue(errc_);
}

return future;
Expand Down Expand Up @@ -488,16 +510,31 @@ class coro_http_server {
return connections_.size();
}

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

private:
std::errc listen() {
CINATRA_LOG_INFO << "begin to listen";
using asio::ip::tcp;
auto endpoint = tcp::endpoint(tcp::v4(), port_);
acceptor_.open(endpoint.protocol());
asio::error_code ec;
auto addr = asio::ip::address::from_string(address_, ec);
if (ec) {
CINATRA_LOG_ERROR << "bad address: " << address_
<< " error: " << ec.message();
return std::errc::bad_address;
}

auto endpoint = tcp::endpoint(addr, port_);
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
CINATRA_LOG_ERROR << "acceptor open failed"
<< " error: " << ec.message();
return std::errc::io_error;
}
#ifdef __GNUC__
acceptor_.set_option(tcp::acceptor::reuse_address(true));
acceptor_.set_option(tcp::acceptor::reuse_address(true), ec);
#endif
asio::error_code ec;
acceptor_.bind(endpoint, ec);
if (ec) {
CINATRA_LOG_ERROR << "bind port: " << port_ << " error: " << ec.message();
Expand All @@ -508,7 +545,12 @@ class coro_http_server {
#ifdef _MSC_VER
acceptor_.set_option(tcp::acceptor::reuse_address(true));
#endif
acceptor_.listen();
acceptor_.listen(asio::socket_base::max_listen_connections, ec);
if (ec) {
CINATRA_LOG_ERROR << "get local endpoint port: " << port_
<< " listen error: " << ec.message();
return std::errc::io_error;
}

auto end_point = acceptor_.local_endpoint(ec);
if (ec) {
Expand Down Expand Up @@ -749,11 +791,36 @@ class coro_http_server {
response.set_delay(true);
}

void init_address(std::string address) {
if (size_t pos = address.find(':'); pos != std::string::npos) {
auto port_sv = std::string_view(address).substr(pos + 1);

uint16_t port;
auto [ptr, ec] = std::from_chars(
port_sv.data(), port_sv.data() + port_sv.size(), port, 10);
if (ec != std::errc{}) {
address_ = std::move(address);
return;
}

port_ = port;
address = address.substr(0, pos);
}

if (iequal0(address, "localhost")) {
address = "127.0.0.1";
}

address_ = std::move(address);
}

private:
std::unique_ptr<coro_io::io_context_pool> pool_;
asio::io_context *out_ctx_ = nullptr;
std::unique_ptr<coro_io::ExecutorWrapper<>> out_executor_ = nullptr;
uint16_t port_;
std::string address_;
std::errc errc_ = {};
asio::ip::tcp::acceptor acceptor_;
std::thread thd_;
std::promise<void> acceptor_close_waiter_;
Expand Down
58 changes: 58 additions & 0 deletions tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,64 @@ TEST_CASE("test select coro channel") {
CHECK(val == 42);
}

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{});
}
{
coro_http_server server(1, 9001, "localhost");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}
{
coro_http_server server(1, 9001, "0.0.0.0");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}
{
coro_http_server server(1, 9001);
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}
{
coro_http_server server(1, "0.0.0.0:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}
{
coro_http_server server(1, "127.0.0.1:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}
{
coro_http_server server(1, "localhost:9001");
server.async_start();
auto ec = server.get_errc();
CHECK(ec == std::errc{});
}

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

async_simple::coro::Lazy<void> test_collect_all() {
asio::io_context ioc;
std::thread thd([&] {
Expand Down
Loading