Skip to content

Commit

Permalink
enh(Net): non-blocking TLS shutdown
Browse files Browse the repository at this point in the history
  • Loading branch information
obiltschnig committed Nov 25, 2024
1 parent 6c7fd8d commit fc64e5a
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 44 deletions.
14 changes: 12 additions & 2 deletions Net/include/Poco/Net/SocketImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,22 @@ class Net_API SocketImpl: public Poco::RefCountedObject
virtual void shutdownReceive();
/// Shuts down the receiving part of the socket connection.

virtual void shutdownSend();
virtual int shutdownSend();
/// Shuts down the sending part of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried.

virtual void shutdown();
virtual int shutdown();
/// Shuts down both the receiving and the sending part
/// of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried.

virtual int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through
Expand Down
14 changes: 12 additions & 2 deletions Net/include/Poco/Net/StreamSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,22 @@ class Net_API StreamSocket: public Socket
void shutdownReceive();
/// Shuts down the receiving part of the socket connection.

void shutdownSend();
int shutdownSend();
/// Shuts down the sending part of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried.

void shutdown();
int shutdown();
/// Shuts down both the receiving and the sending part
/// of the socket connection.
///
/// Returns 0 for a non-blocking socket. May return
/// a negative value for a non-blocking socket in case
/// of a TLS connection. In that case, the operation should
/// be retried.

int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through
Expand Down
4 changes: 2 additions & 2 deletions Net/include/Poco/Net/WebSocketImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class Net_API WebSocketImpl: public StreamSocketImpl
virtual void listen(int backlog = 64);
virtual void close();
virtual void shutdownReceive();
virtual void shutdownSend();
virtual void shutdown();
virtual int shutdownSend();
virtual int shutdown();
virtual int sendTo(const void* buffer, int length, const SocketAddress& address, int flags = 0);
virtual int receiveFrom(void* buffer, int length, SocketAddress& address, int flags = 0);
virtual void sendUrgent(unsigned char data);
Expand Down
6 changes: 4 additions & 2 deletions Net/src/SocketImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,21 +293,23 @@ void SocketImpl::shutdownReceive()
}


void SocketImpl::shutdownSend()
int SocketImpl::shutdownSend()
{
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();

int rc = ::shutdown(_sockfd, 1);
if (rc != 0) error();
return 0;
}


void SocketImpl::shutdown()
int SocketImpl::shutdown()
{
if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException();

int rc = ::shutdown(_sockfd, 2);
if (rc != 0) error();
return 0;
}


Expand Down
8 changes: 4 additions & 4 deletions Net/src/StreamSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ void StreamSocket::shutdownReceive()
}


void StreamSocket::shutdownSend()
int StreamSocket::shutdownSend()
{
impl()->shutdownSend();
return impl()->shutdownSend();
}


void StreamSocket::shutdown()
int StreamSocket::shutdown()
{
impl()->shutdown();
return impl()->shutdown();
}


Expand Down
8 changes: 4 additions & 4 deletions Net/src/WebSocketImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,15 +537,15 @@ void WebSocketImpl::shutdownReceive()
}


void WebSocketImpl::shutdownSend()
int WebSocketImpl::shutdownSend()
{
_pStreamSocketImpl->shutdownSend();
return _pStreamSocketImpl->shutdownSend();
}


void WebSocketImpl::shutdown()
int WebSocketImpl::shutdown()
{
_pStreamSocketImpl->shutdown();
return _pStreamSocketImpl->shutdown();
}


Expand Down
10 changes: 8 additions & 2 deletions NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,19 @@ class NetSSL_API SecureSocketImpl
/// number of connections that can be queued
/// for this socket.

void shutdown();
int shutdown();
/// Shuts down the connection by attempting
/// an orderly SSL shutdown, then actually
/// shutting down the TCP connection.
/// shutting down the TCP connection in the
/// send direction.

void close();
/// Close the socket.
///
/// If the SSL connection has not been shut down
/// yet, tries to perform a fast shutdown
/// (sending a close notify shutdown alert message,
/// but not waiting for the response).

void abort();
/// Aborts the connection by closing the
Expand Down
19 changes: 15 additions & 4 deletions NetSSL_OpenSSL/include/Poco/Net/SecureStreamSocketImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,25 @@ class NetSSL_API SecureStreamSocketImpl: public StreamSocketImpl
/// Since SSL does not support a half shutdown, this does
/// nothing.

void shutdownSend();
int shutdownSend();
/// Shuts down the receiving part of the socket connection.
///
/// Since SSL does not support a half shutdown, this does
/// nothing.
/// Sends a close notify shutdown alert message to the peer
/// (if not sent yet), then calls shutdownSend() on the
/// underlying socket.
///
/// Returns 0 if the message has been sent.
/// Returns 1 if the message has been sent, but the peer
/// has not yet sent its shutdown alert message.
/// In case of a non-blocking socket, returns < 0 if the
/// message cannot be sent at the moment. In this case,
/// the call to shutdownSend() must be retried after the
/// underlying socket becomes writable again.

void shutdown();
int shutdown();
/// Shuts down the SSL connection.
///
/// Same as shutdownSend().

void abort();
/// Aborts the connection by closing the underlying
Expand Down
33 changes: 20 additions & 13 deletions NetSSL_OpenSSL/src/SecureSocketImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,30 +237,34 @@ void SecureSocketImpl::listen(int backlog)
}


void SecureSocketImpl::shutdown()
int SecureSocketImpl::shutdown()
{
if (_pSSL)
{
// Don't shut down the socket more than once.
int shutdownState = SSL_get_shutdown(_pSSL);
bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN;
if (!shutdownSent)
{
// A proper clean shutdown would require us to
// retry the shutdown if we get a zero return
// value, until SSL_shutdown() returns 1.
// However, this will lead to problems with
// most web browsers, so we just set the shutdown
// flag by calling SSL_shutdown() once and be
// done with it.
int rc = SSL_shutdown(_pSSL);
if (rc < 0) handleError(rc);
if (_pSocket->getBlocking())
if (rc < 0)
{
if (SocketImpl::lastError() == POCO_EWOULDBLOCK)
rc = SecureStreamSocket::ERR_SSL_WANT_WRITE;
else
rc = handleError(rc);
}
if (rc >= 0)
{
_pSocket->shutdown();
_pSocket->shutdownSend();
}
return rc;
}
else
{
return (shutdownState & SSL_RECEIVED_SHUTDOWN) == SSL_RECEIVED_SHUTDOWN;
}
}
return 1;
}


Expand Down Expand Up @@ -500,7 +504,10 @@ int SecureSocketImpl::handleError(int rc)
#endif
if (socketError)
{
SocketImpl::error(socketError);
if (socketError == POCO_EWOULDBLOCK)
return SSL_ERROR_WANT_READ;
else
SocketImpl::error(socketError);
}
// fallthrough
default:
Expand Down
7 changes: 4 additions & 3 deletions NetSSL_OpenSSL/src/SecureStreamSocketImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,15 @@ void SecureStreamSocketImpl::shutdownReceive()
}


void SecureStreamSocketImpl::shutdownSend()
int SecureStreamSocketImpl::shutdownSend()
{
return _impl.shutdown();
}


void SecureStreamSocketImpl::shutdown()
int SecureStreamSocketImpl::shutdown()
{
_impl.shutdown();
return _impl.shutdown();
}


Expand Down
22 changes: 16 additions & 6 deletions WebTunnel/src/SocketDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,12 +508,17 @@ void SocketDispatcher::writable(const Poco::Net::Socket& socket, SocketDispatche
PendingSend& pending = *pInfo->pendingSends.begin();
if (pending.options == PendingSend::OPT_SHUTDOWN)
{
ss.shutdownSend();
if (pInfo->pendingSends.size() > 1)
_logger.debug("Shutting down socket %?d..."s, ss.impl()->sockfd());
if (ss.shutdownSend() >= 0)
{
_logger.debug("Discarding pending writes after shutdown");
_logger.debug("Shut down socket %?d."s, ss.impl()->sockfd());
if (pInfo->pendingSends.size() > 1)
{
_logger.debug("Discarding pending writes after shutdown."s);
}
pInfo->pendingSends.clear();
}
pInfo->pendingSends.clear();
else break;
}
else
{
Expand Down Expand Up @@ -667,7 +672,7 @@ void SocketDispatcher::sendBytesImpl(Poco::Net::StreamSocket& socket, Poco::Buff
}
else
{
_logger.error("sendBytes() called with unknown socket.");
_logger.error("sendBytes() called with unknown socket."s);
}
}

Expand All @@ -679,7 +684,12 @@ void SocketDispatcher::shutdownSendImpl(Poco::Net::StreamSocket& socket)
{
if (it->second->pendingSends.empty())
{
socket.shutdownSend();
int rc = socket.shutdownSend();
if (rc < 0)
{
// would block, try again later
it->second->pendingSends.emplace_back(PendingSend::OPT_SHUTDOWN);
}
}
else
{
Expand Down

0 comments on commit fc64e5a

Please sign in to comment.