polardbxengine/plugin/x/tests/driver/connector/connection_manager.cc

373 lines
13 KiB
C++

/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is also distributed with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have included with MySQL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "plugin/x/tests/driver/connector/connection_manager.h"
#include <sstream>
#include <stdexcept>
#include <utility>
#include <vector>
#include "my_dbug.h"
#include "plugin/x/tests/driver/processor/variable_names.h"
google::protobuf::LogHandler *g_lh = nullptr;
Connection_manager::Connection_manager(const Connection_options &co,
Variable_container *variables,
const Console &console)
: m_default_connection_options(co),
m_variables(variables),
m_console(console) {
g_lh = google::protobuf::SetLogHandler([](google::protobuf::LogLevel level,
const char *filename, int line,
const std::string &message) {
if (g_lh) g_lh(level, filename, line, message);
DBUG_LOG("debug",
"Protobuf error (level:" << level << ", filename:" << filename
<< ":" << line << ", text:" << message);
});
m_variables->make_special_variable(
k_variable_option_user,
new Variable_dynamic_string(m_default_connection_options.user));
m_variables->make_special_variable(
k_variable_option_pass,
new Variable_dynamic_string(m_default_connection_options.password));
m_variables->make_special_variable(
k_variable_option_host,
new Variable_dynamic_string(m_default_connection_options.host));
m_variables->make_special_variable(
k_variable_option_socket,
new Variable_dynamic_string(m_default_connection_options.socket));
m_variables->make_special_variable(
k_variable_option_schema,
new Variable_dynamic_string(m_default_connection_options.schema));
m_variables->make_special_variable(
k_variable_option_port,
new Variable_dynamic_int(m_default_connection_options.port));
m_variables->make_special_variable(
k_variable_option_ssl_mode,
new Variable_dynamic_string(m_default_connection_options.ssl_mode));
m_variables->make_special_variable(
k_variable_option_ssl_cipher,
new Variable_dynamic_string(m_default_connection_options.ssl_cipher));
m_variables->make_special_variable(
k_variable_option_tls_version,
new Variable_dynamic_string(m_default_connection_options.allowed_tls));
m_variables->make_special_variable(
k_variable_option_compression_algorithm,
new Variable_dynamic_array_of_strings(
m_default_connection_options.compression_algorithm));
m_variables->make_special_variable(
k_variable_option_compression_server_style,
new Variable_dynamic_array_of_strings(
m_default_connection_options.compression_server_style));
m_variables->make_special_variable(
k_variable_option_compression_client_style,
new Variable_dynamic_array_of_strings(
m_default_connection_options.compression_client_style));
m_active_holder.reset(new Session_holder(xcl::create_session(), m_console,
m_default_connection_options));
m_session_holders[""] = m_active_holder;
}
Connection_manager::~Connection_manager() {
std::vector<std::string> disconnect_following;
for (const auto &connection : m_session_holders) {
if (connection.first != "") {
disconnect_following.push_back(connection.first);
}
}
for (auto &name : disconnect_following) {
safe_close(name);
}
if (m_session_holders.count("") > 0) {
safe_close("");
}
}
void Connection_manager::get_credentials(std::string *ret_user,
std::string *ret_pass) {
assert(ret_user);
assert(ret_pass);
*ret_user = m_default_connection_options.user;
*ret_pass = m_default_connection_options.password;
}
void Connection_manager::safe_close(const std::string &name) {
try {
set_active(name, true);
close_active(true, true);
} catch (const std::exception &) {
} catch (const xcl::XError &) {
}
}
void Connection_manager::connect_default(const bool send_cap_password_expired,
const bool client_interactive,
const bool no_auth,
const bool connect_attrs) {
m_console.print_verbose("Connecting...\n");
auto session = m_active_holder->get_session();
if (send_cap_password_expired) {
session->set_capability(
xcl::XSession::Capability_can_handle_expired_password, true);
}
if (client_interactive) {
session->set_capability(xcl::XSession::Capability_client_interactive, true);
}
if (connect_attrs) {
auto attrs = session->get_connect_attrs();
attrs.emplace_back("program_name", xcl::Argument_value{"mysqlxtest"});
session->set_capability(xcl::XSession::Capability_session_connect_attrs,
attrs, false);
}
xcl::XError error = m_active_holder->connect(no_auth);
if (error) {
// In case of configuration error, lets do safe_close to synchronize
// closing of socket with exist of mysqlxtest (in other case mysqlxtest
// is going to exit and after a while the connection is going to be
// accepted on server side).
if (CR_X_TLS_WRONG_CONFIGURATION != error.error() || no_auth) {
session->get_protocol().get_connection().close();
}
throw error;
}
setup_variables(session);
m_console.print_verbose("Connected client #", session->client_id(), "\n");
}
void Connection_manager::create(const std::string &name,
const std::string &user,
const std::string &password,
const std::string &db,
const std::vector<std::string> &auth_methods,
const bool is_raw_connection) {
if (m_session_holders.count(name))
throw std::runtime_error("a session named " + name + " already exists");
m_console.print_verbose("Connecting...\n");
Connection_options co = m_default_connection_options;
if (!user.empty()) {
co.user = user;
co.password = password;
}
if (!db.empty()) {
co.schema = db;
}
if (!auth_methods.empty()) {
co.auth_methods = auth_methods;
}
auto session = xcl::create_session();
std::shared_ptr<Session_holder> holder{
new Session_holder(std::move(session), m_console, co)};
xcl::XError error = holder->connect(is_raw_connection);
if (error) {
throw error;
}
m_active_holder = holder;
m_session_holders[name] = holder;
m_active_session_name = name;
setup_variables(active_xsession());
m_console.print_verbose("Connected client #", active_xsession()->client_id(),
"\n");
}
void Connection_manager::abort_active() {
if (m_active_holder) {
if (!m_active_session_name.empty())
std::cout << "aborting session " << m_active_session_name << "\n";
/* Close connection first, to stop XSession from executing
Disconnection flow */
active_xconnection()->close();
m_active_holder.reset();
m_session_holders.erase(m_active_session_name);
if (m_active_session_name != "") set_active("");
} else {
throw std::runtime_error("no active session");
}
}
bool Connection_manager::is_default_active() {
return m_active_session_name.empty();
}
void Connection_manager::close_active(const bool shutdown,
const bool be_quiet) {
if (m_active_holder) {
if (m_active_session_name.empty() && !shutdown)
throw std::runtime_error("cannot close default session");
try {
if (!m_active_session_name.empty() && !be_quiet)
m_console.print("closing session ", m_active_session_name, "\n");
if (active_xconnection()->state().is_connected()) {
// send a close message and wait for the corresponding Ok message
active_xprotocol()->send(Mysqlx::Connection::Close());
xcl::XProtocol::Server_message_type_id msgid;
xcl::XError error;
Message_ptr msg{
active_xprotocol()->recv_single_message(&msgid, &error)};
if (error) throw error;
if (!be_quiet) m_console.print(*msg);
if (Mysqlx::ServerMessages::OK != msgid)
throw xcl::XError(CR_COMMANDS_OUT_OF_SYNC,
"Disconnect was expecting Mysqlx.Ok(bye!), but "
"got the one above (one or more calls to -->recv "
"are probably missing)");
std::string text = static_cast<Mysqlx::Ok *>(msg.get())->msg();
if (text != "bye!" && text != "tchau!")
throw xcl::XError(CR_COMMANDS_OUT_OF_SYNC,
"Disconnect was expecting Mysqlx.Ok(bye!), but "
"got the one above (one or more calls to -->recv "
"are probably missing)");
if (!m_default_connection_options.dont_wait_for_disconnect) {
Message_ptr msg{
active_xprotocol()->recv_single_message(&msgid, &error)};
if (!error && !be_quiet) {
m_console.print_error("Was expecting closure but got message:",
*msg);
}
}
active_xconnection()->close();
}
m_session_holders.erase(m_active_session_name);
if (!shutdown) set_active("", be_quiet);
} catch (const std::exception &error) {
active_xconnection()->close();
m_session_holders.erase(m_active_session_name);
if (!shutdown) set_active("", be_quiet);
throw error;
} catch (const xcl::XError &error) {
active_xconnection()->close();
m_session_holders.erase(m_active_session_name);
if (!shutdown) set_active("", be_quiet);
throw error;
}
} else {
if (!shutdown) throw std::runtime_error("no active session");
}
}
void Connection_manager::set_active(const std::string &name,
const bool be_quiet) {
if (m_session_holders.count(name) == 0) {
std::string slist;
for (auto it = m_session_holders.begin(); it != m_session_holders.end();
++it)
slist.append(it->first).append(", ");
if (!slist.empty()) slist.resize(slist.length() - 2);
throw std::runtime_error("no session named '" + name + "': " + slist);
}
m_active_holder = m_session_holders[name];
m_active_session_name = name;
setup_variables(active_xsession());
if (!be_quiet)
m_console.print(
"switched to session ",
(m_active_session_name.empty() ? "default" : m_active_session_name),
"\n");
}
Session_holder &Connection_manager::active_holder() {
if (!m_active_holder) throw std::runtime_error("no active session");
return *m_active_holder;
}
xcl::XSession *Connection_manager::active_xsession() {
if (!m_active_holder) throw std::runtime_error("no active session");
return m_active_holder->get_session();
}
xcl::XProtocol *Connection_manager::active_xprotocol() {
return &active_xsession()->get_protocol();
}
xcl::XConnection *Connection_manager::active_xconnection() {
return &active_xprotocol()->get_connection();
}
uint64_t Connection_manager::active_session_messages_received(
const std::string &message_name) const {
uint64_t result = 0;
m_active_holder->try_get_number_of_received_messages(message_name, &result);
return result;
}
void Connection_manager::setup_variables(xcl::XSession *session) {
auto &connection = session->get_protocol().get_connection();
m_variables->set(k_variable_active_client_id,
std::to_string(session->client_id()));
m_variables->set(k_variable_active_socket_id,
std::to_string(connection.get_socket_fd()));
}