...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
This section discusses several aspects regarding the creation, establishment and termination of client connections.
connect_params::username
and connect_params::password
contain the credentials used during authentication. The password is sent
to the server either hashed or over a secure channel such as TLS, as mandated
by the protocol.
MySQL implements several authentication plugins that can be used to authenticate
a user (see the pluggable
authentication MySQL docs). Each MySQL user is associated to a single
authentication plugin, specified during using creation. Additionally, servers
define a default authentication plugin (see authentication_policy
and default_authentication_plugin
).
The default plugin will be used for newly created users, and may affect how
the handshake works.
This library implements the two most common authentication plugins:
mysql_native_password
. Unless
otherwise configured, this is the default plugin for MySQL 5.7 and MariaDB.
It can be used over both TLS and plaintext connections. It sends the
password hashed, salted by a nonce.
caching_sha2_password
. This
is the default plugin for MySQL 8.0+. It can only be used over secure
transports, like TCP with TLS or UNIX sockets.
Multi-factor authentication is not yet supported. If you require support for a plugin not listed above or for MFA, please file a feature request against the GitHub repository.
![]() |
Note |
---|---|
Servers configured with a default authentication plugin not implemented in Boost.MySQL are not supported, regardless of the actual plugin the concrete user employs. This limitation may be lifted in the future. |
connect_params::database
contains the database name to connect to. If you specify it, your connection
will default to use that database, as if you had issued a USE
statement. You can leave it
blank to select no database. You can always issue a USE
statement using async_execute
to select a different database after establishing the connection.
TLS encrypted connections are fully supported by Boost.MySQL. TCP connections
established using any_connection
use TLS by default.
The TLS handshake is performed by any_connection::async_connect
.
This contrasts with libraries like Boost.Beast,
where the TLS handshake must be explicitly invoked by the user. We selected
this approach because the TLS handshake is part of the MySQL protocol's handshake:
the client and server exchange several unencrypted messages, then perform
the TLS handshake and continue exchanging encrypted messages, until the connection
either succeeds or fails. This scheme enables the TLS negotiation feature
(see below for more info).
If the TLS handshake fails, the entire async_connect
operation will also fail.
TLS shutdown is performed by any_connection::async_close
.
MySQL doesn't always close TLS connections gracefully, so errors generated
by the TLS shutdown are ignored.
During connection establishment, client and server negotiate whether to use
TLS or not. Boost.MySQL supports such negotiation using connect_params::ssl
.
This is a ssl_mode
enum with the following options:
ssl_mode::enable
will make the connection use
TLS if the server supports it, falling back to a plaintext connection
otherwise. This is the default for
any_connection
when using TCP.
ssl_mode::require
ensures that the connection
uses TLS. If the server does not support it, async_connect
fails.
ssl_mode::disable
unconditionally disables TLS.
UNIX sockets are considered secure channels
and never use TLS. When connecting using
a UNIX socket, connect_params::ssl
is ignored.
After a successful connection establishment, you can use any_connection::uses_ssl
to query whether the connection is encrypted or not.
As mentioned above, setting connect_params::ssl
to ssl_mode::disable
disables TLS:
// The server host, username, password and database to use. // Passing ssl_mode::disable will disable the use of TLS. mysql::connect_params params; params.server_address.emplace_host_and_port(std::string(server_hostname)); params.username = std::move(username); params.password = std::move(password); params.database = "boost_mysql_examples"; params.ssl = mysql::ssl_mode::disable;
See the full example here.
You can pass an optional asio::ssl::context
to any_connection
constructor. You can set many TLS parameters doing this, including trusted
CAs, certificate validation callbacks and TLS extensions.
any_connection_params
contains a ssl_context
member that can be used for this. For example, TLS certificate validation
is disabled by default. To enable it:
// Create a SSL context, which contains TLS configuration options asio::ssl::context ssl_ctx(asio::ssl::context::tls_client); // Enable certificate verification. If the server's certificate // is not valid or not signed by a trusted CA, async_connect will error. ssl_ctx.set_verify_mode(asio::ssl::verify_peer); // Load a trusted CA, which was used to sign the server's certificate. // This will allow the signature verification to succeed in our example. // You will have to run your MySQL server with the test certificates // located under $BOOST_MYSQL_ROOT/tools/ssl/ // If you want to use your system's trusted CAs, use // ssl::context::set_default_verify_paths() instead of this function. ssl_ctx.add_certificate_authority(asio::buffer(CA_PEM)); // We expect the server certificate's common name to be "mysql". // If it's not, the certificate will be rejected and handshake or connect will fail. // Replace "mysql" by the common name you expect. ssl_ctx.set_verify_callback(asio::ssl::host_name_verification("mysql")); // Create a connection. // We pass the context as the second argument to the connection's constructor. // Other TLS options can be also configured using this approach. // We need to keep ssl_ctx alive as long as we use the connection. mysql::any_connection conn(co_await asio::this_coro::executor, mysql::any_connection_params{&ssl_ctx}); // The hostname, username, password and database to use mysql::connect_params params; params.server_address.emplace_host_and_port(std::string(server_hostname)); params.username = username; params.password = password; params.database = "boost_mysql_examples"; // Connect to the server. If certificate verification fails, // async_connect will fail. co_await conn.async_connect(params);
You can safely share a single asio::ssl::context
among several connections.
If no ssl::context
is passed, one will be internally
created by the connection when required. The default context doesn't perform
certificate validation.
The full source code for the above example is here.
Since connection_pool
creates any_connection
instances, the mechanics for TLS are similar. TLS-related parameters are
specified during pool construction, as members of pool_params
:
pool_params::ssl
controls TLS negotiation. It's a ssl_mode
value, with the semantics explained above.
pool_params::ssl_ctx
is a asio::ssl::context
that is passed to connections created by the pool. It can be used to
configure TLS
options like certificate verification. The pool takes ownership
of the passed ssl::context
, as opposed to any_connection
.
connect_params::server_address
is an any_address
,
a variant-like type that can hold a (hostname, port) pair or a UNIX socket
path. To connect to MySQL using a UNIX socket, set server_address
to a UNIX domain path:
// Create a connection. // Will use the same executor as the coroutine. mysql::any_connection conn(co_await asio::this_coro::executor); // The socket path, username, password and database to use. // server_address is a variant-like type. Using emplace_unix_path, // we can specify a UNIX socket path, instead of a hostname and a port. // UNIX socket connections never use TLS. mysql::connect_params params; params.server_address.emplace_unix_path(std::string(unix_socket_path)); params.username = username; params.password = password; params.database = "boost_mysql_examples"; // Connect to the server co_await conn.async_connect(params);
Note that UNIX sockets never use TLS, regardless of the value of connect_params::ssl
.
any_connection
owns an internal network buffer used to store messages that are to be written
or have been read from the server. Its initial size is given by any_connection_params::initial_buffer_size
.
Every protocol message needs to fit in memory, so the buffer is expanded
as required. When reading data, every row is sent as an individual message.
The buffer never resizes past any_connection_params::max_buffer_size
.
If an operation requires a bigger buffer, it will fail with the client_errc::max_buffer_size_exceeded
error code. The
default size is 64MB.
If you need to read or write individual rows bigger than the default limit, you can increase it when constructing the connection:
// Increase the max buffer size to 512MB. // This allows reading individual rows as big as 512MB. // This is only required if each individual row is extremely big, // and is not required for many smaller rows. mysql::any_connection_params conn_params; conn_params.max_buffer_size = 0x20000000; // Create the connection mysql::any_connection conn(ctx, conn_params); // Connect and use the connection normally
Note that reading datasets bigger than 64MB does not require increasing the limit as long as individual rows are smaller than the aforementioned limit.
Tweaking any_connection_params::initial_buffer_size
may affect any_connection::async_read_some_rows
performance, as explained in this
section.
You can run several several semicolon-separated queries at once using any_connection::async_execute
.
This is a protocol feature that is disabled by default. You can enable it
by setting connect_params::multi_queries
to true before connecting:
// The server host, username, password and database to use. // Setting multi_queries to true makes it possible to run several // semicolon-separated queries with async_execute. mysql::connect_params params; params.server_address.emplace_host_and_port(std::string(server_hostname)); params.username = std::move(username); params.password = std::move(password); params.database = "boost_mysql_examples"; params.multi_queries = true; // Connect to the server co_await conn.async_connect(params);
As explained in the tutorial, multi-separated queries are useful in a number of cases, like when using transactions. This section contains more info on how to use multi-queries.
This protocol feature is disabled by default as a security hardening measure. If your application contains a SQL injection vulnerability, this feature can make exploiting it easier. Applications that don't need this feature should leave it off as a best practice.
![]() |
Note |
---|---|
Using multi-queries correctly is secure. Just make sure to use the adequate client-side SQL formatting tools to generate queries securely. |
You can cleanly close a connection by calling any_connection::async_close
.
This sends a quit packet to the server, notifying that
we're about to end the connection, performs TLS shutdown, and closes the
underlying transport. A clean close involves I/O and can thus fail.
Destroying the connection without performing a clean close will just close the underlying transport. It won't leak any resource, but you might see warnings in the server log. Try to close connections cleanly when possible.
any_connection
doesn't perform any re-connection on its own. If a fatal error (like a network
error) is encountered during an operation, you need to re-establish the connection
explicitly.
By design, any_connection::async_connect
can always be used to re-establish connections.
It works even after the connection encountered a network error or a cancellation.
To achieve this, async_connect
will wipe any previous connection state before proceeding.
If you need reliable, long-lived connections, consider using
a connection pool instead of rolling out your own strategy. connection_pool
takes care of re-connecting
and re-using connections for you.