Skip to content
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
16 changes: 9 additions & 7 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ jobs:
run: pip install clang-format
- name: check-diff
run: |
diff=`git-clang-format --diff HEAD^`
if ! [[ "$diff" = "no modified files to format" || "$diff" = "clang-format did not modify any files" ]]; then
echo "The diff you sent is not formatted correctly."
echo "The suggested format is"
echo "$diff"
exit 1
fi
diff=$(git-clang-format --diff HEAD^) || true
if [ -n "$diff" ] \
&& [ "$diff" != "no modified files to format" ] \
&& [ "$diff" != "clang-format did not modify any files" ]; then
echo "The diff you sent is not formatted correctly."
echo "The suggested format is:"
echo "$diff"
exit 1
fi
25 changes: 15 additions & 10 deletions include/ylt/easylog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

#include <ylt/util/b_stacktrace.h>

#include <memory>

#include "easylog/appender.hpp"

namespace easylog {
Expand Down Expand Up @@ -100,10 +102,10 @@ class logger {
bool flush_every_time,
std::chrono::milliseconds log_sample_interval = {},
std::chrono::milliseconds log_sample_duartion = {}) {
static appender appender(filename, async, enable_console, max_file_size,
max_files, flush_every_time);
appender_ =
std::make_unique<appender>(filename, async, enable_console,
max_file_size, max_files, flush_every_time);
async_ = async;
appender_ = &appender;
min_severity_ = min_severity;
enable_console_ = enable_console;
log_sample_interval_ = log_sample_interval;
Expand All @@ -128,7 +130,11 @@ class logger {
appenders_.emplace_back(std::move(fn));
}

void stop_async_log() { appender_->stop(); }
void stop_async_log() {
if (appender_) {
appender_->stop();
}
}

// set and get
void set_min_severity(Severity severity) { min_severity_ = severity; }
Expand Down Expand Up @@ -157,14 +163,13 @@ class logger {

private:
logger() {
static appender appender{};
appender.start_thread();
appender.enable_console(true);
appender_ = std::make_unique<appender>();
appender_->enable_console(true);
async_ = true;
appender_ = &appender;
}

logger(const logger &) = default;
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;

void append_record(record_t record) { appender_->write(std::move(record)); }

Expand All @@ -190,7 +195,7 @@ class logger {
std::atomic<std::chrono::milliseconds> log_sample_interval_;
std::atomic<std::chrono::milliseconds> log_sample_duration_;
std::chrono::system_clock::time_point init_time_{};
appender *appender_ = nullptr;
std::unique_ptr<appender> appender_ = nullptr;
std::vector<std::function<void(record_t &record)>> appenders_;
inline static std::atomic<bool> has_destruct_ = false;
};
Expand Down
25 changes: 13 additions & 12 deletions include/ylt/easylog/appender.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,6 @@ class appender {

buf[0] = '[';
auto [ptr, ec] = std::to_chars(buf + 1, buf + 21, tid);
buf[22] = ']';
buf[23] = ' ';
last_tid = tid;
last_len = ptr - buf;
buf[last_len++] = ']';
Expand Down Expand Up @@ -295,29 +293,29 @@ class appender {
std::lock_guard guard(mtx_);
if (file_.is_open()) {
file_.flush();
file_.sync_with_stdio();
}
}

void stop() {
std::lock_guard guard(mtx_);
if (!write_thd_.joinable()) {
return;
}

if (stop_) {
return;
{
std::lock_guard guard(que_mtx_);
if (!stop_) {
stop_ = true;
cnd_.notify_one();
}
}
stop_ = true;
cnd_.notify_one();
}

~appender() {
stop();
if (write_thd_.joinable())
if (write_thd_.joinable()) {
write_thd_.join();
}
}

~appender() { stop(); }

private:
void open_log_file() {
file_size_ = 0;
Expand Down Expand Up @@ -347,6 +345,9 @@ class appender {
file_size_ += BOM_STR.size();
}
}
else {
file_size_ = file_size;
}
}
}

Expand Down
22 changes: 19 additions & 3 deletions include/ylt/standalone/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,9 +674,13 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
timer_guard(coro_http_client *self,
std::chrono::steady_clock::duration duration, std::string msg)
: self(self), dur_(duration) {
if (duration.count() == 0) {
// Zero duration means immediate timeout.
self->socket_->is_timeout_ = true;
return;
}
self->socket_->is_timeout_ = false;

if (duration.count() >= 0) {
if (duration.count() > 0) {
self->timeout(self->timer_, duration, std::move(msg))
.start([](auto &&) {
});
Expand Down Expand Up @@ -1196,6 +1200,9 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
}

auto time_guard = timer_guard(this, req_timeout_duration_, "request timer");
if (socket_->is_timeout_) {
co_return resp_data{make_error_code(http_errc::request_timeout), 404};
}
std::tie(ec, size) = co_await async_write(asio::buffer(header_str));
if (ec) {
handle_upload_timeout_error(ec);
Expand Down Expand Up @@ -1393,6 +1400,10 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
CINATRA_LOG_DEBUG << req_head_str;
#endif
auto guard = timer_guard(this, req_timeout_duration_, "request timer");
if (socket_->is_timeout_) {
ec = make_error_code(http_errc::request_timeout);
break;
}
if (has_body) {
std::tie(ec, size) = co_await async_write(vec);
}
Expand Down Expand Up @@ -2249,7 +2260,9 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
if (socket_->has_closed_) {
auto time_out_guard =
timer_guard(this, conn_timeout_duration_, "connect timer");
socket_->is_timeout_ = false;
if (socket_->is_timeout_) {
co_return resp_data{make_error_code(http_errc::connect_timeout), 404};
}
host_ = proxy_host_.empty() ? u.get_host() : proxy_host_;
port_ = proxy_port_.empty() ? u.get_port() : proxy_port_;
if (eps->empty()) {
Expand Down Expand Up @@ -2278,6 +2291,9 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
<< ":" << std::to_string((*eps)[0].port());
std::error_code ec;
if (ec = co_await coro_io::async_connect(socket_->impl_, *eps); ec) {
if (socket_->is_timeout_) {
ec = make_error_code(http_errc::connect_timeout);
}
co_return resp_data{ec, 404};
}
#ifdef INJECT_FOR_HTTP_CLIENT_TEST
Expand Down
48 changes: 46 additions & 2 deletions include/ylt/standalone/cinatra/session_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class session_manager {

void start_check_session_timer() {
std::lock_guard lock(timer_mtx_);
if (stop_timer_ || !check_session_timer_) {
return;
}
std::weak_ptr<asio::steady_timer> timer = check_session_timer_;
check_session_timer_->expires_after(check_session_duration_);
check_session_timer_->async_wait([this, timer](auto ec) {
Expand All @@ -69,12 +72,26 @@ class session_manager {
return;
}

if (ec || stop_timer_) {
if (ec) {
return;
}

{
std::lock_guard lock(shutdown_mtx_);
if (stop_timer_) {
return;
}
++active_callbacks_;
}

remove_expire_session();
start_check_session_timer();

{
std::lock_guard lock(shutdown_mtx_);
--active_callbacks_;
}
shutdown_cv_.notify_all();
});
}

Expand All @@ -89,9 +106,32 @@ class session_manager {
start_check_session_timer();
}

void stop_timer() { stop_timer_ = true; }
void stop_timer() {
std::lock_guard lock(shutdown_mtx_);
stop_timer_ = true;
}

private:
~session_manager() {
{
std::lock_guard lock(timer_mtx_);
check_session_timer_->cancel();
check_session_timer_.reset();
}
{
std::lock_guard lock(shutdown_mtx_);
stop_timer_ = true;
}
{
std::unique_lock lock(shutdown_mtx_);
shutdown_cv_.wait(lock, [this] {
return active_callbacks_ == 0;
});
}
std::lock_guard lock(mtx_);
map_.clear();
}

session_manager()
: check_session_timer_(std::make_shared<asio::steady_timer>(
coro_io::get_global_executor()->get_asio_executor())) {
Expand All @@ -111,6 +151,10 @@ class session_manager {
std::shared_ptr<asio::steady_timer> check_session_timer_;
std::atomic<std::chrono::steady_clock::duration> check_session_duration_ = {
std::chrono::seconds(15)};

std::mutex shutdown_mtx_;
std::condition_variable shutdown_cv_;
int active_callbacks_ = 0;
};

} // namespace cinatra
83 changes: 83 additions & 0 deletions src/coro_http/tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2754,6 +2754,89 @@ TEST_CASE("test coro_http_client chunked upload and download") {
}
}

TEST_CASE("test zero duration timeout returns correct error codes") {
// 验证 set_conn_timeout(0ms) 和 set_req_timeout(0ms)
// 能确定性地返回正确错误码, 不依赖与异步操作的竞争结果。
coro_http_server server(1, 8091);
server.set_http_handler<cinatra::GET, cinatra::PUT, cinatra::POST>(
"/timeout_test", [](coro_http_request&, coro_http_response& resp) {
resp.set_status_and_content(status_type::ok, "ok");
});
server.async_start();

std::string uri = "http://127.0.0.1:8091/timeout_test";
create_file("timeout_test.txt", 1024);

// --- conn_timeout(0ms) 场景 ---

{
// async_upload_chunked:0ms 连接超时应返回 connect_timeout
coro_http_client client{};
client.set_conn_timeout(0ms);
auto result = async_simple::coro::syncAwait(client.async_upload_chunked(
uri, http_method::PUT, "timeout_test.txt"sv));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::connect_timeout);
}

{
// async_upload:0ms 连接超时应返回 connect_timeout
coro_http_client client{};
client.set_conn_timeout(0ms);
auto result = async_simple::coro::syncAwait(
client.async_upload(uri, http_method::PUT, "timeout_test.txt"sv));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::connect_timeout);
}

{
// async_request(GET):0ms 连接超时应返回 connect_timeout
coro_http_client client{};
client.set_conn_timeout(0ms);
auto result = async_simple::coro::syncAwait(client.async_get(uri));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::connect_timeout);
}

// --- req_timeout(0ms) 场景(连接正常,请求立即超时) ---

{
// async_upload_chunked:0ms 请求超时应返回 request_timeout
coro_http_client client{};
client.set_conn_timeout(500ms);
client.set_req_timeout(0ms);
client.add_header("filename", "timeout_test.txt");
auto result = async_simple::coro::syncAwait(client.async_upload_chunked(
uri, http_method::PUT, "timeout_test.txt"sv));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::request_timeout);
}

{
// async_upload:0ms 请求超时应返回 request_timeout
coro_http_client client{};
client.set_conn_timeout(500ms);
client.set_req_timeout(0ms);
auto result = async_simple::coro::syncAwait(
client.async_upload(uri, http_method::PUT, "timeout_test.txt"sv));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::request_timeout);
}

{
// async_request(GET):0ms 请求超时应返回 request_timeout
coro_http_client client{};
client.set_conn_timeout(500ms);
client.set_req_timeout(0ms);
auto result = async_simple::coro::syncAwait(client.async_get(uri));
CHECK(result.status != 200);
CHECK(result.net_err == http_errc::request_timeout);
}

std::error_code ignore_ec;
std::filesystem::remove("timeout_test.txt", ignore_ec);
}

TEST_CASE("test coro_http_client get") {
coro_http_client client{};
client.set_conn_timeout(1s);
Expand Down
Loading