| Home | Libraries | People | FAQ | More

## Connection establishment and termination

This section discusses several aspects regarding the creation, establishment and termination of client connections.

### Authentication

`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+. This plugin used to require using TLS. Since Boost 1.89, this is no longer the case, and it can be used with plaintext connections, too.

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 with database

`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 support

TLS encrypted connections are fully supported by Boost.MySQL. TCP connections established using `any_connection` use TLS by default.

##### TLS handshake and termination

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.

##### TLS negotiation

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.

##### Disabling TLS

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.

##### Certificate validation and other TLS options

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.

##### TLS in connection_pool

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`.

### UNIX sockets

`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`.

### Changing the network buffer size limit

`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.

### Enabling multi-queries

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.

### Closing a connection

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.

### Reconnection and long-lived connections

`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.