WL#11381: Add asynchronous support into the mysql protocol

Affects: Server-8.0   —   Status: Complete   —   Priority: Medium

C APIs are synchronous, which means client sends a request and waits until server
responds. This makes applications to not do anything until server sends a 
response. There can be a requirement where client is not bothered about when
server sends data back, client can submit a query, do some other tasks and when
in need of server response, client can check if server has sent the response and 
act accordingly. Thus making a need to implement asynchronous communication 
between client and server.

use case:
There can be a PHP script which can submit multiple queries from multiple
connections and return immediately without waiting for the results. Later it
can poll on results from all the connections to get the results and do further
processing on fetched data.
FR1. All C APIs which read data from the socket will have an equivalent
     non blocking version of C API.
FR2. These C APIs include following:
     mysql_real_connect, mysql_send_query,  mysql_real_query, mysql_store_result
     mysql_next_result, mysql_fetch_row, mysql_free_result.
FR3. New non blocking version of above C APIs are as follows:
     mysql_real_connect_nonblocking, mysql_send_query_nonblocking,
     mysql_real_query_nonblocking, mysql_store_result_nonblocking,
     mysql_next_result_nonblocking, mysql_fetch_row_nonblocking,
     mysql_free_result_nonblocking
FR4. Non blocking version of C APIs does not wait for data to be available on the
     socket to be read. These APIs will return immediately with a return status.
FR5. Return status is used to decide if the API has completed its operation or
     needs to be called again at some later point in time to complete the 
     operation.
FR6. APIs which will not be asynchronous are as follows:
     mysql_autocommit, mysql_change_user, mysql_close, mysql_commit, 
     mysql_connect, mysql_dump_debug_info, mysql_kill, mysql_list_*
     mysql_ping, mysql_query, mysql_refresh, mysql_reload, 
     mysql_reset_connection, mysql_rollback, mysql_select_db,
     mysql_set_server_option, mysql_shutdown, mysql_stat, mysql_use_result,
     mysql_stmt_*, mysql_binlog_*
In mysql, client server communication is synchronous which means client sends
a request and waits in an idle state until it receives a response from server.
This idle state can be overcome by making the client server communication
asynchronous. Thus in an asynchronous communication protocol, client sends a
request which makes the request to return immediately without waiting for
server response and let client do some other tasks. When client at some later
point in time needs the server response can further read the network to get
the data. To achieve this kind of communication this WL will introduce a list of
non blocking C APIs.

Below is the list of C APIs which wait on the network socket for data to be
available for further processing:
mysql_autocommit
mysql_change_user
mysql_close
mysql_commit
mysql_connect
mysql_dump_debug_info
mysql_kill
mysql_list_dbs
mysql_list_fields
mysql_list_processes
mysql_list_tables
mysql_ping
mysql_query
mysql_real_connect
mysql_refresh
mysql_reload
mysql_reset_connection
mysql_rollback
mysql_select_db
mysql_set_server_option
mysql_shutdown
mysql_stat
mysql_use_result
mysql_stmt_close
mysql_stmt_execute
mysql_stmt_fetch
mysql_stmt_free_result
mysql_stmt_next_result
mysql_stmt_prepare
mysql_stmt_reset
mysql_stmt_send_long_data
mysql_stmt_store_result
mysql_binlog_open
mysql_binlog_fetch

All the above C APIs call these below C APIs:
1. mysql_send_query
2. mysql_real_query
3. mysql_store_result
4. mysql_next_result
5. mysql_fetch_row
6. mysql_free_result

These C APIs are the once which futher make calls to read or write to the
network socket. Thus by introducing non blocking version of C APIs for
these 6 APIs will make client have asynchronous communication support.

Connection phase between client server is also synchronous. To make this
asynchronous non blocking version of mysql_real_connect will be introduced.

This WL will have these non blocking C APIs:
mysql_real_connect_nonblocking
mysql_send_query_nonblocking
mysql_real_query_nonblocking
mysql_store_result_nonblocking
mysql_next_result_nonblocking
mysql_fetch_row_nonblocking
mysql_free_result_nonblocking

Function signature for above non blocking C APIs is as below:
enum net_async_status
mysql_real_connect_nonblocking(MYSQL *mysql,
                               const char *host,
                               const char *user, 
                               const char *passwd,
                               const char *db,
                               unsigned int port,
                               const char *unix_socket,
                               unsigned long clientflag);
Description:
This API attempts to initialize all the context needed to make an asynchronous
connection followed by establishing a connection to MySQL database. If this
API returns NET_ASYNC_COMPLETE then connection is established else call this
API from the client application until the status returned is NET_ASYNC_COMPLETE.
Parameter description:
The parameters are same as specified for mysql_real_connect()

enum net_async_status
mysql_real_query_nonblocking(MYSQL *mysql,
                             const char *query,
                             unsigned long length);
Description:
Executes the SQL statement pointed by query. This sql statement length is set in
length parameter. query string can contain multiple sql statements separated by 
semicolons. This function can return immediately with status set to 
NET_ASYNC_NOT_READY, in this case client application is expected to call this 
API until it returns NET_ASYNC_COMPLETE.
Parameter description:
First parameter is address of MYSQL structure.
Second parameter specifies a string which holds sql statements.
Third parameter specifies length of query string.

enum net_async_status
mysql_send_query_nonblocking(MYSQL *mysql,
                             const char *query,
                             unsigned long length);
Description:
Executes the SQL statement pointed by query. This API is called by
mysql_real_query_nonblocking to send query to server in asynchronous way.
Parameter description:
Same as for mysql_real_query_nonblocking.

enum net_async_status
mysql_store_result_nonblocking(MYSQL *mysql,
                               MYSQL_RES **result);
Description:
Same as mysql_store_result expect that this API can return immediately without
reading all results from server.
Parameter description:
First parameter is address of MYSQL structure.
Second parameter is MYSQL_RES result structure with the results. MYSQL_RES
value NULL can represents either empty resultset or an error. If it is an
error then end user should call mysql_error() to check the same. If there is
no error it means there is no result sets to be read.

enum net_async_status
mysql_next_result_nonblocking(MYSQL *mysql);
Description:
Same as mysql_next_result expect that this API can return immediately without
reading next statement result.
Parameter description:
First parameter is address of MYSQL structure.

enum net_async_status
mysql_fetch_row_nonblocking(MYSQL_RES *res,
                            MYSQL_ROW *row);
Description:
Reads next row of a result set in an asynchronous way.
Parameter description:
First parameter is MYSQL_RES structure with the results.
Second parameter specifies the read row. NULL if there are no more rows to 
retrieve or if an error occurred. When used after mysql_use_result(), NULL 
indicates that there are no more rows to retrieve or if an error occurred. To 
determine whether an error occurred, check whether mysql_error() returns a 
nonempty string or mysql_errno() returns nonzero.

enum net_async_status
mysql_free_result_nonblocking(MYSQL_RES *result);
Description:
Frees the memory allocated for a result, set by APIs which would have returned
rows.
Parameter description:
First parameter is MYSQL_RES structure with the results.

For all above non blocking C APIs whose return type is net_async_status, return 
value description is below:
NET_ASYNC_COMPLETE if asynchronous operation is complete.
NET_ASYNC_NOT_READY if operation is incomplete. In this case call API again
until NET_ASYNC_COMPLETE is returned.
NET_ASYNC_ERROR if asynchronous operation ended with an error.
NET_ASYNC_COMPLETE_WITH_MORE_RESULTS if mysql_next_result_nonblocking() API did
return with no more results.

New data structures to support asynchronous operations:
=======================================================
To keep track of the context associated with asynchronous operations
mysql_async struct will be introduced. Below is list of members added to
this structure.

struct mysql_async {
  /* Buffer storing the rows result for cli_read_rows_nonblocking */
  MYSQL_DATA *rows_result_buffer;
  /* a pointer to keep track of the previous row of the current result row */
  MYSQL_ROWS **prev_row_ptr;
  /* Context needed to track the state of a connection being established */
  struct mysql_async_connect *connect_context;
  /* Status of the current async op */
  enum mysql_async_operation_status async_op_status;
  /* Size of the current running async query */
  size_t async_query_length;
  /* If a query is running, this is its state */
  enum mysql_async_query_state_enum async_query_state;
  /* context needed to support metadata read operation */
  unsigned long *async_read_metadata_field_len;
  MYSQL_FIELD *async_read_metadata_fields;
  MYSQL_ROWS async_read_metadata_data;
  unsigned int async_read_metadata_cur_field;
  /* a pointer to keep track of the result sets */
  struct MYSQL_RES *async_store_result_result;
};

struct mysql_async will be added to struct MYSQL_EXTENSION. This way it will be
available in MYSQL struct through MYSQL::extension member.
struct MYSQL_EXTENSION {
  struct st_mysql_trace_info *trace_data;
  STATE_INFO state_change;
  /* Struct to track the state of asynchronous operations */
  struct mysql_async mysql_async_context;
};

struct mysql_async_connect keeps all context needed to establish asynchronous
connection.
struct mysql_async_connect {
  /* state for the overall connection process */
  MYSQL *mysql;
  const char *host;
  const char *user;
  const char *passwd;
  const char *db;
  uint port;
  const char *unix_socket;
  ulong client_flag;
  bool non_blocking;

  ulong pkt_length;
  char *host_info;
  char buff[NAME_LEN + USERNAME_LENGTH + 100];
  int scramble_data_len;
  char *scramble_data;
  const char *scramble_plugin;
  char *scramble_buffer;
  bool scramble_buffer_allocated;

  struct mysql_async_auth *auth_context;
  /* state for running init_commands */
  bool saved_reconnect;
  char **current_init_command;

  /* context needed to establish asynchronous ssl connection */  
  ssl_exchange_state ssl_state;
#if defined(HAVE_OPENSSL)
  SSL *ssl;
#endif
  /* state function that will be called next */
  csm_function state_function;
};

struct mysql_async_auth keeps all context needed to establish asynchronous
authentication.

struct mysql_async_auth {
  MYSQL *mysql;
  bool non_blocking;
  char *data;
  uint data_len;
  const char *data_plugin;
  const char *db;
  const char *auth_plugin_name;
  auth_plugin_t *auth_plugin;
  MCPVIO_EXT mpvio;
  ulong pkt_length;
  int res;
  char *change_user_buff;
  int change_user_buff_len;
  void* client_auth_plugin_state;
  authsm_function state_function;
};

/* A state machine for connection itself. */
typedef mysql_state_machine_status (*csm_function)(mysql_async_connect *);
/* A state machine for authentication itself. */
typedef mysql_state_machine_status (*authsm_function)(mysql_async_auth *);

Asynchronous operations are broadly classified into 2 categories. 1. Connection 
2. Query execution. This classification is defined in below enum 
mysql_async_operation_status
enum mysql_async_operation_status {
  ASYNC_OP_UNSET = 18000,
  ASYNC_OP_CONNECT = 18010,
  ASYNC_OP_QUERY = 18020
};

Query execution in an asynchronous fashion is broadly divided into 3 states 
QUERY_IDLE, QUERY_SENDING, QUERY_READING_RESULT this state is defined in 
mysql_async_query_state_enum 
enum mysql_async_query_state_enum {
  QUERY_IDLE = 0,
  QUERY_SENDING,
  QUERY_READING_RESULT
};

A new enum is introduced to represent state of an asynchronous operation. This
enum is exposed to end users to be able to declare in client applications to
check the state of asynchronous operation.
enum net_async_status {
NET_ASYNC_COMPLETE = 20100,
NET_ASYNC_NOT_READY,
NET_ASYNC_ERROR,
NET_ASYNC_COMPLETE_WITH_MORE_RESULTS
};

To define different states of an asynchronous SSL connection phase below enum
is introduced.
enum ssl_exchange_state {
  SSL_REQUEST = 8100,
  SSL_CONNECT = 8101,
  SSL_COMPLETE = 8102,
  SSL_NONE = 8103
};

A sample application is as below:

#include <'stdio.h'>
#include <'string.h'>
#include <'iostream'>
#include <'mysql.h'>
#include <'mysqld_error.h'>

using namespace std;

static char * c_host = "localhost";
static char * c_user = "root";
static char * c_auth = "";
static int    c_port = 17171;
static char * c_sock = "/tmp/mysqld47.sock";
static char * c_dbnm = "";

void perform_arithmatic() {
  cout<<"\n dummy function";
  for (int i =0; i < 1000; i++)
    i*i;
}

static void wl11381() {
  MYSQL *mysql_local;
  MYSQL_RES *result;
  MYSQL_ROW row;
  net_async_status status;
  const char *stmt_text;

  if (!(mysql_local = mysql_init(NULL))) {
    cout<<"\n mysql_init() failed";
    exit(1);
  }
  while ((status == mysql_real_connect_nonblocking(mysql_local, c_host, c_user,
    c_auth, c_dbnm, c_port, c_sock, 0)) == NET_ASYNC_NOT_READY);
  if (status == NET_ASYNC_ERROR) {
    cout<<"\n mysql_real_connect_nonblocking() failed";
    exit(1);
  }
  stmt_text = "SELECT * FROM db.test_table order by id";
  /* run query in asynchronous way */
  status = mysql_real_query_nonblocking(mysql_local, stmt_text,
    (ulong)strlen(stmt_text));
  /* do some other task */
  perform_arithmatic();
  while (status == NET_ASYNC_NOT_READY) {
    status = mysql_real_query_nonblocking(mysql_local, stmt_text,
      (ulong)strlen(stmt_text));
  }
  if (status == NET_ASYNC_ERROR) {
    cout<<"\n mysql_real_query_nonblocking() failed";
    exit(1);
  }
  status = mysql_store_result_nonblocking(mysql_local, &result);
  /* do some other task */
  perform_arithmatic();
  while (status == NET_ASYNC_NOT_READY) {
    status = mysql_store_result_nonblocking(mysql_local, &result);
  }
  if (!result) {
    cout<<"\n mysql_store_result_nonblocking() found 0 records";
    exit(1);
  }
  row = mysql_fetch_row(result);
  if (strcmp(row[0], "10") == 0)
    cout<<"\n ROW :" << row[0];
  else
    cout<<" \n wrong result fetched";

  while ((status == mysql_fetch_row_nonblocking(result, &row)) != 
    NET_ASYNC_COMPLETE);
  /* 2nd row fetched */
  if (strcmp(row[0], "20") == 0)
    cout<<"\n ROW :"<< row[0];
  else
    cout<<" \n wrong result fetched";

  status = mysql_fetch_row_nonblocking(result, &row);
  /* do some other task */
  perform_arithmatic();
  if (status == NET_ASYNC_COMPLETE) {
    if (strcmp(row[0], "30") == 0)
    cout<<"\n ROW :"<< row[0];
  else
    cout<<" \n wrong result fetched";
  } else {
    while ((status == mysql_fetch_row_nonblocking(result, &row)) != 
      NET_ASYNC_COMPLETE);
    /* 3rd row fetched */
    if (strcmp(row[0], "30") == 0)
      cout<<"\n ROW :"<< row[0];
    else
      cout<<" \n wrong result fetched";
  }
  while ((status = mysql_fetch_row_nonblocking(result, &row)) != 
    NET_ASYNC_COMPLETE);
  if (row == NULL) { cout <<" \n Nor more rows to process."; }

  while((status == mysql_free_result_nonblocking(result)) != 
    NET_ASYNC_COMPLETE);
  mysql_close(mysql_local);
}

int main(int argc, char ** argv)
{
/*
  create below sql objects:
      "DROP DATABASE IF EXISTS db;"
      "CREATE DATABASE db;"
      "USE db;"
      "CREATE TABLE test_table(id INT);"
      "INSERT INTO test_table VALUES (10), (20), (30);";

*/
  wl11381();
}

build above program as follows:
gcc -g async_app.cc -std=c++11 -I/export/home/tmp/bhsatish/11381/include -o 
async_app -L/usr/lib64/ -lstdc++ -L/export/home/tmp/bhsatish/11381/lib/ -
lmysqlclient

Asynchronous connection phase:
==============================
1. Client connects to server.
2. Server sends initial handshake packet along with authentication method data.
3. Client replies with initial response packet along with client side
   authentication method data.
4. Server and client further send more packets once common authentication
   method is agreed upon.
5. On success server sends OK packet where connection is successful else ERR
   packet in case of failure.

With this WL all the above communication will be made asynchronous.
To achieve this several non-blocking APIs needs to be introduced which are
listed below:
1. mysql_real_connect_nonblocking - initialize all context needed to start
   asynchronous connection, read and parse the handshake packets sent by
   server.
2. New non blocking client authentication plugin APIs will be introduced as 
   part of it.   
   mysql_declare_client_plugin(AUTHENTICATION) will be extended with new API
   to achieve asynchronous authentication.

   enum net_async_status (*authenticate_user_nonblocking)(MYSQL_PLUGIN_VIO *vio,
                                                          struct MYSQL *mysql,
                                                          int *result);
   This plugin API will executes a state machine defined in variable
   mysql->extension->mysql_async_context->connect_context->
   auth_context->client_auth_plugin_state
   Presence of this non blocking call in plugin will ensure that plugin
   supports asynchronous authentication.
3. Above client authentication plugin API further calls methods to read and 
   write to network. These APIs needs to be non blocking too, thus 
   MYSQL_PLUGIN_VIO is extended to add 2 more non blocking APIs.
   
   enum net_async_status (*read_packet_nonblocking)(
          struct MYSQL_PLUGIN_VIO *vio,
          unsigned char **buf,
          int *result);
   "buf" parameter holds the data read which is sent by server.
   "result" parameter holds error state zero for success nonzero for error.

   enum net_async_status (*write_packet_nonblocking)(
          struct MYSQL_PLUGIN_VIO *vio,
          const unsigned char *pkt,
          int pkt_len, int *result);
   "pkt" parameter holds the data to be written.
   "pkt_len" length of "pkt" to be sent to server.
   "result" parameter holds error state zero for success nonzero for error.

   Return value for above APIs:
   NET_ASYNC_COMPLETE if asynchronous operation is complete.
   NET_ASYNC_NOT_READY if operation is incomplete. In this case call this API
   again until NET_ASYNC_COMPLETE is returned.

Note: If client initiates asynchronous connection and the underlying client side 
authentication plugin does not support asynchronous communication then 
connection is aborted and an error is reported.

New non blocking client authentication plugin API is available only for these
plugins: mysql_native_password, sha256_password, caching_sha2_password.
Its is not supported for other plugins like clear_text_password, ldap client
side plugin etc.

Before sending initial response packet from client to server, client can decide 
upon establishing SSL connection too if server supports it.
Currently cli_establish_ssl() function obtains SSL connection. This WL 
implements cli_establish_ssl_nonblocking() function to achieve non blocking
SSL connection.

Currently following statements are not supported with asynchronous APIs:
Load data infile statement, Load xml statements.
Asynchronous support with compression protocol enabled is also not supported.

Note: Asynchronous communication is supported only for TCPIP/SSL/SOCKET based
connections only.
One of the ways to implement asynchronous support is to use a finite state
machine. In this WL we define different states to represent different
operations w.r.t network read or write or query related states or connection
related states or state to represent authentication phase.

Different states are defined below:

This enum represents state of an asynchronous operation. State can be either
complete or yet to start.
enum net_async_status
{ NET_ASYNC_COMPLETE = 20100, /* async operation is complete */
  NET_ASYNC_NOT_READY,        /* async operation needs to be restarted again */
  NET_ASYNC_ERROR             /* async operation ended with an error */
};

This enum is to represent different asynchronous operations like reading the
network, writing to network, idle state, or complete state.
enum net_async_operation {
  NET_ASYNC_OP_IDLE = 0,        /* default state */
  NET_ASYNC_OP_READING = 20200, /* used by my_net_read calls */
  NET_ASYNC_OP_WRITING,         /* used by my_net_write calls */
  NET_ASYNC_OP_COMPLETE         /* network read or write is complete */
};

Data is transferred between client and server in the form of packets. Thus
reading this packet is segregated into different states. This includes read
packet header, read packet body, read complete, or idle state. Below enum
represents the same.
enum net_async_read_packet_state {
  NET_ASYNC_PACKET_READ_IDLE = 0,       /* default packet read state */
  NET_ASYNC_PACKET_READ_HEADER = 2030   /* read packet header */
  NET_ASYNC_PACKET_READ_BODY,           /* read packet contents */
  NET_ASYNC_PACKET_READ_COMPLETE        /* state to define if packet is 
                                           completely read */
};

Reading query results is segregated into different states as described below.
enum net_read_query_result_status {
  NET_ASYNC_READ_QUERY_RESULT_IDLE = 0,   /* default read query result state */
  NET_ASYNC_READ_QUERY_RESULT_FIELD_COUNT = 20240,
                                          /* read Ok or read field count sent 
                                              as part of COM_QUERY */
  NET_ASYNC_READ_QUERY_RESULT_FIELD_INFO  /* read metadata if field count > 0 */
};

As part of client server communication several COM_* commands are sent from
server. State of this communication is described in below enum.
enum net_send_command_status {
  NET_ASYNC_SEND_COMMAND_IDLE = 0,              /* default send command state */
  NET_ASYNC_SEND_COMMAND_WRITE_COMMAND = 20340, /* send COM_* command */
  NET_ASYNC_SEND_COMMAND_READ_STATUS = 20340    /* read result of above COM_* 
command */
};

At a more higher level async operation is broadly classified into 3 phases.
Connection phase, phase of sending data to server (which is writing phase) and
reading data from server (which is reading phase). Below enum describes the 
same.
enum net_async_block_state {
  NET_NONBLOCKING_CONNECT = 20130,    /* state describing connection phase */
  NET_NONBLOCKING_READ = 20140,       /* reading state */
  NET_NONBLOCKING_WRITE = 20150       /* writing state */
};

Changes to vio:
===============
vio_read() and vio_write() functions read or write on the network. If there is
no data on the socket, then these calls will wait until data is present or
timeout and retry. In this WL based on a flag (is_blocking == false) we return
immediately if there is no data on the socket.
Similarly changes are made in vio_socket_connect() to ensure connection phase
is also asynchronous.

Changes to my_net_read/write:
=============================
my_net_read/my_net_write are high level calls used to read or write onto the
network. These functions internally call vio_read/write. Non blocking version
for these functions are introduced as part of this WL.

my_net_read_nonblocking: This function will read packet header followed by
packet contents. These 2 operations are driven by change in state. Initial
state is set to NET_ASYNC_PACKET_READ_IDLE which is then changed to
NET_ASYNC_PACKET_READ_HEADER. Reading packet header is defined by
net_read_packet_header_nonblock(). If this function 
net_read_packet_header_nonblock
returns immediately with a return status set to NET_ASYNC_NOT_READY, then the
caller will again call same function my_net_read_nonblocking() to again
check if packet header is read or not based on the state saved in NET structure.
This way based on state of an asynchronous operations non-blocking functions
are called to finish the operations. Once packet contents are read using
net_read_data_nonblocking() we then reset the state to 
NET_ASYNC_PACKET_READ_IDLE for next packet to be read.

State change when reading from network is as below:
NET_ASYNC_OP_IDLE -> NET_ASYNC_OP_READING -> NET_ASYNC_OP_COMPLETE -> 
NET_ASYNC_OP_IDLE

State change when reading packet sent by server to client is as below:
NET_ASYNC_PACKET_READ_IDLE -> NET_ASYNC_PACKET_READ_HEADER -> 
NET_ASYNC_PACKET_READ_BODY -> NET_ASYNC_PACKET_READ_COMPLETE -> 
NET_ASYNC_PACKET_READ_IDLE

my_net_write_nonblocking: Similar to my_net_write except that this function
returns immediately by either setting return status to NET_ASYNC_NOT_READY or
NET_ASYNC_COMPLETE.
State change when writing from network is as below:
NET_ASYNC_OP_IDLE -> NET_ASYNC_OP_WRITING -> NET_ASYNC_OP_COMPLETE -> 
NET_ASYNC_OP_IDLE

Changes during query execution:
===============================
Executing a query involves sending a query followed by reading results. Reading
results might include following:
1. Reading OK packet in case there is no result set.
2. Reading metadata.
3. Reading results sets by calling mysql_fetch_row or calling 
   mysql_store_result.

Sending query to server is done by call to mysql_send_query. mysql_send_query
further calls cli_advanced_command to write the query on the network. Thus a
non-blocking version for these API is introduced as part of this WL.
mysql_send_query_nonblocking followed by call to 
cli_advanced_command_nonblocking which does send query to server in an 
asynchronous way.

Reading response as part of sending a query is achieved by call to
mysql_read_query_result API. This API further calls cli_safe_read or
cli_read_metadata in case there is a metadata sent by server. Thus we have
mysql_read_query_result_nonblocking to achieve the same in asynchronous way.

Similarly mysql_fetch_row_nonblocking or mysql_store_result_nonblocking will
be introduced to read result set in an asynchronous way.

Changes during authentication phase:
====================================
After reading initial handshake packet from server, client establishes ssl
connection and then checks with client side authentication plugin to know if it
supports asynchronous communication or not. If it supports then plugin will 
return true along with state machine set in 
mysql->extension->mysql_async_context->auth_context->client_auth_plugin_state. 
This enum represents a state machine applicable to this client plugin. Client 
further calls plugin authentication API authenticate_user_nonblocking() to 
obtain connection.

Client authentication plugin does following (in case of caching_sha2 plugin)
1. Read scramble sent by server.
2. Generate random string from password and above scramble and send to server.
3. Read response. Response can be fast authentication success or perform full
   authentication.
4. For non ssl connection request public key
 4.1 Read public key sent by server.
 4.2. Encrypted password using public key and send to server.
5. If ssl connection is established send plain text password.

Above sequence of steps are mapped to represent a state flow defined by below
enum:
enum caching_sha2_password_plugin_state {
  READING_PASSWORD = 1,    /* read scramble */
  WRITING_RESPONSE,        /* write scrambled password */
  CHALLENGE_RESPONSE,      /* check for fastpath or full authentication */
  REQUEST_PUBLIC_KEY,      /* request public key from server */
  READ_PUBLIC_KEY,         /* read public key sent by server */
  SEND_ENCRYPTED_PASSWORD, /* send encrypted password */
  SEND_PLAIN_PASSWORD      /* if ssl set send plain text password */
};

caching_sha2_password_auth_client_nonblocking: Initially state is set to 
READING_PASSWORD, thus making plugin API read the scramble sent by server, then 
state is changed to WRITING_RESPONSE. Reading of this password should be 
asynchrounous, thus client_mpvio_read_packet_nonblocking() is implemented to 
achieve this. Writing scrambled password is done by 
client_mpvio_write_packet_nonblocking(), this call can return immediately or 
complete the operation. If operation is not complete server calls this plugin 
API again until operation is complete and change the state to 
CHALLENGE_RESPONSE. This way based on what state client authentication plugin is 
in execute that state to complete authentication phase without blocking.

Below sample code path executes asynchronous authentication operation:

int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
                    const char *data_plugin, const char *db) {
  DBUG_ENTER("run_plugin_auth");
  ...
  ...
  if (ctx->non_blocking) {
    if (!(ctx->auth_plugin->is_async_authentication_supported(mysql))) {
      net_async_status status = ctx->auth_plugin->authenticate_user_nonblocking(
        (struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql, &ctx->res);
      if (status == NET_ASYNC_NOT_READY) {
        DBUG_RETURN(STATE_MACHINE_WOULD_BLOCK);
      }
    } else {
      DBUG_RETURN(STATE_MACHINE_FAILED);
    }
  } else {
    ctx->res = ctx->auth_plugin->authenticate_user(
        (struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql);
  }
  ...
}

Below enum represents state flow for sha256_password client plugin:
enum sha256_password_plugin_state {
  READING_PASSWORD = 1,    /* read scramble */
  REQUEST_PUBLIC_KEY,      /* request public key from server */
  READ_PUBLIC_KEY,         /* read public key sent by server */
  SEND_ENCRYPTED_PASSWORD, /* send encrypted password */
  SEND_PLAIN_PASSWORD      /* if ssl set send plain text password */
};

Below enum represents state flow for native_password client plugin:
enum native_password_plugin_state {
  READING_PASSWORD = 1,    /* read scramble */
  WRITING_RESPONSE,        /* write scrambled password */
};

MCPVIO_EXT and MYSQL_PLUGIN_VIO are extended to support nonblocking read and
write operations. Below APIs are added to this structure.

enum net_async_status (*read_packet_nonblocking)(struct MYSQL_PLUGIN_VIO *vio,
                                                 unsigned char **buf,
                                                 int *result);

enum net_async_status (*write_packet_nonblocking)(
     struct MYSQL_PLUGIN_VIO *vio, const unsigned char *pkt, int pkt_len,
     int *result);

Changes during SSL connection:
==============================
Different states of an asynchronous SSL connection phase is as below:
enum ssl_exchange_state {
  SSL_REQUEST = 8100,
  SSL_CONNECT = 8101,
  SSL_COMPLETE = 8102,
  SSL_NONE = 8103
};
New nonblocking cli_establish_ssl_nonblocking function is introduced which will
set initial state to SSL_NONE during which we check if server supports ssl or 
not, if CA certificate is required when ssl_mode is set to VERIFY_CA. Once 
preliminary checks are done state is changed to SSL_REQUEST. In this state ssl 
request packet is sent by client. If this network IO is complete, state is 
changed to SSL_CONNECT. During SSL_CONNECT sslconnect() is called which can 
return immediately or complete SSL handshake. If it returns immediately we need 
to store all SSL context in struct mysql_async_auth , so that next call to same 
function will ensure that SSL_new() is not called twice.
During SSL_REQUEST state my_net_write_nonblocking() is called
to write the packet containing ssl request to server in asynchronous way.