1 /*
2 Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <gtest/gtest_prod.h> // must be the first header
26
27 #include <stdexcept>
28
29 #include "common.h"
30 #include "mysql/harness/loader.h"
31 #include "mysql/harness/net_ts/impl/socket.h"
32 #include "mysql/harness/stdx/expected.h"
33 #include "mysql_routing.h"
34 #include "mysql_routing_common.h"
35 #include "mysqlrouter/routing.h"
36 #include "protocol/classic_protocol.h"
37 #include "routing_mocks.h"
38 #include "socket_operations.h"
39 #include "tcp_port_pool.h"
40 #include "test/helpers.h"
41
42 #ifdef _WIN32
43 #include <windows.h>
44 #include <winsock2.h>
45 #include <ws2tcpip.h>
46 #else
47 #include <fcntl.h>
48 #include <sys/socket.h>
49 #include <sys/un.h>
50 #endif
51
52 #include "mysql/harness/net_ts/impl/socket.h"
53
54 using mysql_harness::TCPAddress;
55 using routing::AccessMode;
56
57 using ::testing::_;
58 using ::testing::ContainerEq;
59 using ::testing::Eq;
60 using ::testing::Gt;
61 using ::testing::InSequence;
62 using ::testing::Ne;
63 using ::testing::Return;
64 using ::testing::StrEq;
65
66 class RoutingTests : public ::testing::Test {
67 protected:
68 MockRoutingSockOps routing_sock_ops;
69 MockSocketOperations &socket_op = *routing_sock_ops.so();
70 };
71
TEST_F(RoutingTests,AccessModes)72 TEST_F(RoutingTests, AccessModes) {
73 ASSERT_EQ(static_cast<int>(AccessMode::kReadWrite), 1);
74 ASSERT_EQ(static_cast<int>(AccessMode::kReadOnly), 2);
75 }
76
TEST_F(RoutingTests,AccessModeLiteralNames)77 TEST_F(RoutingTests, AccessModeLiteralNames) {
78 using routing::get_access_mode;
79 ASSERT_THAT(get_access_mode("read-write"), Eq(AccessMode::kReadWrite));
80 ASSERT_THAT(get_access_mode("read-only"), Eq(AccessMode::kReadOnly));
81 }
82
TEST_F(RoutingTests,GetAccessLiteralName)83 TEST_F(RoutingTests, GetAccessLiteralName) {
84 using routing::get_access_mode_name;
85 ASSERT_THAT(get_access_mode_name(AccessMode::kReadWrite),
86 StrEq("read-write"));
87 ASSERT_THAT(get_access_mode_name(AccessMode::kReadOnly), StrEq("read-only"));
88 }
89
TEST_F(RoutingTests,Defaults)90 TEST_F(RoutingTests, Defaults) {
91 ASSERT_EQ(routing::kDefaultWaitTimeout, 0);
92 ASSERT_EQ(routing::kDefaultMaxConnections, 512);
93 ASSERT_EQ(routing::kDefaultDestinationConnectionTimeout,
94 std::chrono::seconds(1));
95 ASSERT_EQ(routing::kDefaultBindAddress, "127.0.0.1");
96 ASSERT_EQ(routing::kDefaultNetBufferLength, 16384U);
97 ASSERT_EQ(routing::kDefaultMaxConnectErrors, 100ULL);
98 ASSERT_EQ(routing::kDefaultClientConnectTimeout, std::chrono::seconds(9));
99 }
100
101 #ifndef _WIN32
102 // No way to read nonblocking status in Windows
TEST_F(RoutingTests,SetSocketBlocking)103 TEST_F(RoutingTests, SetSocketBlocking) {
104 int s = socket(PF_INET, SOCK_STREAM, 6);
105 auto so = mysql_harness::SocketOperations::instance();
106 ASSERT_EQ(fcntl(s, F_GETFL, nullptr) & O_NONBLOCK, 0);
107 so->set_socket_blocking(s, false);
108 ASSERT_EQ(fcntl(s, F_GETFL, nullptr) & O_NONBLOCK, O_NONBLOCK);
109 so->set_socket_blocking(s, true);
110 ASSERT_EQ(fcntl(s, F_GETFL, nullptr) & O_NONBLOCK, 0) << std::endl;
111
112 fcntl(s, F_SETFL, O_RDONLY);
113 so->set_socket_blocking(s, false);
114 ASSERT_EQ(fcntl(s, F_GETFL, nullptr) & O_NONBLOCK, O_NONBLOCK);
115 ASSERT_EQ(fcntl(s, F_GETFL, nullptr) & O_RDONLY, O_RDONLY);
116 }
117 #endif
118
TEST_F(RoutingTests,CopyPacketsSingleWrite)119 TEST_F(RoutingTests, CopyPacketsSingleWrite) {
120 int sender_socket = 1, receiver_socket = 2;
121 RoutingProtocolBuffer buffer(500);
122 int curr_pktnr = 100;
123 bool handshake_done = true;
124
125 EXPECT_CALL(socket_op, read(sender_socket, &buffer[0], buffer.size()))
126 .WillOnce(Return(200));
127 EXPECT_CALL(socket_op, write(receiver_socket, &buffer[0], 200))
128 .WillOnce(Return(200));
129
130 ClassicProtocol cp(&routing_sock_ops);
131 const auto copy_res = cp.copy_packets(sender_socket, receiver_socket,
132 true /* sender is writable */, buffer,
133 &curr_pktnr, handshake_done, false);
134
135 ASSERT_TRUE(copy_res);
136 EXPECT_EQ(200u, copy_res.value());
137 }
138
TEST_F(RoutingTests,CopyPacketsMultipleWrites)139 TEST_F(RoutingTests, CopyPacketsMultipleWrites) {
140 int sender_socket = 1, receiver_socket = 2;
141 RoutingProtocolBuffer buffer(500);
142 int curr_pktnr = 100;
143 bool handshake_done = true;
144
145 InSequence seq;
146
147 EXPECT_CALL(socket_op, read(sender_socket, &buffer[0], buffer.size()))
148 .WillOnce(Return(200));
149
150 // first write does not write everything
151 EXPECT_CALL(socket_op, write(receiver_socket, &buffer[0], 200))
152 .WillOnce(Return(100));
153 // second does not do anything (which is not treated as an error
154 EXPECT_CALL(socket_op, write(receiver_socket, &buffer[100], 100))
155 .WillOnce(Return(0));
156 // third writes the remaining chunk
157 EXPECT_CALL(socket_op, write(receiver_socket, &buffer[100], 100))
158 .WillOnce(Return(100));
159
160 ClassicProtocol cp(&routing_sock_ops);
161 const auto copy_res =
162 cp.copy_packets(sender_socket, receiver_socket, true, buffer, &curr_pktnr,
163 handshake_done, false);
164
165 ASSERT_TRUE(copy_res);
166 EXPECT_EQ(200u, copy_res.value());
167 }
168
TEST_F(RoutingTests,CopyPacketsWriteError)169 TEST_F(RoutingTests, CopyPacketsWriteError) {
170 int sender_socket = 1, receiver_socket = 2;
171 RoutingProtocolBuffer buffer(500);
172 int curr_pktnr = 100;
173 bool handshake_done = true;
174
175 EXPECT_CALL(socket_op, read(sender_socket, &buffer[0], buffer.size()))
176 .WillOnce(Return(200));
177 EXPECT_CALL(socket_op, write(receiver_socket, &buffer[0], 200))
178 .WillOnce(Return(
179 stdx::make_unexpected(make_error_code(std::errc::connection_reset))));
180
181 ClassicProtocol cp(&routing_sock_ops);
182 // will log "Write error: ..." as we don't mock an errno
183 const auto copy_res =
184 cp.copy_packets(sender_socket, receiver_socket, true, buffer, &curr_pktnr,
185 handshake_done, false);
186
187 ASSERT_FALSE(copy_res);
188 }
189
190 #ifndef _WIN32 // [_HERE_]
191
192 // a valid Connection::Close xprotocol message
193 #define kByeMessage "\x01\x00\x00\x00\x03"
194
195 class MockServer {
196 public:
MockServer(uint16_t port)197 MockServer(uint16_t port) {
198 socket_operations_ = mysql_harness::SocketOperations::instance();
199
200 const auto socket_res = net::impl::socket::socket(AF_INET, SOCK_STREAM, 0);
201 if (!socket_res) {
202 throw std::system_error(socket_res.error());
203 }
204
205 service_tcp_ = socket_res.value();
206
207 #ifndef _WIN32
208 int option_value{1};
209 const auto sockopt_res =
210 net::impl::socket::setsockopt(service_tcp_, SOL_SOCKET, SO_REUSEADDR,
211 &option_value, sizeof(option_value));
212 if (!sockopt_res) {
213 net::impl::socket::close(service_tcp_);
214 throw std::system_error(sockopt_res.error());
215 }
216 #endif
217
218 struct sockaddr_in addr;
219 memset(&addr, 0, sizeof(addr));
220 addr.sin_family = AF_INET;
221 addr.sin_addr.s_addr = INADDR_ANY;
222 addr.sin_port = htons(port);
223
224 const auto bind_res = net::impl::socket::bind(
225 service_tcp_, (const struct sockaddr *)&addr, sizeof addr);
226 if (!bind_res) {
227 net::impl::socket::close(service_tcp_);
228
229 throw std::system_error(bind_res.error());
230 }
231
232 const auto listen_res = net::impl::socket::listen(service_tcp_, 20);
233 if (!listen_res) {
234 net::impl::socket::close(service_tcp_);
235
236 throw std::system_error(
237 listen_res.error(),
238 "Failed to start listening for connections using TCP");
239 }
240 }
241
~MockServer()242 ~MockServer() { stop(); }
243
start()244 void start() {
245 stop_ = false;
246 thread_ = std::thread(&MockServer::runloop, this);
247 }
248
stop()249 void stop() {
250 if (!stop_) {
251 stop_ = true;
252 socket_operations_->shutdown(service_tcp_);
253 socket_operations_->close(service_tcp_);
254 thread_.join();
255 }
256 }
257
stop_after_n_accepts(int c)258 void stop_after_n_accepts(int c) { max_expected_accepts_ = c; }
259
runloop()260 void runloop() {
261 mysql_harness::rename_thread("runloop()");
262 std::vector<std::thread> client_threads;
263
264 while (!stop_ && (max_expected_accepts_ == 0 ||
265 num_accepts_ < max_expected_accepts_)) {
266 struct sockaddr_in6 client_addr;
267 socklen_t sin_size = sizeof client_addr;
268
269 const auto sock_client_res = net::impl::socket::accept(
270 service_tcp_, (struct sockaddr *)&client_addr, &sin_size);
271 if (!sock_client_res) {
272 std::cout << sock_client_res.error().message() << " ERROR\n";
273 continue;
274 }
275 const auto sock_client = sock_client_res.value();
276 num_accepts_++;
277 client_threads.emplace_back(
278 std::thread([this, sock_client]() { new_client(sock_client); }));
279 }
280
281 // wait for all threads to shut down again
282 for (auto &thr : client_threads) {
283 thr.join();
284 }
285 }
286
new_client(int sock)287 void new_client(int sock) {
288 mysql_harness::rename_thread("new_client()");
289 num_connections_++;
290 char buf[sizeof(kByeMessage)];
291 // block until we receive the bye msg
292 if (read(sock, buf, sizeof(buf)) < 0) {
293 FAIL() << "Unexpected results from read(): "
294 << mysql_harness::get_strerror(errno);
295 }
296 socket_operations_->close(sock);
297 num_connections_--;
298 }
299
300 public:
301 std::atomic_int num_connections_{0};
302 std::atomic_int num_accepts_{0};
303 std::atomic_int max_expected_accepts_{0};
304
305 private:
306 mysql_harness::SocketOperationsBase *socket_operations_;
307 std::thread thread_;
308 mysql_harness::socket_t service_tcp_{mysql_harness::kInvalidSocket};
309 std::atomic_bool stop_;
310 };
311
connect_local(uint16_t port)312 static stdx::expected<mysql_harness::socket_t, std::error_code> connect_local(
313 uint16_t port) {
314 return routing::RoutingSockOps::instance(
315 mysql_harness::SocketOperations::instance())
316 ->get_mysql_socket(TCPAddress("127.0.0.1", port),
317 std::chrono::milliseconds(100), true);
318 }
319
disconnect(int sock)320 static void disconnect(int sock) {
321 if (write(sock, kByeMessage, sizeof(kByeMessage)) < 0)
322 std::cout << "write(xproto-connection-close) returned error\n";
323
324 mysql_harness::SocketOperations::instance()->close(sock);
325 }
326
327 #ifndef _WIN32
connect_socket(const char * path)328 static int connect_socket(const char *path) {
329 const auto socket_res = net::impl::socket::socket(AF_UNIX, SOCK_STREAM, 0);
330 if (!socket_res) {
331 throw std::system_error(socket_res.error());
332 }
333
334 struct sockaddr_un addr;
335 memset(&addr, 0, sizeof(addr));
336 addr.sun_family = AF_UNIX;
337 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
338
339 const auto fd = socket_res.value();
340
341 const auto connect_res =
342 net::impl::socket::connect(fd, (struct sockaddr *)&addr, sizeof(addr));
343 if (!connect_res) {
344 throw std::system_error(connect_res.error());
345 }
346
347 return fd;
348 }
349 #endif
350
call_until(std::function<bool ()> f,int timeout=2)351 static bool call_until(std::function<bool()> f, int timeout = 2) {
352 time_t start = time(nullptr);
353 while (time(nullptr) - start < timeout) {
354 if (f()) return true;
355
356 // wait a bit and let other threads run
357 std::this_thread::sleep_for(std::chrono::milliseconds(1));
358 }
359 return false;
360 }
361
362 // Bug#24841281 NOT ABLE TO CONNECT ANY CLIENTS WHEN ROUTER IS CONFIGURED WITH
363 // SOCKETS OPTION
TEST_F(RoutingTests,bug_24841281)364 TEST_F(RoutingTests, bug_24841281) {
365 mysql_harness::rename_thread("TEST_F()");
366
367 TcpPortPool port_pool_;
368
369 const uint16_t server_port = port_pool_.get_next_available();
370 const uint16_t router_port = port_pool_.get_next_available();
371
372 MockServer server(server_port);
373 server.start();
374
375 TmpDir tmp_dir; // create a tmp dir (it will be destroyed via RAII later)
376 std::string sock_path = tmp_dir() + "/sock";
377
378 // check that connecting to a TCP socket or a UNIX socket works
379 MySQLRouting routing(
380 routing::RoutingStrategy::kNextAvailable, router_port,
381 Protocol::Type::kXProtocol, routing::AccessMode::kReadWrite, "0.0.0.0",
382 mysql_harness::Path(sock_path), "routing:testroute",
383 routing::kDefaultMaxConnections,
384 routing::kDefaultDestinationConnectionTimeout,
385 routing::kDefaultMaxConnectErrors, routing::kDefaultClientConnectTimeout,
386 routing::kDefaultNetBufferLength);
387 routing.set_destinations_from_csv("127.0.0.1:" + std::to_string(server_port));
388 mysql_harness::PluginFuncEnv env(nullptr, nullptr, true);
389 std::thread thd(&MySQLRouting::start, &routing, &env);
390
391 // set the number of accepts that the server should expect for before stopping
392 #ifdef _WIN32
393 server.stop_after_n_accepts(4);
394 #else
395 server.stop_after_n_accepts(6);
396 #endif
397
398 EXPECT_EQ(routing.get_context().info_active_routes_.load(), 0);
399
400 // open connections to the socket and see if we get a matching outgoing
401 // socket connection attempt to our mock server
402
403 stdx::expected<mysql_harness::socket_t, std::error_code> sock1_res;
404 // router is running in a thread, so we need to sync it
405 EXPECT_TRUE(call_until([&]() -> bool {
406 sock1_res = connect_local(router_port);
407 return sock1_res.has_value();
408 })) << "timed out connecting to router_port";
409 auto sock2_res = connect_local(router_port);
410
411 ASSERT_TRUE(sock1_res);
412 ASSERT_TRUE(sock2_res);
413
414 auto sock1 = sock1_res.value();
415 auto sock2 = sock2_res.value();
416
417 EXPECT_TRUE(call_until([&server]() -> bool {
418 return server.num_connections_.load() == 2;
419 })) << "timed out, got "
420 << server.num_connections_.load() << " connections";
421 EXPECT_TRUE(call_until([&routing]() -> bool {
422 return routing.get_context().info_active_routes_.load() == 2;
423 })) << "timed out, got "
424 << routing.get_context().info_active_routes_.load() << " active routes";
425
426 disconnect(sock1);
427
428 EXPECT_TRUE(call_until([&routing]() -> bool {
429 return routing.get_context().info_active_routes_.load() == 1;
430 })) << "timed out, got "
431 << routing.get_context().info_active_routes_.load() << " active routes";
432
433 {
434 auto sock11_res = connect_local(router_port);
435 auto sock12_res = connect_local(router_port);
436
437 ASSERT_TRUE(sock11_res);
438 ASSERT_TRUE(sock12_res);
439
440 auto sock11 = sock11_res.value();
441 auto sock12 = sock12_res.value();
442
443 EXPECT_TRUE(call_until([&server]() -> bool {
444 return server.num_connections_.load() == 3;
445 })) << "timed out: "
446 << server.num_connections_.load();
447
448 call_until([&routing]() -> bool {
449 return routing.get_context().info_active_routes_.load() == 3;
450 });
451 EXPECT_EQ(3, routing.get_context().info_active_routes_.load());
452
453 disconnect(sock11);
454 call_until([&routing]() -> bool {
455 return routing.get_context().info_active_routes_.load() == 2;
456 });
457 EXPECT_EQ(2, routing.get_context().info_active_routes_.load());
458
459 disconnect(sock12);
460 call_until([&routing]() -> bool {
461 return routing.get_context().info_active_routes_.load() == 1;
462 });
463 EXPECT_EQ(1, routing.get_context().info_active_routes_.load());
464
465 call_until(
466 [&server]() -> bool { return server.num_connections_.load() == 1; });
467 EXPECT_EQ(1, server.num_connections_.load());
468 }
469
470 disconnect(sock2);
471 call_until([&routing]() -> bool {
472 return routing.get_context().info_active_routes_.load() == 0;
473 });
474 EXPECT_EQ(0, routing.get_context().info_active_routes_.load());
475
476 #ifndef _WIN32
477 // now try the same with socket ops
478 int sock3 = connect_socket(sock_path.c_str());
479 int sock4 = connect_socket(sock_path.c_str());
480
481 EXPECT_THAT(sock3, Ne(-1));
482 EXPECT_THAT(sock4, Ne(-1));
483
484 call_until(
485 [&server]() -> bool { return server.num_connections_.load() == 2; });
486 EXPECT_EQ(2, server.num_connections_.load());
487
488 call_until([&routing]() -> bool {
489 return routing.get_context().info_active_routes_.load() == 2;
490 });
491 EXPECT_EQ(2, routing.get_context().info_active_routes_.load());
492
493 disconnect(sock3);
494 call_until([&routing]() -> bool {
495 return routing.get_context().info_active_routes_.load() == 1;
496 });
497 EXPECT_EQ(1, routing.get_context().info_active_routes_.load());
498
499 disconnect(sock4);
500 call_until([&routing]() -> bool {
501 return routing.get_context().info_active_routes_.load() == 0;
502 });
503 EXPECT_EQ(0, routing.get_context().info_active_routes_.load());
504 #endif
505 env.clear_running(); // shut down MySQLRouting
506 server.stop();
507 thd.join();
508 }
509
TEST_F(RoutingTests,set_destinations_from_uri)510 TEST_F(RoutingTests, set_destinations_from_uri) {
511 MySQLRouting routing(routing::RoutingStrategy::kFirstAvailable, 7001,
512 Protocol::Type::kXProtocol);
513
514 // valid metadata-cache uri
515 {
516 URI uri("metadata-cache://test/default?role=PRIMARY");
517 EXPECT_NO_THROW(routing.set_destinations_from_uri(uri));
518 }
519
520 // metadata-cache uri, role missing
521 {
522 URI uri("metadata-cache://test/default");
523 try {
524 routing.set_destinations_from_uri(uri);
525 FAIL() << "Expected std::runtime_error exception";
526 } catch (const std::runtime_error &err) {
527 EXPECT_EQ(
528 err.what(),
529 std::string("Missing 'role' in routing destination specification"));
530 } catch (...) {
531 FAIL() << "Expected std::runtime_error exception";
532 }
533 }
534
535 // invalid scheme
536 {
537 URI uri("invalid-scheme://test/default?role=SECONDARY");
538 try {
539 routing.set_destinations_from_uri(uri);
540 FAIL() << "Expected std::runtime_error exception";
541 } catch (const std::runtime_error &err) {
542 EXPECT_EQ(err.what(),
543 std::string("Invalid URI scheme; expecting: 'metadata-cache' "
544 "is: 'invalid-scheme'"));
545 } catch (...) {
546 FAIL() << "Expected std::runtime_error exception";
547 }
548 }
549 }
550
TEST_F(RoutingTests,set_destinations_from_cvs)551 TEST_F(RoutingTests, set_destinations_from_cvs) {
552 MySQLRouting routing(routing::RoutingStrategy::kNextAvailable, 7001,
553 Protocol::Type::kXProtocol);
554
555 // valid address list
556 {
557 const std::string cvs = "127.0.0.1:2002,127.0.0.1:2004";
558 EXPECT_NO_THROW(routing.set_destinations_from_csv(cvs));
559 }
560
561 // no routing strategy, should go with default
562 {
563 MySQLRouting routing_inv(routing::RoutingStrategy::kUndefined, 7001,
564 Protocol::Type::kXProtocol);
565 const std::string csv = "127.0.0.1:2002,127.0.0.1:2004";
566 EXPECT_NO_THROW(routing_inv.set_destinations_from_csv(csv));
567 }
568
569 // no address
570 {
571 const std::string csv = "";
572 EXPECT_THROW(routing.set_destinations_from_csv(csv), std::runtime_error);
573 }
574
575 // invalid address
576 {
577 const std::string csv = "127.0.0.1.2:2222";
578 EXPECT_THROW(routing.set_destinations_from_csv(csv), std::runtime_error);
579 }
580
581 // let's check if the correct defualt port gets chosen for
582 // the respective protocol
583 // we use the trick here setting the expected address also as
584 // the binding address for the routing which should make the method throw
585 // an exception if these are the same
586 {
587 const std::string address = "127.0.0.1";
588 MySQLRouting routing_classic(routing::RoutingStrategy::kNextAvailable, 3306,
589 Protocol::Type::kClassicProtocol,
590 routing::AccessMode::kReadWrite, address);
591 EXPECT_THROW(routing_classic.set_destinations_from_csv("127.0.0.1"),
592 std::runtime_error);
593 EXPECT_THROW(routing_classic.set_destinations_from_csv("127.0.0.1:3306"),
594 std::runtime_error);
595 EXPECT_NO_THROW(
596 routing_classic.set_destinations_from_csv("127.0.0.1:33060"));
597
598 MySQLRouting routing_x(routing::RoutingStrategy::kNextAvailable, 33060,
599 Protocol::Type::kXProtocol,
600 routing::AccessMode::kReadWrite, address);
601 EXPECT_THROW(routing_x.set_destinations_from_csv("127.0.0.1"),
602 std::runtime_error);
603 EXPECT_THROW(routing_x.set_destinations_from_csv("127.0.0.1:33060"),
604 std::runtime_error);
605 EXPECT_NO_THROW(routing_x.set_destinations_from_csv("127.0.0.1:3306"));
606 }
607 }
608
609 #endif // #ifndef _WIN32 [_HERE_]
610
TEST_F(RoutingTests,get_routing_thread_name)611 TEST_F(RoutingTests, get_routing_thread_name) {
612 // config name must begin with "routing" (name of the plugin passed from
613 // configuration file)
614 EXPECT_STREQ(":parse err", get_routing_thread_name("", "").c_str());
615 EXPECT_STREQ(":parse err", get_routing_thread_name("routin", "").c_str());
616 EXPECT_STREQ(":parse err", get_routing_thread_name(" routing", "").c_str());
617 EXPECT_STREQ("pre:parse err", get_routing_thread_name("", "pre").c_str());
618 EXPECT_STREQ("pre:parse err",
619 get_routing_thread_name("routin", "pre").c_str());
620 EXPECT_STREQ("pre:parse err",
621 get_routing_thread_name(" routing", "pre").c_str());
622
623 // normally prefix would never be empty, so the behavior below is not be very
624 // meaningful; it should not crash however
625 EXPECT_STREQ(":", get_routing_thread_name("routing", "").c_str());
626 EXPECT_STREQ(":", get_routing_thread_name("routing:", "").c_str());
627
628 // realistic (but unanticipated) cases - removing everything up to _default_
629 // will fail, in which case we fall back of <prefix>:<everything after
630 // "routing:">, trimmed to 15 chars
631 EXPECT_STREQ(
632 "RtS:test_def_ul",
633 get_routing_thread_name("routing:test_def_ult_x_ro", "RtS").c_str());
634 EXPECT_STREQ(
635 "RtS:test_def_ul",
636 get_routing_thread_name("routing:test_def_ult_ro", "RtS").c_str());
637 EXPECT_STREQ("RtS:", get_routing_thread_name("routing", "RtS").c_str());
638 EXPECT_STREQ("RtS:test_x_ro",
639 get_routing_thread_name("routing:test_x_ro", "RtS").c_str());
640 EXPECT_STREQ("RtS:test_ro",
641 get_routing_thread_name("routing:test_ro", "RtS").c_str());
642
643 // real cases
644 EXPECT_STREQ(
645 "RtS:x_ro",
646 get_routing_thread_name("routing:test_default_x_ro", "RtS").c_str());
647 EXPECT_STREQ(
648 "RtS:ro",
649 get_routing_thread_name("routing:test_default_ro", "RtS").c_str());
650 EXPECT_STREQ("RtS:", get_routing_thread_name("routing", "RtS").c_str());
651 }
652
653 /*
654 * @test This test verifies fix for Bug 23857183 and checks if trying to connect
655 * to wrong port fails immediately not via timeout
656 *
657 * @todo (jan) disabled the test as the result is unpredictable as port may be
658 * in use, IP may be or not be bound, ... The test needs to be rewritten and
659 * have predictable output, or be removed.
660 */
TEST_F(RoutingTests,DISABLED_ConnectToServerWrongPort)661 TEST_F(RoutingTests, DISABLED_ConnectToServerWrongPort) {
662 const std::chrono::seconds TIMEOUT{4};
663
664 // wrong port number
665 {
666 TCPAddress address("127.0.0.1", 10888);
667 auto server_res = routing::RoutingSockOps::instance(
668 mysql_harness::SocketOperations::instance())
669 ->get_mysql_socket(address, TIMEOUT);
670 // should return -1, -2 is timeout expired which is not what we expect when
671 // connecting with the wrong port
672 ASSERT_FALSE(server_res);
673 }
674
675 // in darwin and solaris, attempting connection to 127.0.0.11 will fail by
676 // timeout
677 #if !defined(__APPLE__) && !defined(__sun)
678 // wrong port number and IP
679 {
680 TCPAddress address("127.0.0.11", 10888);
681 auto server_res = routing::RoutingSockOps::instance(
682 mysql_harness::SocketOperations::instance())
683 ->get_mysql_socket(address, TIMEOUT);
684 // should return -1, -2 is timeout expired which is not what we expect when
685 // connecting with the wrong port
686 ASSERT_FALSE(server_res);
687 }
688 #endif
689 }
690
main(int argc,char * argv[])691 int main(int argc, char *argv[]) {
692 init_test_logger();
693 ::testing::InitGoogleTest(&argc, argv);
694 return RUN_ALL_TESTS();
695 }
696