Skip to content

Commit b57f79f

Browse files
Detecting client disconnection (#1373)
* SocketStream need to check connectivity for writability. When the stream is used for SSE server which works via chunked content provider it's not possible to check connectivity over writability because it's wrapped by DataSink. It could make the server stalled, when the provider wants to only keep connection without send data and certain amount of clients consumes entire threadpool. * add unittest for SocketStream::is_writable() SocketStream::is_writable() should return false if peer is disconnected. * revise broken unittest ServerTest.ClientStop DataSink could be unwritable if it's backed by connection based. Because it could be disconnected by client right after enter centent provider. Co-authored-by: Changbin Park <changbin.park@ahnlab.com>
1 parent a9cf097 commit b57f79f

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

httplib.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4722,7 +4722,8 @@ inline bool SocketStream::is_readable() const {
47224722
}
47234723

47244724
inline bool SocketStream::is_writable() const {
4725-
return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
4725+
return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
4726+
is_socket_alive(sock_);
47264727
}
47274728

47284729
inline ssize_t SocketStream::read(char *ptr, size_t size) {
@@ -7345,8 +7346,8 @@ inline bool SSLSocketStream::is_readable() const {
73457346
}
73467347

73477348
inline bool SSLSocketStream::is_writable() const {
7348-
return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
7349-
0;
7349+
return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
7350+
is_socket_alive(sock_);
73507351
}
73517352

73527353
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {

test/test.cc

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,8 @@ class ServerTest : public ::testing::Test {
16431643
res.set_content_provider(
16441644
size_t(-1), "text/plain",
16451645
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
1646-
EXPECT_TRUE(sink.is_writable());
1646+
if (!sink.is_writable()) return false;
1647+
16471648
sink.os << "data_chunk";
16481649
return true;
16491650
});
@@ -5366,4 +5367,66 @@ TEST_F(UnixSocketTest, abstract) {
53665367
t.join();
53675368
}
53685369
#endif
5370+
5371+
TEST(SocketStream, is_writable_UNIX) {
5372+
int fd[2];
5373+
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
5374+
5375+
const auto asSocketStream =
5376+
[&] (socket_t fd, std::function<bool(Stream &)> func) {
5377+
return detail::process_client_socket(fd, 0, 0, 0, 0, func);
5378+
};
5379+
asSocketStream(fd[0], [&] (Stream &s0) {
5380+
EXPECT_EQ(s0.socket(), fd[0]);
5381+
EXPECT_TRUE(s0.is_writable());
5382+
5383+
EXPECT_EQ(0, close(fd[1]));
5384+
EXPECT_FALSE(s0.is_writable());
5385+
5386+
return true;
5387+
});
5388+
EXPECT_EQ(0, close(fd[0]));
5389+
}
5390+
5391+
TEST(SocketStream, is_writable_INET) {
5392+
sockaddr_in addr;
5393+
memset(&addr, 0, sizeof(addr));
5394+
addr.sin_family = AF_INET;
5395+
addr.sin_port = htons(PORT+1);
5396+
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
5397+
5398+
int disconnected_svr_sock = -1;
5399+
std::thread svr {[&] {
5400+
const int s = socket(AF_INET, SOCK_STREAM, 0);
5401+
ASSERT_LE(0, s);
5402+
ASSERT_EQ(0, ::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)));
5403+
ASSERT_EQ(0, listen(s, 1));
5404+
ASSERT_LE(0, disconnected_svr_sock = accept(s, nullptr, nullptr));
5405+
ASSERT_EQ(0, close(s));
5406+
}};
5407+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
5408+
5409+
std::thread cli {[&] {
5410+
const int s = socket(AF_INET, SOCK_STREAM, 0);
5411+
ASSERT_LE(0, s);
5412+
ASSERT_EQ(0, connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)));
5413+
ASSERT_EQ(0, close(s));
5414+
}};
5415+
cli.join();
5416+
svr.join();
5417+
ASSERT_NE(disconnected_svr_sock, -1);
5418+
5419+
const auto asSocketStream =
5420+
[&] (socket_t fd, std::function<bool(Stream &)> func) {
5421+
return detail::process_client_socket(fd, 0, 0, 0, 0, func);
5422+
};
5423+
asSocketStream(disconnected_svr_sock, [&] (Stream &ss) {
5424+
EXPECT_EQ(ss.socket(), disconnected_svr_sock);
5425+
EXPECT_FALSE(ss.is_writable());
5426+
5427+
return true;
5428+
});
5429+
5430+
ASSERT_EQ(0, close(disconnected_svr_sock));
5431+
}
53695432
#endif // #ifndef _WIN32

0 commit comments

Comments
 (0)