mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-12 19:58:29 +00:00
Enhance WebSocket support with unresponsive-peer detection and documentation updates
- Added `set_websocket_max_missed_pongs` method to configure unresponsive-peer detection. - Updated README and documentation to clarify WebSocket limitations and features. - Introduced tests for detecting non-responsive peers and ensuring responsive peers do not trigger timeouts.
This commit is contained in:
@@ -136,6 +136,86 @@ TEST_F(WebSocketServerPingIntervalTest, ServerRuntimeInterval) {
|
||||
client.close();
|
||||
}
|
||||
|
||||
// Verify that the client detects a non-responsive peer via unacked-ping count.
|
||||
// Setup: the server's heartbeat is disabled AND its handler never calls
|
||||
// read(), so no automatic Pong reply is ever produced. The client sends
|
||||
// pings but receives no pongs, and should close itself once the unacked
|
||||
// ping count reaches max_missed_pongs.
|
||||
class WebSocketPongTimeoutTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
svr_.set_websocket_ping_interval(0);
|
||||
svr_.WebSocket("/ws", [this](const Request &, ws::WebSocket &) {
|
||||
std::unique_lock<std::mutex> lock(handler_mutex_);
|
||||
handler_cv_.wait(lock, [this]() { return release_; });
|
||||
});
|
||||
|
||||
port_ = svr_.bind_to_any_port("localhost");
|
||||
thread_ = std::thread([this]() { svr_.listen_after_bind(); });
|
||||
svr_.wait_until_ready();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(handler_mutex_);
|
||||
release_ = true;
|
||||
}
|
||||
handler_cv_.notify_all();
|
||||
svr_.stop();
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
Server svr_;
|
||||
int port_;
|
||||
std::thread thread_;
|
||||
std::mutex handler_mutex_;
|
||||
std::condition_variable handler_cv_;
|
||||
bool release_ = false;
|
||||
};
|
||||
|
||||
TEST_F(WebSocketPongTimeoutTest, ClientDetectsNonResponsivePeer) {
|
||||
ws::WebSocketClient client("ws://localhost:" + std::to_string(port_) + "/ws");
|
||||
client.set_websocket_max_missed_pongs(2);
|
||||
ASSERT_TRUE(client.connect());
|
||||
ASSERT_TRUE(client.is_open());
|
||||
|
||||
// Client pings every 1s (compile-time default in this test file).
|
||||
// With max_missed_pongs = 2, the heartbeat thread should self-close within
|
||||
// roughly 3s. Poll is_open() up to 6s.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (client.is_open() &&
|
||||
std::chrono::steady_clock::now() - start < std::chrono::seconds(6)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
EXPECT_FALSE(client.is_open());
|
||||
}
|
||||
|
||||
// Verify that a responsive peer does NOT trigger the pong-timeout mechanism,
|
||||
// even with a small max_missed_pongs budget. This is the positive counterpart
|
||||
// of ClientDetectsNonResponsivePeer: the client must actively drive read() so
|
||||
// that incoming Pong frames are consumed and the unacked counter is reset.
|
||||
TEST_F(WebSocketHeartbeatTest, ResponsivePeerNeverTimesOut) {
|
||||
ws::WebSocketClient client("ws://localhost:" + std::to_string(port_) + "/ws");
|
||||
client.set_websocket_max_missed_pongs(2);
|
||||
ASSERT_TRUE(client.connect());
|
||||
|
||||
// Interactive loop over ~6s, longer than 2 ping intervals, so the
|
||||
// pong-timeout mechanism would trigger if pongs weren't being consumed.
|
||||
// Each iteration's read() also drains any pending Pong frame.
|
||||
for (int i = 0; i < 6; i++) {
|
||||
std::string text = "keepalive" + std::to_string(i);
|
||||
ASSERT_TRUE(client.send(text));
|
||||
std::string msg;
|
||||
ASSERT_TRUE(client.read(msg));
|
||||
EXPECT_EQ(text, msg);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(client.is_open());
|
||||
client.close();
|
||||
}
|
||||
|
||||
// Verify that multiple heartbeat cycles work
|
||||
TEST_F(WebSocketHeartbeatTest, MultipleHeartbeatCycles) {
|
||||
ws::WebSocketClient client("ws://localhost:" + std::to_string(port_) + "/ws");
|
||||
|
||||
Reference in New Issue
Block a user