Skip to content

Commit eb94de0

Browse files
authored
fix(TCPServer): continues to accept connections after stop() #4892 (#4896)
* fix(TCPServer): continues to accept connections after stop() #4892 * fix(TCPServer): first attempt to silence TSAN #4892 * fix(TCPServer): check stopped status after poll; add TCPServerFactory::stop() and allow factory to return nullptr on connection creation request #4892 * fix(TCPServer): initialize factory stopped flag #4892 * revert(SocketImpl): atomic sock fd
1 parent 91244ac commit eb94de0

5 files changed

+74
-21
lines changed

Net/include/Poco/Net/SocketImpl.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -295,19 +295,19 @@ class Net_API SocketImpl: public Poco::RefCountedObject
295295
/// If count is != 0, sends the given number of bytes, otherwise
296296
/// sends all bytes, starting from the given offset.
297297
///
298-
/// On Linux, macOS and FreeBSD systems, the implementation
298+
/// On Linux, macOS and FreeBSD systems, the implementation
299299
/// uses sendfile() or sendfile64().
300300
/// On Windows, the implementation uses TransmitFile().
301301
///
302-
/// If neither sendfile() nor TransmitFile() is available,
302+
/// If neither sendfile() nor TransmitFile() is available,
303303
/// or the socket is a secure one (secure() returne true),
304304
/// falls back to reading the file block by block and calling sendBytes().
305305
///
306306
/// Returns the number of bytes sent, which should be the same
307307
/// as count, unless count is 0.
308308
///
309309
/// Throws NetException (or a subclass) in case of any errors.
310-
/// Also throws a NetException if the socket has been set to
310+
/// Also throws a NetException if the socket has been set to
311311
/// non-blocking.
312312

313313
virtual int available();
@@ -636,7 +636,7 @@ inline bool SocketImpl::getBlocking() const
636636

637637

638638
#if defined(POCO_OS_FAMILY_WINDOWS)
639-
#pragma comment(lib, "mswsock.lib")
640-
#endif
639+
#pragma comment(lib, "mswsock.lib")
640+
#endif
641641

642642
#endif // Net_SocketImpl_INCLUDED

Net/include/Poco/Net/TCPServerConnectionFactory.h

+45-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ class Net_API TCPServerConnectionFactory
3535
/// it accepts.
3636
///
3737
/// Subclasses must override the createConnection()
38-
/// method.
38+
/// method, which can refuse connections by returning nullptr.
39+
/// Some examples when an implementation may refuse new connections:
40+
///
41+
/// - number of connections exceeded a limit
42+
/// - a connection from unwanted client attempted
43+
/// - too many connection attempts in a short timespan
44+
/// - etc.
3945
///
4046
/// The TCPServerConnectionFactoryImpl template class
4147
/// can be used to automatically instantiate a
@@ -45,23 +51,55 @@ class Net_API TCPServerConnectionFactory
4551
public:
4652
using Ptr = Poco::SharedPtr<TCPServerConnectionFactory>;
4753

54+
TCPServerConnectionFactory(const TCPServerConnectionFactory&) = delete;
55+
TCPServerConnectionFactory& operator = (const TCPServerConnectionFactory&) = delete;
56+
TCPServerConnectionFactory(TCPServerConnectionFactory&&) = delete;
57+
TCPServerConnectionFactory& operator = (TCPServerConnectionFactory&&) = delete;
58+
4859
virtual ~TCPServerConnectionFactory();
4960
/// Destroys the TCPServerConnectionFactory.
5061

5162
virtual TCPServerConnection* createConnection(const StreamSocket& socket) = 0;
5263
/// Creates an instance of a subclass of TCPServerConnection,
5364
/// using the given StreamSocket.
65+
/// This function is allowed to return nullptr, in which case an accepted
66+
/// socket will be destroyed by the TCPServerDispatcher.
67+
68+
void stop();
69+
/// Stops the factory.
70+
/// Normally, this function is called by TCPServerDispatcher
71+
/// to indicate that the server is shutting down; the expected
72+
/// implementation behavior after this call is to return nullptr
73+
/// on all subsequent connection creation attempts.
74+
75+
bool isStopped() const;
76+
/// Returns true if the factory was stopped, false otherwise.
5477

5578
protected:
5679
TCPServerConnectionFactory();
5780
/// Creates the TCPServerConnectionFactory.
5881

5982
private:
60-
TCPServerConnectionFactory(const TCPServerConnectionFactory&);
61-
TCPServerConnectionFactory& operator = (const TCPServerConnectionFactory&);
83+
std::atomic<bool> _stopped;
6284
};
6385

6486

87+
//
88+
// inlines
89+
//
90+
91+
inline void TCPServerConnectionFactory::stop()
92+
{
93+
_stopped = true;
94+
}
95+
96+
97+
inline bool TCPServerConnectionFactory::isStopped() const
98+
{
99+
return _stopped;
100+
}
101+
102+
65103
template <class S>
66104
class TCPServerConnectionFactoryImpl: public TCPServerConnectionFactory
67105
/// This template provides a basic implementation of
@@ -78,7 +116,10 @@ class TCPServerConnectionFactoryImpl: public TCPServerConnectionFactory
78116

79117
TCPServerConnection* createConnection(const StreamSocket& socket)
80118
{
81-
return new S(socket);
119+
if (!isStopped())
120+
return new S(socket);
121+
122+
return nullptr;
82123
}
83124
};
84125

Net/src/TCPServer.cpp

+15-7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ void TCPServer::run()
132132
{
133133
if (_socket.poll(timeout, Socket::SELECT_READ))
134134
{
135+
if (_stopped) return;
136+
135137
try
136138
{
137139
StreamSocket ss = _socket.acceptConnection();
@@ -150,24 +152,30 @@ void TCPServer::run()
150152
}
151153
catch (Poco::Exception& exc)
152154
{
153-
ErrorHandler::handle(exc);
155+
if (!_stopped)
156+
ErrorHandler::handle(exc);
154157
}
155158
catch (std::exception& exc)
156159
{
157-
ErrorHandler::handle(exc);
160+
if (!_stopped)
161+
ErrorHandler::handle(exc);
158162
}
159163
catch (...)
160164
{
161-
ErrorHandler::handle();
165+
if (!_stopped)
166+
ErrorHandler::handle();
162167
}
163168
}
164169
}
165170
catch (Poco::Exception& exc)
166171
{
167-
ErrorHandler::handle(exc);
168-
// possibly a resource issue since poll() failed;
169-
// give some time to recover before trying again
170-
Poco::Thread::sleep(50);
172+
if (!_stopped)
173+
{
174+
ErrorHandler::handle(exc);
175+
// possibly a resource issue since poll() failed;
176+
// give some time to recover before trying again
177+
Poco::Thread::sleep(50);
178+
}
171179
}
172180
}
173181
}

Net/src/TCPServerConnectionFactory.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ namespace Poco {
1919
namespace Net {
2020

2121

22-
TCPServerConnectionFactory::TCPServerConnectionFactory()
22+
TCPServerConnectionFactory::TCPServerConnectionFactory():
23+
_stopped(false)
2324
{
2425
}
2526

Net/src/TCPServerDispatcher.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,12 @@ void TCPServerDispatcher::run()
112112
if (pCNf)
113113
{
114114
std::unique_ptr<TCPServerConnection> pConnection(_pConnectionFactory->createConnection(pCNf->socket()));
115-
poco_check_ptr(pConnection.get());
116-
beginConnection();
117-
pConnection->start();
118-
endConnection();
115+
if (pConnection)
116+
{
117+
beginConnection();
118+
pConnection->start();
119+
endConnection();
120+
}
119121
}
120122
}
121123
}
@@ -173,6 +175,7 @@ void TCPServerDispatcher::enqueue(const StreamSocket& socket)
173175
void TCPServerDispatcher::stop()
174176
{
175177
FastMutex::ScopedLock lock(_mutex);
178+
_pConnectionFactory->stop();
176179
_stopped = true;
177180
_queue.clear();
178181
for (int i = 0; i < _threadPool.allocated(); i++)

0 commit comments

Comments
 (0)