WL#11381: Add asynchronous support into the mysql protocol
Affects: Server-8.0
—
Status: Complete
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.
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.