diff --git a/src/jamidht/p2p.cpp b/src/jamidht/p2p.cpp
index c87f3a0b5de1b9e292071cedd0e44412c7095d5a..edf0f66e4787f0db6a796fd47f3303aa623dc86b 100644
--- a/src/jamidht/p2p.cpp
+++ b/src/jamidht/p2p.cpp
@@ -28,7 +28,6 @@
 #include "ftp_server.h"
 #include "manager.h"
 #include "peer_connection.h"
-#include "security/tls_session.h"
 #include "turn_transport.h"
 #include "account_manager.h"
 
@@ -217,6 +216,7 @@ public:
         thread.join();
       servers_.clear();
       clients_.clear();
+      waitForReadyEndpoints_.clear();
       turnAuthv4_.reset();
       turnAuthv6_.reset();
       ctrl << makeMsg<CtrlMsgType::STOP>();
@@ -259,6 +259,7 @@ public:
 private:
     std::map<IpAddr, std::unique_ptr<ConnectedTurnTransport>> turnEndpoints_;
     std::map<std::pair<dht::InfoHash, IpAddr>, std::unique_ptr<AbstractSocketEndpoint>> p2pEndpoints_;
+    std::map<std::pair<dht::InfoHash, IpAddr>, std::unique_ptr<TlsSocketEndpoint>> waitForReadyEndpoints_;
     std::unique_ptr<TurnTransport> turnAuthv4_;
     std::unique_ptr<TurnTransport> turnAuthv6_;
 
@@ -269,6 +270,8 @@ private:
 
 protected:
     std::map<std::pair<dht::InfoHash, IpAddr>, std::unique_ptr<PeerConnection>> servers_;
+    std::map<IpAddr, std::unique_ptr<TlsTurnEndpoint>> tls_turn_ep_;
+
     std::map<std::pair<dht::InfoHash, DRing::DataTransferId>, std::unique_ptr<ClientConnector>> clients_;
     std::mutex clientsMutex_;
     std::mutex turnMutex_;
@@ -480,38 +483,27 @@ private:
 
         // Negotiate a TLS session
         JAMI_DBG() << parent_.account << "[CNX] start TLS session";
-        auto tls_ep = std::make_unique<TlsSocketEndpoint>(
+        tls_ep_ = std::make_unique<TlsSocketEndpoint>(
             *peer_ep, parent_.account.identity(), parent_.account.dhParams(),
             *peerCertificate_);
-        // block until TLS is negotiated (with 3 secs of
-        // timeout) (must throw in case of error)
-        try {
-          tls_ep->waitForReady(SOCK_TIMEOUT);
-        } catch (const std::logic_error &e) {
-          // In case of a timeout
-          JAMI_WARN() << "TLS connection timeout from peer " << peer_.toString()
-                      << ": " << e.what();
-          ice->cancelOperations(); // This will stop current PeerChannel operations
-          cancel();
-          return;
-        } catch (...) {
-          JAMI_WARN() << "TLS connection failure from peer "
-                      << peer_.toString();
-          ice->cancelOperations(); // This will stop current PeerChannel operations
-          cancel();
-          return;
-        }
-
-        // Connected!
-        connection_ = std::make_unique<PeerConnection>(
-            [this] { cancel(); }, peer_.toString(),
-            std::move(tls_ep));
-        peer_ep_ = std::move(peer_ep);
-
-        connected_ = true;
-        for (auto &cb : listeners_) {
-          cb(connection_.get());
-        }
+        tls_ep_->setOnStateChange([this, ice=std::move(ice), peer_ep] (tls::TlsSessionState state) {
+            if (state == tls::TlsSessionState::SHUTDOWN) {
+                JAMI_WARN() << "TLS connection failure from peer "
+                            << peer_.toString();
+                ice->cancelOperations(); // This will stop current PeerChannel operations
+                cancel();
+            } else if (state == tls::TlsSessionState::ESTABLISHED) {
+                // Connected!
+                connected_ = true;
+                connection_ = std::make_unique<PeerConnection>(
+                    [this] { cancel(); }, peer_.toString(),
+                    std::move(tls_ep_));
+                peer_ep_ = std::move(peer_ep);
+                for (auto &cb : listeners_) {
+                    cb(connection_.get());
+                }
+            }
+        });
     }
 
     Impl& parent_;
@@ -526,6 +518,7 @@ private:
     std::shared_ptr<dht::crypto::Certificate> peerCertificate_;
     std::shared_ptr<AbstractSocketEndpoint> peer_ep_;
     std::unique_ptr<PeerConnection> connection_;
+    std::unique_ptr<TlsSocketEndpoint> tls_ep_;
 
     std::atomic_bool connected_ {false};
     std::mutex listenersMutex_;
@@ -643,28 +636,25 @@ DhtPeerConnector::Impl::onTurnPeerConnection(const IpAddr& peer_addr)
 
     JAMI_DBG() << account << "[CNX] start TLS session over TURN socket";
     dht::InfoHash peer_h;
-    auto tls_ep = std::make_unique<TlsTurnEndpoint>(
+    tls_turn_ep_[peer_addr] = std::make_unique<TlsTurnEndpoint>(
         *turn_ep, account.identity(), account.dhParams(),
         [&, this] (const dht::crypto::Certificate& cert) { return validatePeerCertificate(cert, peer_h); });
 
-    // block until TLS is negotiated (with 3 secs of timeout) (must throw in case of error)
-    try {
-        tls_ep->waitForReady(SOCK_TIMEOUT);
-    } catch (const std::logic_error& e) {
-        // In case of a timeout
-        JAMI_WARN() << "TLS connection timeout from peer " << peer_addr.toString(true, true) << ": " << e.what();
-        return;
-    } catch (...) {
-        JAMI_WARN() << "[CNX] TLS connection failure from peer " << peer_addr.toString(true, true);
-        return;
-    }
 
-    JAMI_DBG() << account << "[CNX] Accepted TLS-TURN connection from RingID " << peer_h;
-    connectedPeers_.emplace(peer_addr, tls_ep->peerCertificate().getId());
-    auto connection = std::make_unique<PeerConnection>(
-        [] {}, peer_addr.toString(), std::move(tls_ep));
-    connection->attachOutputStream(std::make_shared<FtpServer>(account.getAccountID(), peer_h.toString()));
-    servers_.emplace(std::make_pair(peer_h, peer_addr), std::move(connection));
+    tls_turn_ep_[peer_addr]->setOnStateChange([this, peer_addr, peer_h] (tls::TlsSessionState state) {
+        if (state == tls::TlsSessionState::SHUTDOWN) {
+            JAMI_WARN() << "[CNX] TLS connection failure from peer " << peer_addr.toString(true, true);
+            tls_turn_ep_.erase(peer_addr);
+        } else if (state == tls::TlsSessionState::ESTABLISHED) {
+            JAMI_DBG() << account << "[CNX] Accepted TLS-TURN connection from RingID " << peer_h;
+            connectedPeers_.emplace(peer_addr, tls_turn_ep_[peer_addr]->peerCertificate().getId());
+            auto connection = std::make_unique<PeerConnection>(
+                [] {}, peer_addr.toString(), std::move(tls_turn_ep_[peer_addr]));
+            connection->attachOutputStream(std::make_shared<FtpServer>(account.getAccountID(), peer_h.toString()));
+            servers_.emplace(std::make_pair(peer_h, peer_addr), std::move(connection));
+            tls_turn_ep_.erase(peer_addr);
+        }
+    });
 
     // note: operating this way let endpoint to be deleted safely in case of exceptions
     {
@@ -865,30 +855,36 @@ DhtPeerConnector::Impl::answerToRequest(PeerConnectionMsg&& request,
         JAMI_DBG() << account << "[CNX] start TLS session";
         auto ph = peer_h;
         if (hasPubIp) ice->setSlaveSession();
-        auto tls_ep = std::make_unique<TlsSocketEndpoint>(
-            *peer_ep, account.identity(), account.dhParams(),
-            [&, this](const dht::crypto::Certificate &cert) {
-              return validatePeerCertificate(cert, ph);
-            });
-        // block until TLS is negotiated (with 3 secs of timeout)
-        // (must throw in case of error)
-        try {
-          tls_ep->waitForReady(SOCK_TIMEOUT);
-        } catch (const std::exception &e) {
-          // In case of a timeout
-          JAMI_WARN() << "TLS connection timeout: " << e.what();
-          return;
-        }
 
-        auto connection = std::make_unique<PeerConnection>(
-            [] {}, peer_h.toString(),
-            std::move(tls_ep));
-        connection->attachOutputStream(std::make_shared<FtpServer>(
-            account.getAccountID(), peer_h.toString()));
-        servers_.emplace(std::make_pair(peer_h, ice->getRemoteAddress(0)),
-                        std::move(connection));
-        p2pEndpoints_.emplace(std::make_pair(peer_h, ice->getRemoteAddress(0)),
-                        std::move(peer_ep));
+        auto idx = std::make_pair(peer_h, ice->getRemoteAddress(0));
+        auto it = waitForReadyEndpoints_.emplace(
+            idx,
+            std::make_unique<TlsSocketEndpoint>(*peer_ep, account.identity(), account.dhParams(),
+                [&, this](const dht::crypto::Certificate &cert) {
+                    return validatePeerCertificate(cert, ph);
+                }
+            )
+        );
+        p2pEndpoints_.emplace(idx, std::move(peer_ep));
+
+        it.first->second->setOnStateChange([this, idx=std::move(idx)] (tls::TlsSessionState state) {
+            if (waitForReadyEndpoints_.find(idx) == waitForReadyEndpoints_.end()) {
+                return;
+            }
+            if (state == tls::TlsSessionState::SHUTDOWN) {
+                JAMI_WARN() << "TLS connection failure";
+                waitForReadyEndpoints_.erase(idx);
+            } else if (state == tls::TlsSessionState::ESTABLISHED) {
+                // Connected!
+                auto peer_h = idx.first.toString();
+                auto connection = std::make_unique<PeerConnection>(
+                    [] {}, peer_h,
+                    std::move(waitForReadyEndpoints_[idx]));
+                connection->attachOutputStream(std::make_shared<FtpServer>(account.getAccountID(), peer_h));
+                servers_.emplace(idx, std::move(connection));
+                waitForReadyEndpoints_.erase(idx);
+            }
+        });
     }
     // Now wait for a TURN connection from peer (see onTurnPeerConnection) if fallbacking
 }
diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp
index e2e36d39602ec30fb1d397f1a914f6bc5902403e..4aef29e137a92718dd1bc3c086657609208d82a8 100644
--- a/src/peer_connection.cpp
+++ b/src/peer_connection.cpp
@@ -111,6 +111,7 @@ public:
     ConnectedTurnTransport& turn;
     std::function<bool(const dht::crypto::Certificate&)> peerCertificateCheckFunc;
     dht::crypto::Certificate peerCertificate;
+    OnStateChangeCb onStateChangeCb_;
 };
 
 // Declaration at namespace scope is necessary (until C++17)
@@ -135,8 +136,11 @@ TlsTurnEndpoint::Impl::verifyCertificate(gnutls_session_t session)
 }
 
 void
-TlsTurnEndpoint::Impl::onTlsStateChange(tls::TlsSessionState)
-{}
+TlsTurnEndpoint::Impl::onTlsStateChange(tls::TlsSessionState state)
+{
+    if (onStateChangeCb_)
+        onStateChangeCb_(state);
+}
 
 void
 TlsTurnEndpoint::Impl::onTlsRxData(UNUSED std::vector<uint8_t>&& buf)
@@ -226,6 +230,12 @@ TlsTurnEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_code&
     return pimpl_->tls->waitForData(timeout, ec);
 }
 
+void
+TlsTurnEndpoint::setOnStateChange(std::function<void(tls::TlsSessionState state)>&& cb)
+{
+    pimpl_->onStateChangeCb_ = std::move(cb);
+}
+
 //==============================================================================
 
 TcpSocketEndpoint::TcpSocketEndpoint(const IpAddr& addr)
@@ -397,6 +407,7 @@ public:
     const dht::crypto::Certificate& peerCertificate;
     dht::crypto::Certificate null_cert;
     std::function<bool(const dht::crypto::Certificate &)> peerCertificateCheckFunc;
+    OnStateChangeCb onStateChangeCb_;
 };
 
 // Declaration at namespace scope is necessary (until C++17)
@@ -426,8 +437,11 @@ TlsSocketEndpoint::Impl::verifyCertificate(gnutls_session_t session)
 }
 
 void
-TlsSocketEndpoint::Impl::onTlsStateChange(UNUSED tls::TlsSessionState state)
-{}
+TlsSocketEndpoint::Impl::onTlsStateChange(tls::TlsSessionState state)
+{
+    if (onStateChangeCb_)
+        onStateChangeCb_(state);
+}
 
 void
 TlsSocketEndpoint::Impl::onTlsRxData(UNUSED std::vector<uint8_t>&& buf)
@@ -530,6 +544,13 @@ TlsSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_cod
     return pimpl_->tls->waitForData(timeout, ec);
 }
 
+void
+TlsSocketEndpoint::setOnStateChange(std::function<void(tls::TlsSessionState state)>&& cb)
+{
+    pimpl_->onStateChangeCb_ = std::move(cb);
+}
+
+
 //==============================================================================
 
 // following namespace prevents an ODR violation with definitions in p2p.cpp
diff --git a/src/peer_connection.h b/src/peer_connection.h
index 11a00554f861f1ac95cd1e3fd7c56ad0340ac30d..0f15665b3c66d1bab0ce308101a08bc1331f218d 100644
--- a/src/peer_connection.h
+++ b/src/peer_connection.h
@@ -27,6 +27,7 @@
 #include "security/diffie-hellman.h"
 #include "opendht/crypto.h"
 #include "ice_transport.h"
+#include "security/tls_session.h"
 
 #include <functional>
 #include <future>
@@ -44,6 +45,8 @@ struct Certificate;
 
 namespace jami {
 
+using OnStateChangeCb = std::function<void(tls::TlsSessionState state)>;
+
 class TurnTransport;
 class ConnectedTurnTransport;
 
@@ -100,6 +103,7 @@ public:
     void waitForReady(const std::chrono::steady_clock::duration& timeout = {});
 
     const dht::crypto::Certificate& peerCertificate() const;
+    void setOnStateChange(OnStateChangeCb&& cb);
 
 private:
     class Impl;
@@ -200,6 +204,8 @@ public:
 
     void waitForReady(const std::chrono::milliseconds& timeout = {});
 
+    void setOnStateChange(OnStateChangeCb&& cb);
+
 private:
     class Impl;
     std::unique_ptr<Impl> pimpl_;