|
20 | 20 | #include <pulsar/Authentication.h> |
21 | 21 | #include <pulsar/Client.h> |
22 | 22 |
|
| 23 | +#include <array> |
23 | 24 | #include <boost/algorithm/string.hpp> |
| 25 | +#include <sstream> |
24 | 26 | #ifdef USE_ASIO |
25 | 27 | #include <asio.hpp> |
26 | 28 | #else |
@@ -305,39 +307,105 @@ TEST(AuthPluginTest, testTlsDetectClientCertSignedByICA) { |
305 | 307 | ASSERT_EQ(ResultOk, res); |
306 | 308 | } |
307 | 309 |
|
| 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 | + |
308 | 327 | namespace testAthenz { |
309 | 328 | 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 | + |
310 | 371 | void mockZTS(Latch& latch, int port) { |
311 | 372 | LOG_INFO("-- MockZTS started"); |
312 | 373 | ASIO::io_service io; |
313 | | - ASIO::ip::tcp::iostream stream; |
314 | 374 | ASIO::ip::tcp::acceptor acceptor(io, ASIO::ip::tcp::endpoint(ASIO::ip::tcp::v4(), port)); |
315 | 375 |
|
316 | 376 | LOG_INFO("-- MockZTS waiting for connnection"); |
317 | 377 | latch.countdown(); |
318 | | - acceptor.accept(*stream.rdbuf()); |
| 378 | + ASIO::ip::tcp::socket socket(io); |
| 379 | + acceptor.accept(socket); |
319 | 380 | LOG_INFO("-- MockZTS got connection"); |
320 | 381 |
|
321 | 382 | 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, ' '); |
325 | 389 | if (kv[0] == "Athenz-Principal-Auth:") { |
326 | 390 | principalToken = kv[1]; |
327 | 391 | } |
328 | 392 |
|
329 | | - if (headerLine == "\r" || headerLine == "\n" || headerLine == "\r\n") { |
| 393 | + if (headerLine == "\r") { |
| 394 | + std::ostringstream stream; |
330 | 395 | std::string mockToken = "{\"token\":\"mockToken\",\"expiryTime\":4133980800}"; |
331 | 396 | stream << "HTTP/1.1 200 OK" << '\n'; |
332 | 397 | stream << "Host: localhost" << '\n'; |
333 | 398 | stream << "Content-Type: application/json" << '\n'; |
334 | 399 | stream << "Content-Length: " << mockToken.size() << '\n'; |
335 | 400 | stream << '\n'; |
336 | 401 | stream << mockToken << '\n'; |
| 402 | + auto response = stream.str(); |
| 403 | + socket.send(ASIO::const_buffer(response.c_str(), response.length())); |
337 | 404 | break; |
338 | 405 | } |
339 | 406 | } |
340 | 407 |
|
| 408 | + socket.close(); |
341 | 409 | acceptor.close(); |
342 | 410 | LOG_INFO("-- MockZTS exiting"); |
343 | 411 | } |
@@ -366,11 +434,9 @@ TEST(AuthPluginTest, testAthenz) { |
366 | 434 | ASSERT_EQ(data->getHttpHeaders(), "Athenz-Role-Auth: mockToken"); |
367 | 435 | ASSERT_EQ(data->getCommandData(), "mockToken"); |
368 | 436 | 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, ';'); |
371 | 438 | 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, '='); |
374 | 440 | if (kv[0] == "d") { |
375 | 441 | ASSERT_EQ(kv[1], "pulsar.test.tenant"); |
376 | 442 | } else if (kv[0] == "n") { |
@@ -441,11 +507,9 @@ TEST(AuthPluginTest, testAuthFactoryAthenz) { |
441 | 507 | zts.join(); |
442 | 508 | LOG_INFO("Done zts.join()"); |
443 | 509 |
|
444 | | - std::vector<std::string> kvs; |
445 | | - boost::algorithm::split(kvs, testAthenz::principalToken, boost::is_any_of(";")); |
| 510 | + auto kvs = split(testAthenz::principalToken, ';'); |
446 | 511 | 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, '='); |
449 | 513 | if (kv[0] == "d") { |
450 | 514 | ASSERT_EQ(kv[1], "pulsar.test2.tenant"); |
451 | 515 | } else if (kv[0] == "n") { |
|
0 commit comments