Skip to content

Commit b57c81a

Browse files
committed
Fix clang-tidy failures caused by boost::algorithm::split and asio::ip::tcp::iostream
1 parent e310f53 commit b57c81a

File tree

1 file changed

+78
-14
lines changed

1 file changed

+78
-14
lines changed

tests/AuthPluginTest.cc

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include <pulsar/Authentication.h>
2121
#include <pulsar/Client.h>
2222

23+
#include <array>
2324
#include <boost/algorithm/string.hpp>
25+
#include <sstream>
2426
#ifdef USE_ASIO
2527
#include <asio.hpp>
2628
#else
@@ -305,39 +307,105 @@ TEST(AuthPluginTest, testTlsDetectClientCertSignedByICA) {
305307
ASSERT_EQ(ResultOk, res);
306308
}
307309

310+
// There is a bug that clang-tidy could report memory leak for boost::algorithm::split, see
311+
// https://github.com/boostorg/algorithm/issues/63.
312+
static std::vector<std::string> split(const std::string& s, char separator) {
313+
std::vector<std::string> tokens;
314+
size_t startPos = 0;
315+
while (startPos < s.size()) {
316+
auto pos = s.find(separator, startPos);
317+
if (pos == std::string::npos) {
318+
tokens.emplace_back(s.substr(startPos));
319+
break;
320+
}
321+
tokens.emplace_back(s.substr(startPos, pos - startPos));
322+
startPos = pos + 1;
323+
}
324+
return tokens;
325+
}
326+
308327
namespace testAthenz {
309328
std::string principalToken;
329+
330+
// ASIO::ip::tcp::iostream could call a virtual function during destruction, so the clang-tidy will fail by
331+
// clang-analyzer-optin.cplusplus.VirtualCall. Here we write a simple stream to read lines from socket.
332+
class SocketStream {
333+
public:
334+
SocketStream(ASIO::ip::tcp::socket& socket) : socket_(socket) {}
335+
336+
bool getline(std::string& line) {
337+
auto pos = buffer_.find('\n', bufferPos_);
338+
if (pos != std::string::npos) {
339+
line = buffer_.substr(bufferPos_, pos - bufferPos_);
340+
bufferPos_ = pos + 1;
341+
return true;
342+
}
343+
344+
std::array<char, 1024> buffer;
345+
ASIO_ERROR error;
346+
auto length = socket_.read_some(ASIO::buffer(buffer.data(), buffer.size()), error);
347+
if (error == ASIO::error::eof) {
348+
return false;
349+
} else if (error) {
350+
LOG_ERROR("Failed to read from socket: " << error.message());
351+
return false;
352+
}
353+
buffer_.append(buffer.data(), length);
354+
355+
pos = buffer_.find('\n', bufferPos_);
356+
if (pos != std::string::npos) {
357+
line = buffer_.substr(bufferPos_, pos - bufferPos_);
358+
bufferPos_ = pos + 1;
359+
} else {
360+
line = "";
361+
}
362+
return true;
363+
}
364+
365+
private:
366+
ASIO::ip::tcp::socket& socket_;
367+
std::string buffer_;
368+
size_t bufferPos_{0};
369+
};
370+
310371
void mockZTS(Latch& latch, int port) {
311372
LOG_INFO("-- MockZTS started");
312373
ASIO::io_service io;
313-
ASIO::ip::tcp::iostream stream;
314374
ASIO::ip::tcp::acceptor acceptor(io, ASIO::ip::tcp::endpoint(ASIO::ip::tcp::v4(), port));
315375

316376
LOG_INFO("-- MockZTS waiting for connnection");
317377
latch.countdown();
318-
acceptor.accept(*stream.rdbuf());
378+
ASIO::ip::tcp::socket socket(io);
379+
acceptor.accept(socket);
319380
LOG_INFO("-- MockZTS got connection");
320381

321382
std::string headerLine;
322-
while (getline(stream, headerLine)) {
323-
std::vector<std::string> kv;
324-
boost::algorithm::split(kv, headerLine, boost::is_any_of(" "));
383+
SocketStream stream(socket);
384+
while (stream.getline(headerLine)) {
385+
if (headerLine.empty()) {
386+
continue;
387+
}
388+
auto kv = split(headerLine, ' ');
325389
if (kv[0] == "Athenz-Principal-Auth:") {
326390
principalToken = kv[1];
327391
}
328392

329-
if (headerLine == "\r" || headerLine == "\n" || headerLine == "\r\n") {
393+
if (headerLine == "\r") {
394+
std::ostringstream stream;
330395
std::string mockToken = "{\"token\":\"mockToken\",\"expiryTime\":4133980800}";
331396
stream << "HTTP/1.1 200 OK" << '\n';
332397
stream << "Host: localhost" << '\n';
333398
stream << "Content-Type: application/json" << '\n';
334399
stream << "Content-Length: " << mockToken.size() << '\n';
335400
stream << '\n';
336401
stream << mockToken << '\n';
402+
auto response = stream.str();
403+
socket.send(ASIO::const_buffer(response.c_str(), response.length()));
337404
break;
338405
}
339406
}
340407

408+
socket.close();
341409
acceptor.close();
342410
LOG_INFO("-- MockZTS exiting");
343411
}
@@ -366,11 +434,9 @@ TEST(AuthPluginTest, testAthenz) {
366434
ASSERT_EQ(data->getHttpHeaders(), "Athenz-Role-Auth: mockToken");
367435
ASSERT_EQ(data->getCommandData(), "mockToken");
368436
zts.join();
369-
std::vector<std::string> kvs;
370-
boost::algorithm::split(kvs, testAthenz::principalToken, boost::is_any_of(";"));
437+
auto kvs = split(testAthenz::principalToken, ';');
371438
for (std::vector<std::string>::iterator itr = kvs.begin(); itr != kvs.end(); itr++) {
372-
std::vector<std::string> kv;
373-
boost::algorithm::split(kv, *itr, boost::is_any_of("="));
439+
auto kv = split(*itr, '=');
374440
if (kv[0] == "d") {
375441
ASSERT_EQ(kv[1], "pulsar.test.tenant");
376442
} else if (kv[0] == "n") {
@@ -441,11 +507,9 @@ TEST(AuthPluginTest, testAuthFactoryAthenz) {
441507
zts.join();
442508
LOG_INFO("Done zts.join()");
443509

444-
std::vector<std::string> kvs;
445-
boost::algorithm::split(kvs, testAthenz::principalToken, boost::is_any_of(";"));
510+
auto kvs = split(testAthenz::principalToken, ';');
446511
for (std::vector<std::string>::iterator itr = kvs.begin(); itr != kvs.end(); itr++) {
447-
std::vector<std::string> kv;
448-
boost::algorithm::split(kv, *itr, boost::is_any_of("="));
512+
auto kv = split(*itr, '=');
449513
if (kv[0] == "d") {
450514
ASSERT_EQ(kv[1], "pulsar.test2.tenant");
451515
} else if (kv[0] == "n") {

0 commit comments

Comments
 (0)