Skip to content

Commit ba714ea

Browse files
committed
Fix bad_weak_ptr when close a ClientConnection during construction
Fixes #348 Fixes #349 ### Motivation When `close` is called in `ClientConnection`'s constructor, `shared_from_this()` will be called, which results in a `std::bad_weak_ptr` error. This error does not happen before #317 because `shared_from_this()` could only be called when the `producers` or `consumers` field is not empty. ### Modifications Add a `ResultException` and throw it if there is a failure in `ClientConnection`'s constructor and catch it in `ConnectionPool::getConnectionAsync`. Then retrieve the result and return the failed future. Since `close` is now always called on a constructed `ClientConnection` object, remove the 2nd parameter. In addition, check `authentication_` even for non-TLS URLs. Otherwise, the null authentication will be used to construct `CommandConnect`. Add `testInvalidPlugin` and `testTlsConfigError` to verify the changes.
1 parent 6d47e94 commit ba714ea

File tree

7 files changed

+63
-39
lines changed

7 files changed

+63
-39
lines changed

lib/ClientConnection.cc

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ ClientConnection::ClientConnection(const std::string& logicalAddress, const std:
187187
pool_(pool),
188188
poolIndex_(poolIndex) {
189189
LOG_INFO(cnxString_ << "Create ClientConnection, timeout=" << clientConfiguration.getConnectionTimeout());
190+
if (!authentication_) {
191+
LOG_ERROR("Invalid authentication plugin");
192+
throw ResultException{ResultAuthenticationError};
193+
}
190194
if (clientConfiguration.isUseTls()) {
191195
#if BOOST_VERSION >= 105400
192196
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client);
@@ -207,20 +211,13 @@ ClientConnection::ClientConnection(const std::string& logicalAddress, const std:
207211
ctx.load_verify_file(trustCertFilePath);
208212
} else {
209213
LOG_ERROR(trustCertFilePath << ": No such trustCertFile");
210-
close(ResultAuthenticationError, false);
211-
return;
214+
throw ResultException{ResultAuthenticationError};
212215
}
213216
} else {
214217
ctx.set_default_verify_paths();
215218
}
216219
}
217220

218-
if (!authentication_) {
219-
LOG_ERROR("Invalid authentication plugin");
220-
close(ResultAuthenticationError, false);
221-
return;
222-
}
223-
224221
std::string tlsCertificates = clientConfiguration.getTlsCertificateFilePath();
225222
std::string tlsPrivateKey = clientConfiguration.getTlsPrivateKeyFilePath();
226223

@@ -231,13 +228,11 @@ ClientConnection::ClientConnection(const std::string& logicalAddress, const std:
231228
tlsPrivateKey = authData->getTlsPrivateKey();
232229
if (!file_exists(tlsCertificates)) {
233230
LOG_ERROR(tlsCertificates << ": No such tlsCertificates");
234-
close(ResultAuthenticationError, false);
235-
return;
231+
throw ResultException{ResultAuthenticationError};
236232
}
237233
if (!file_exists(tlsCertificates)) {
238234
LOG_ERROR(tlsCertificates << ": No such tlsCertificates");
239-
close(ResultAuthenticationError, false);
240-
return;
235+
throw ResultException{ResultAuthenticationError};
241236
}
242237
ctx.use_private_key_file(tlsPrivateKey, boost::asio::ssl::context::pem);
243238
ctx.use_certificate_file(tlsCertificates, boost::asio::ssl::context::pem);
@@ -1260,7 +1255,7 @@ void ClientConnection::handleConsumerStatsTimeout(const boost::system::error_cod
12601255
startConsumerStatsTimer(consumerStatsRequests);
12611256
}
12621257

1263-
void ClientConnection::close(Result result, bool detach) {
1258+
void ClientConnection::close(Result result) {
12641259
Lock lock(mutex_);
12651260
if (isClosed()) {
12661261
return;
@@ -1320,9 +1315,7 @@ void ClientConnection::close(Result result, bool detach) {
13201315
LOG_INFO(cnxString_ << "Connection disconnected (refCnt: " << refCount << ")");
13211316
}
13221317
// Remove the connection from the pool before completing any promise
1323-
if (detach) {
1324-
pool_.remove(logicalAddress_ + "-" + std::to_string(poolIndex_), this);
1325-
}
1318+
pool_.remove(logicalAddress_ + "-" + std::to_string(poolIndex_), this);
13261319

13271320
auto self = shared_from_this();
13281321
for (ProducersMap::iterator it = producers.begin(); it != producers.end(); ++it) {

lib/ClientConnection.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <boost/asio/strand.hpp>
3333
#include <boost/optional.hpp>
3434
#include <deque>
35+
#include <exception>
3536
#include <functional>
3637
#include <memory>
3738
#include <string>
@@ -146,13 +147,8 @@ class PULSAR_PUBLIC ClientConnection : public std::enable_shared_from_this<Clien
146147
* Close the connection.
147148
*
148149
* @param result all pending futures will complete with this result
149-
* @param detach remove it from the pool if it's true
150-
*
151-
* `detach` should only be false when:
152-
* 1. Before the connection is put into the pool, i.e. during the construction.
153-
* 2. When the connection pool is closed
154150
*/
155-
void close(Result result = ResultConnectError, bool detach = true);
151+
void close(Result result = ResultConnectError);
156152

157153
bool isClosed() const;
158154

lib/ConnectionPool.cc

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ClientConnection.h"
2525
#include "ExecutorService.h"
2626
#include "LogUtils.h"
27+
#include "ResultUtils.h"
2728

2829
using boost::asio::ip::tcp;
2930
namespace ssl = boost::asio::ssl;
@@ -53,21 +54,17 @@ bool ConnectionPool::close() {
5354
for (auto cnxIt = pool_.begin(); cnxIt != pool_.end(); cnxIt++) {
5455
auto& cnx = cnxIt->second;
5556
if (cnx) {
56-
// The 2nd argument is false because removing a value during the iteration will cause segfault
57-
cnx->close(ResultDisconnected, false);
57+
cnx->close(ResultDisconnected);
5858
}
5959
}
6060
pool_.clear();
6161
return true;
6262
}
6363

64-
Future<Result, ClientConnectionWeakPtr> ConnectionPool::getConnectionAsync(const std::string& logicalAddress,
65-
const std::string& physicalAddress,
66-
size_t keySuffix) {
64+
auto ConnectionPool::getConnectionAsync(const std::string& logicalAddress, const std::string& physicalAddress,
65+
size_t keySuffix) -> ConnectionFuture {
6766
if (closed_) {
68-
Promise<Result, ClientConnectionWeakPtr> promise;
69-
promise.setFailed(ResultAlreadyClosed);
70-
return promise.getFuture();
67+
return ConnectionFuture::failure(ResultAlreadyClosed);
7168
}
7269

7370
std::unique_lock<std::recursive_mutex> lock(mutex_);
@@ -99,12 +96,12 @@ Future<Result, ClientConnectionWeakPtr> ConnectionPool::getConnectionAsync(const
9996
cnx.reset(new ClientConnection(logicalAddress, physicalAddress, executorProvider_->get(keySuffix),
10097
clientConfiguration_, authentication_, clientVersion_, *this,
10198
keySuffix));
99+
} catch (const ResultException& e) {
100+
return ConnectionFuture::failure(e.result());
102101
} catch (const std::runtime_error& e) {
103102
lock.unlock();
104103
LOG_ERROR("Failed to create connection: " << e.what())
105-
Promise<Result, ClientConnectionWeakPtr> promise;
106-
promise.setFailed(ResultConnectError);
107-
return promise.getFuture();
104+
return ConnectionFuture::failure(ResultConnectError);
108105
}
109106

110107
LOG_INFO("Created connection for " << key);

lib/ConnectionPool.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class PULSAR_PUBLIC ConnectionPool {
5353

5454
void remove(const std::string& key, ClientConnection* value);
5555

56+
using ConnectionFuture = Future<Result, ClientConnectionWeakPtr>;
57+
5658
/**
5759
* Get a connection from the pool.
5860
* <p>
@@ -73,16 +75,15 @@ class PULSAR_PUBLIC ConnectionPool {
7375
* @param keySuffix the key suffix to choose which connection on the same broker
7476
* @return a future that will produce the ClientCnx object
7577
*/
76-
Future<Result, ClientConnectionWeakPtr> getConnectionAsync(const std::string& logicalAddress,
77-
const std::string& physicalAddress,
78-
size_t keySuffix);
78+
ConnectionFuture getConnectionAsync(const std::string& logicalAddress, const std::string& physicalAddress,
79+
size_t keySuffix);
7980

80-
Future<Result, ClientConnectionWeakPtr> getConnectionAsync(const std::string& logicalAddress,
81-
const std::string& physicalAddress) {
81+
ConnectionFuture getConnectionAsync(const std::string& logicalAddress,
82+
const std::string& physicalAddress) {
8283
return getConnectionAsync(logicalAddress, physicalAddress, generateRandomIndex());
8384
}
8485

85-
Future<Result, ClientConnectionWeakPtr> getConnectionAsync(const std::string& address) {
86+
ConnectionFuture getConnectionAsync(const std::string& address) {
8687
return getConnectionAsync(address, address);
8788
}
8889

lib/Future.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class Future {
116116

117117
Result get(Type &result) { return state_->get(result); }
118118

119+
static Future<Result, Type> failure(Result result);
120+
119121
private:
120122
InternalStatePtr<Result, Type> state_;
121123

@@ -144,6 +146,13 @@ class Promise {
144146
const InternalStatePtr<Result, Type> state_;
145147
};
146148

149+
template <typename Result, typename Type>
150+
inline Future<Result, Type> Future<Result, Type>::failure(Result result) {
151+
Promise<Result, Type> promise;
152+
promise.setFailed(result);
153+
return promise.getFuture();
154+
}
155+
147156
} // namespace pulsar
148157

149158
#endif

lib/ResultUtils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,22 @@
2020

2121
#include <pulsar/Result.h>
2222

23+
#include <exception>
24+
2325
namespace pulsar {
2426

2527
inline bool isResultRetryable(Result result) {
2628
return result == ResultRetryable || result == ResultDisconnected;
2729
}
2830

31+
class ResultException : public std::exception {
32+
public:
33+
ResultException(Result result) : result_(result) {}
34+
Result result() const noexcept { return result_; }
35+
const char* what() const noexcept { return strResult(result_); }
36+
37+
private:
38+
const Result result_;
39+
};
40+
2941
} // namespace pulsar

tests/AuthPluginTest.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,3 +581,19 @@ TEST(AuthPluginTest, testOauth2Failure) {
581581
ASSERT_EQ(client5.createProducer(topic, producer), ResultAuthenticationError);
582582
client5.close();
583583
}
584+
585+
TEST(AuthPluginTest, testInvalidPlugin) {
586+
Client client("pulsar://localhost:6650", ClientConfiguration{}.setAuth(AuthFactory::create("invalid")));
587+
Producer producer;
588+
ASSERT_EQ(ResultAuthenticationError, client.createProducer("my-topic", producer));
589+
client.close();
590+
}
591+
592+
TEST(AuthPluginTest, testTlsConfigError) {
593+
Client client(serviceUrlTls, ClientConfiguration{}
594+
.setAuth(AuthTls::create(clientPublicKeyPath, clientPrivateKeyPath))
595+
.setTlsTrustCertsFilePath("invalid"));
596+
Producer producer;
597+
ASSERT_EQ(ResultAuthenticationError, client.createProducer("my-topic", producer));
598+
client.close();
599+
}

0 commit comments

Comments
 (0)