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