Fixed issue #139: thread-safe exception handling.

This commit is contained in:
Vincent Richard 2016-08-25 18:36:05 +02:00
parent 4d1a6ad2f2
commit 4e28af3a21
2 changed files with 26 additions and 42 deletions

View File

@ -71,11 +71,7 @@ TLSSocket_GnuTLS::TLSSocket_GnuTLS(shared_ptr <TLSSession_GnuTLS> session, share
TLSSocket_GnuTLS::~TLSSocket_GnuTLS() TLSSocket_GnuTLS::~TLSSocket_GnuTLS()
{ {
if (m_ex) resetException();
{
delete m_ex;
m_ex = NULL;
}
try try
{ {
@ -194,12 +190,13 @@ size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count)
{ {
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
resetException();
const ssize_t ret = gnutls_record_recv const ssize_t ret = gnutls_record_recv
(*m_session->m_gnutlsSession, (*m_session->m_gnutlsSession,
buffer, static_cast <size_t>(count)); buffer, static_cast <size_t>(count));
if (m_ex) throwException();
internalThrow();
if (ret < 0) if (ret < 0)
{ {
@ -226,12 +223,13 @@ void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count)
for (size_t size = count ; size > 0 ; ) for (size_t size = count ; size > 0 ; )
{ {
resetException();
ssize_t ret = gnutls_record_send ssize_t ret = gnutls_record_send
(*m_session->m_gnutlsSession, (*m_session->m_gnutlsSession,
buffer, static_cast <size_t>(size)); buffer, static_cast <size_t>(size));
if (m_ex) throwException();
internalThrow();
if (ret < 0) if (ret < 0)
{ {
@ -260,12 +258,13 @@ size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t c
{ {
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
resetException();
ssize_t ret = gnutls_record_send ssize_t ret = gnutls_record_send
(*m_session->m_gnutlsSession, (*m_session->m_gnutlsSession,
buffer, static_cast <size_t>(count)); buffer, static_cast <size_t>(count));
if (m_ex) throwException();
internalThrow();
if (ret < 0) if (ret < 0)
{ {
@ -307,10 +306,11 @@ void TLSSocket_GnuTLS::handshake()
{ {
while (true) while (true)
{ {
resetException();
const int ret = gnutls_handshake(*m_session->m_gnutlsSession); const int ret = gnutls_handshake(*m_session->m_gnutlsSession);
if (m_ex) throwException();
internalThrow();
if (ret < 0) if (ret < 0)
{ {
@ -498,38 +498,21 @@ shared_ptr <security::cert::certificateChain> TLSSocket_GnuTLS::getPeerCertifica
// gnutls_record_recv() calls TLSSocket::gnutlsPullFunc, and exceptions // gnutls_record_recv() calls TLSSocket::gnutlsPullFunc, and exceptions
// thrown by the socket can not be caught. // thrown by the socket can not be caught.
#ifndef VMIME_BUILDING_DOC void TLSSocket_GnuTLS::throwException()
class TLSSocket_DeleteExWrapper : public object
{ {
public:
TLSSocket_DeleteExWrapper(exception* ex) : m_ex(ex) { }
~TLSSocket_DeleteExWrapper() { delete m_ex; }
private:
exception* m_ex;
};
#endif // VMIME_BUILDING_DOC
void TLSSocket_GnuTLS::internalThrow()
{
static std::vector <shared_ptr <TLSSocket_DeleteExWrapper> > exToDelete;
if (m_ex) if (m_ex)
{ {
// Reset the current exception pointer to prevent the same throw *m_ex;
// exception from being thrown again later }
exception* ex = m_ex; }
void TLSSocket_GnuTLS::resetException()
{
if (m_ex)
{
delete m_ex;
m_ex = NULL; m_ex = NULL;
// To avoid memory leaks
exToDelete.push_back(make_shared <TLSSocket_DeleteExWrapper>(ex));
throw *ex;
} }
} }

View File

@ -88,7 +88,8 @@ public:
private: private:
void internalThrow(); void resetException();
void throwException();
#ifdef LIBGNUTLS_VERSION #ifdef LIBGNUTLS_VERSION
static ssize_t gnutlsPushFunc(gnutls_transport_ptr_t trspt, const void* data, size_t len); static ssize_t gnutlsPushFunc(gnutls_transport_ptr_t trspt, const void* data, size_t len);