diff --git a/Notes.txt b/Notes.txt
index a8e0f7f..df70f20 100644
--- a/Notes.txt
+++ b/Notes.txt
@@ -9,7 +9,24 @@ connection, and use the new one afterward.
+---------------------------+
OPTION DESCRIPTION
------------------ ----------------------------------------------------------
-mysqlnd_azure.enabled This option is to control enable or disable mysqlnd_rd.
-If this is set to 0, it will not use redirection.
-(Default: 0)
+mysqlnd_azure.enableRedirect This option is to control enable or disable redirection feature of mysqlnd_azure.
+If this is set to off, it will not use redirection.
+
+Available option values:
+(Default: off)
+---------------|------------------------------------------------------------------------------------------------------------------------------------------
+off(0) | - It will not use redirection.
+---------------|------------------------------------------------------------------------------------------------------------------------------------------
+on(1) | - If ssl is off, no connection will be made, return error:
+ | "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL."
+ | - If on server side redirection is not available, and there is no last message in OK packet, abort the first connection and return error:
+ | "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol."
+ | - If server supports redirection, but the redirected connection failed for any reason, also abort the first proxy connection.
+ | Return the error of the redirected connection.
+---------------|------------------------------------------------------------------------------------------------------------------------------------------
+preferred(2) | - it will use redirection if possible.
+ | - If connection does not use SSL, or server does not support redirection, or redirected connection fails
+ | to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback
+ | to the first proxy connection.
+---------------|------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/README.md b/README.md
index 11b9788..74ef439 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,72 @@
# PHP mysqlnd redirection extension mysqlnd_azure
The source code here is a PHP extension implemented using mysqlnd plugin API (https://www.php.net/manual/en/mysqlnd.plugin.php), which provides redirection feature support. The extension is also available on PECL website at https://pecl.php.net/package/mysqlnd_azure.
+**Important notice: There is a limitation that for Azure MySQL, redirection is only possible when the connection is configured with SSL.**
+
+## Option Usage
+
+In 1.0.x versions, when redirection is turned on, but if connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection. The detailed usage of the option enableRedirect is as follows:
+
+(Version 1.0.x. Config name: **mysqlnd_azure.enableRedirect**. Valid value: on/off. Default value: off)
+
+
+
off(0)
+
- It will not use redirection.
+
+
+
+
on(1)
+
- It will use redirection if possible (Connection is with SSL and Server supports/need redirection).
+ - If connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection.
+
+
+
+
+Since 1.1.0beta1, the logic changes as follows:
+- The option mysqlnd_azure.enabled is renamed to **mysqlnd_azure.enableRedirect**, and there is a new option value "preferred" provided.
+- The detailed usage of the option enableRedirect is as follows:
+
+(Version 1.1.0beta1. Config name: **mysqlnd_azure.enableRedirect**. Valid value: on/off/preferred. Default value: off)
+
+
+
off(0)
+
- It will not use redirection.
+
+
+
+
on(1)
+
- If SSL is off, no connection will be made, return error:
+ "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL."
+ - If on server side redirection is not supported, abort the first connection and return error: "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol."
+ - If server supports redirection, but the redirected connection failed for any reason, also abort the first proxy connection. Return the error of the redirected connection.
+
+
+
+
+preferred(2)
+
+
- It will use redirection if possible.
+ - If connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection.
+
+
+
+
## Name and Extension Version
Extension name: **mysqlnd_azure**
Required PHP min version: PHP7.2.15+ and PHP7.3.2+.
Valid version:
-- 1.0.0 Change: initial version. Limitation: cannot install with pecl on linux; cannot work with 7.2.23+ and 7.3.10+
+- 1.0.0 Change: initial version. Limitation: cannot install with pecl on linux, the package on PECL website is invalid, only possible to install with manual compilation on Linux. Cannot work with 7.2.23+ and 7.3.10+
- 1.0.1 Change: with pecl install command line support on linux. Limitation: cannot work with 7.2.23+ and 7.3.10+
- 1.0.2 Change: fix compatibility problem with 7.2.23+ and 7.3.10+
+- 1.1.0beta1 Change: In 1.0.x versions, if connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection. Since 1.1.0beta1, the logic changes as follows:
+ 1. Rename option mysqlnd_azure.enabled to mysqlnd_azure.enableRedirect, and add a new option value "preferred".
+ 2. When enableRedirect is "preferred", it will use redirection if possible. If connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection.
+ 3. If enableRedirect is with value "on", SSL is off, no connection will be made, return error "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL."
+ 4. If enableRedirect is with value "on", but on server side redirection is not supported, abort the first connection and return error "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol."
+ 5. If enableRedirect is with value "on" and server supports redirection, but the redirected connection failed for any reason, also abort the first proxy connection. Return the error of the redirected connection.
+ 6. The cache implementation has been changed from module global to true global to improve share range for multi-thread scenario.
Following is a brief guide of how to install using pecl or build and test the extension from source.
@@ -85,7 +142,8 @@ Then you can run **make install** to put the .so to your php so library. However
- put mysqlnd_azure.so under extension_dir.
- under directory for additional .ini files, you will find the ini files for the common used modules, e.g. 10-mysqlnd.ini for mysqlnd, 20-mysqli.ini for mysqli. Create a new ini file for mysqlnd_azure here. **Make sure the alphabet order of the name is after that of mysqnld**, since the modules are loaded according to the name order of the ini files. E.g. if mysqlnd ini is with name 10-mysqlnd.ini,then name the ini as 20-mysqlnd-azure.ini. In the ini file, add the following two lines:
- extension=mysqlnd_azure
- - mysqlnd_azure.enabled = on ; you can also set this to off to disable redirection
+ - mysqlnd_azure.enableRedirect = on/off/preferred
+ - **Notice:** since 1.1.0beta1, if this value is set to on, the connection must be configured with SSL, and it requires server support redirection. Otherwise, the connection will fail. Please check the Option Usage section for detailed information.
## Step to build on Windows
@@ -131,15 +189,17 @@ After this, the code directory should look like C:\php-sdk\phpdev\vc15\x64\php-s
- extension=mysqlnd_azure
- Under the Module Settings section add:
- [mysqlnd_azure]
- - mysqlnd_azure.enabled = on
+ - mysqlnd_azure.enableRedirect = on/off/preferred
+ - **Notice:** since 1.1.0beta1, if this value is set to on, the connection must be configured with SSL, and it requires server support redirection. Otherwise, the connection will fail. Please check the Option Usage section for detailed information.
## Test
-* Currently redirection is only possible when the connection is via ssl, and it need that the redirection feature switch is enabled on server side. Following is a snippet to test connection with redirection:
+* Currently redirection is only possible when the connection is configured with SSL, and it need that the redirection is supported and enabled on server side. Following is a snippet to test connection with redirection:
```php
- echo "mysqlnd_azure.enabled: ", ini_get("mysqlnd_azure.enabled") == true?"On":"Off", "\n";
+ echo "mysqlnd_azure.enableRedirect: ", ini_get("mysqlnd_azure.enableRedirect"), "\n";
$db = mysqli_init();
+ //The connection must be configured with SSL for redirection test
$link = mysqli_real_connect ($db, 'your-hostname-with-redirection-enabled', 'user@host', 'password', "db", 3306, NULL, MYSQLI_CLIENT_SSL);
if (!$link) {
die ('Connect error (' . mysqli_connect_errno() . '): ' . mysqli_connect_error() . "\n");
@@ -150,4 +210,4 @@ After this, the code directory should look like C:\php-sdk\phpdev\vc15\x64\php-s
print_r ($res);
$db->close();
}
-```
+```
\ No newline at end of file
diff --git a/mysqlnd_azure.c b/mysqlnd_azure.c
index 5c75cbd..2996e22 100644
--- a/mysqlnd_azure.c
+++ b/mysqlnd_azure.c
@@ -174,6 +174,45 @@ set_redirect_client_options(MYSQLND_CONN_DATA * const conn, MYSQLND_CONN_DATA *
}
/* }}} */
+/* {{{ get_redirect_info */
+static zend_bool
+get_redirect_info(MYSQLND_CONN_DATA * const conn, char* redirect_host, char* redirect_user, unsigned int* p_ui_redirect_port)
+{
+ /**
+ * Get redirected server information contained in OK packet.
+ * Redirection string somehow look like:
+ * Location: mysql://redirectedHostName:redirectedPort/user=redirectedUser
+ * the minimal len is 27 bytes
+ */
+ if (conn->last_message.l > 27 && (strncmp((char*)conn->last_message.s, "Location:", strlen("Location:")) == 0) && ((strstr((char*)conn->last_message.s, "mysql://")) != NULL)) {
+ char redirect_port[8] = { 0 };
+
+ int redirect_total_len = conn->last_message.l;
+ unsigned char *cur_pos = conn->last_message.s;
+ char *p1 = strstr((char*)cur_pos, "//");
+ char *p2 = strstr(p1, ":");
+ char *p3 = strstr((char*)cur_pos, "user=");
+
+ int redirect_host_len = p2 - p1 - 2;
+ int redirect_port_len = p3 - p2 - 2;
+ int redirect_user_len = redirect_total_len - (p3 + 5 - ((char*)cur_pos));
+ if (redirect_host_len <= 0 || redirect_port_len <= 0 || redirect_user_len <= 0) {
+ redirect_host_len = redirect_port_len = redirect_user_len = 0;
+ } else {
+ memcpy(redirect_host, p1 + 2, redirect_host_len);
+ memcpy(redirect_user, p3 + 5, redirect_user_len);
+ memcpy(redirect_port, p2 + 1, redirect_port_len);
+ *p_ui_redirect_port = atoi(redirect_port);
+ }
+
+ return redirect_host_len > 0 && redirect_user_len > 0 && redirect_port_len > 0;
+
+ } else {
+ return FALSE;
+ }
+}
+
+
/* {{{ mysqlnd_azure_data::connect */
MYSQLND_METHOD(mysqlnd_azure_data, connect)(MYSQLND_CONN_DATA ** pconn,
MYSQLND_CSTRING hostname,
@@ -277,111 +316,143 @@ MYSQLND_METHOD(mysqlnd_azure_data, connect)(MYSQLND_CONN_DATA ** pconn,
{
SET_CONNECTION_STATE(&conn->state, CONN_READY); //set ready status so the connection can be closed correctly later if redirect succeeds
+ DBG_ENTER("[redirect] mysqlnd_azure_data::connect::redirect");
+ char redirect_host[MAX_REDIRECT_HOST_LEN] = { 0 };
+ char redirect_user[MAX_REDIRECT_USER_LEN] = { 0 };
+ unsigned int ui_redirect_port = 0;
+ zend_bool serverSupportRedirect = get_redirect_info(conn, redirect_host, redirect_user, &ui_redirect_port);
+ if(!serverSupportRedirect) {
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_ON) {
+ //When REDIRECT_ON, if there is no redirection information contained in the last_message, then redirection is not possible. In this case, abort the connection
+ conn->m->send_close(conn);
+ SET_CLIENT_ERROR(conn->error_info, -1, UNKNOWN_SQLSTATE, "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol.");
+ DBG_ENTER("[redirect]: Server doesnot supoort redirection, Abort the connection. ");
+ goto err;
+ } else {
+ //REDIRECT_PREFERRED, do nothing else for redirection, just use the previous connection
+ DBG_ENTER("[redirect]: Server doesnot supoort redirection, do not need redirection. ");
+ goto after_conn;
+ }
+ }
+
+ //Get here means serverSupportRedirect
MYSQLND_AZURE_CONN_DATA** pdata = mysqlnd_azure_get_is_using_redirect(conn);
- if (!(*pdata)->is_using_redirect && conn->last_message.l > 27 && (strncmp((char*)conn->last_message.s, "Location:", strlen("Location:")) == 0) && ((strstr((char*)conn->last_message.s, "mysql://")) != NULL)) { //there is a redirection connection info we can take use of
- /**
- * Get redirected server information contained in OK packet.
- * Redirection string somehow look like:
- * Location: mysql://redirectedHostName:redirectedPort/user=redirectedUser
- * the minimal len is 27 bytes
- */
- DBG_ENTER("[redirect] mysqlnd_azure_data::connect::redirect");
- char redirect_host[MAX_REDIRECT_HOST_LEN] = { 0 };
- char redirect_user[MAX_REDIRECT_USER_LEN] = { 0 };
- unsigned int ui_redirect_port = 0;
- char redirect_port[8] = { 0 };
-
- int redirect_total_len = conn->last_message.l;
- unsigned char *cur_pos = conn->last_message.s;
- char *p1 = strstr((char*)cur_pos, "//");
- char *p2 = strstr(p1, ":");
- char *p3 = strstr((char*)cur_pos, "user=");
-
- int redirect_host_len = p2 - p1 - 2;
- int redirect_port_len = p3 - p2 - 2;
- int redirect_user_len = redirect_total_len - (p3 + 5 - ((char*)cur_pos));
- if (redirect_host_len <= 0 || redirect_port_len <= 0 || redirect_user_len <= 0) {
- redirect_host_len = redirect_port_len = redirect_user_len = 0;
- }
- else {
- memcpy(redirect_host, p1 + 2, redirect_host_len);
- memcpy(redirect_user, p3 + 5, redirect_user_len);
- memcpy(redirect_port, p2 + 1, redirect_port_len);
- ui_redirect_port = atoi(redirect_port);
- }
+ //Already use redirected connection, or the connection string is a redirected on
+ if((*pdata)->is_using_redirect || (strcmp(redirect_host, hostname.s)==0 && strcmp(redirect_user, username.s)==0 && ui_redirect_port == port)) {
+ DBG_ENTER("[redirect]: Is using redirection, or redirection info are equal to origin, no need to redirect");
+ goto after_conn;
+ }
+
+ //serverSupportRedirect, and currently used conn is not redirected connection, start redirection handshake
+ {
+ DBG_INF_FMT("[redirect]: redirect host=%s user=%s port=%d ", redirect_host, redirect_user, ui_redirect_port);
+ enum_func_status ret = FAIL;
+ MYSQLND* redirect_conneHandle = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, conn->persistent); //init MYSQLND but only need only MYSQLND_CONN_DATA here
+ MYSQLND_CONN_DATA* redirect_conn = redirect_conneHandle->data;
+ redirect_conneHandle->data = NULL;
+ mnd_pefree(redirect_conneHandle, redirect_conneHandle->persistent);
+ redirect_conneHandle = NULL;
+
+ ret = set_redirect_client_options(conn, redirect_conn);
+
+ //when REDIRECT_ON, fallback is not supported. Close the first connection first before establish the redirected connection
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_ON) {
+ conn->m->send_close(conn);
+ conn->m->dtor(conn);
+ pfc = NULL;
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
+
+ //when REDIRECT_ON, previous connection has been closed, need update variable to handle both success/failure case
+ conn = redirect_conn;
+ *pconn = redirect_conn; //use new conn outside for caller
+ pfc = redirect_conn->protocol_frame_codec;
+ }
+
+ //init redirect_conn failed, if REDIRECT_ON, abort connection, if REDIRECT_PREFERRED, use the proxy connection
+ if (ret == FAIL) {
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_ON) {
+ SET_CLIENT_ERROR(conn->error_info, -1, UNKNOWN_SQLSTATE, "[redirect]: mysql redirect fails to copy MYSQLND_CONN_DATA. Abort the connection.");
+ goto err;
+ }
+ else {
+ //REDIRECT_PREFERRED, just use previous direct connection
+ redirect_conn->m->dtor(redirect_conn);
+ goto after_conn;
+ }
+ }
+
+ //init redirect_conn succeeded, use this conn to start a new connection and handshake
+ MYSQLND_CSTRING redirect_hostname = { redirect_host, strlen(redirect_host) };
+ MYSQLND_CSTRING redirect_username = { redirect_user, strlen(redirect_user) };
+ MYSQLND_STRING redirect_transport = redirect_conn->m->get_scheme(redirect_conn, redirect_hostname, &socket_or_pipe, ui_redirect_port, &unix_socket, &named_pipe);
+
+ const MYSQLND_CSTRING redirect_scheme = { redirect_transport.s, redirect_transport.l };
+
+ //set is_using_redirect flag
+ mysqlnd_azure_set_is_using_redirect(redirect_conn, 1);
+
+ enum_func_status redirectState = redirect_conn->m->connect_handshake(redirect_conn, &redirect_scheme, &redirect_username, &password, &database, mysql_flags);
+
+ if (redirectState == PASS) { //handshake with redirect_conn succeeded, replace original connection info with redirect_conn and add the redirect info into cache table
+
+ //add the redirect info into cache table
+ mysqlnd_azure_add_redirect_cache(redirect_conn->persistent, username.s, hostname.s, port, redirect_username.s, redirect_hostname.s, ui_redirect_port);
+
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_PREFERRED) { //when REDIRECT_PREFERRED, previous proxy conn is not closed by now, need close it
+ //close previous proxy connection
+ conn->m->send_close(conn);
+ conn->m->dtor(conn);
+ pfc = NULL;
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
+ //upate conn, pfc, pconn for later user
+ conn = redirect_conn;
+ pfc = redirect_conn->protocol_frame_codec;
+ *pconn = redirect_conn; //use new conn outside for caller
+ }
+
+ //following variable need update for both REDIRECT_PREFERRED and REDIRECT_ON
+
+ //upate host, user, transport for later user
+ hostname = redirect_hostname;
+ username = redirect_username;
+ port = ui_redirect_port;
+ transport = redirect_transport;
+
+ DBG_ENTER("[redirect]: mysql redirect handshake succeeded.");
+
+ } else { //redirect failed. If REDIRECT_ON, redirect failed, fail the connection and report error; if REDIRECT_PREFERRED, use original connection information
+
+ //clear is_using_redirect flag
+ mysqlnd_azure_set_is_using_redirect(redirect_conn, 0);
+
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_PREFERRED) {
+ //free resource, and use original connection information
+ redirect_conn->m->dtor(redirect_conn);
+ if (redirect_transport.s) {
+ mnd_sprintf_free(redirect_transport.s);
+ redirect_transport.s = NULL;
+ }
+ DBG_ENTER("[redirect]: mysql redirect handshake fails, use original connection information!");
+ } else { //REDIRECT_ON
+
+ DBG_ENTER("[redirect]: mysql redirect handshake fails, report error");
+ goto err; //REDIRECT_ON, redirect failed, fail the connection and report error
+
+ }
+ }
+ }
- if (redirect_host_len > 0 && redirect_user_len > 0 && redirect_port_len > 0
- && (strcmp(redirect_host, hostname.s) || strcmp(redirect_user, username.s) || ui_redirect_port != port)) { //currently used conn is not redirected connection
- enum_func_status ret = FAIL;
- MYSQLND* redirect_conneHandle = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, conn->persistent); //init MYSQLND but only need only MYSQLND_CONN_DATA here
- MYSQLND_CONN_DATA* redirect_conn = redirect_conneHandle->data;
- redirect_conneHandle->data = NULL;
- mnd_pefree(redirect_conneHandle, redirect_conneHandle->persistent);
- redirect_conneHandle = NULL;
-
- DBG_INF_FMT("[redirect]: redirect host=%s user=%s port=%s ", redirect_host, redirect_user, redirect_port);
-
- ret = set_redirect_client_options(conn, redirect_conn);
- if (ret == FAIL) { //init redirect_conn failed
- redirect_conn->m->dtor(redirect_conn);
- DBG_ENTER("[redirect]: mysql redirect fails to copy MYSQLND_CONN_DATA, use original connection information!");
- }
- else { //init redirect_conn succeeded, use this conn to start a new connection and handshake
- MYSQLND_CSTRING redirect_hostname = { redirect_host, strlen(redirect_host) };
- MYSQLND_CSTRING redirect_username = { redirect_user, strlen(redirect_user) };
- MYSQLND_STRING redirect_transport = redirect_conn->m->get_scheme(redirect_conn, redirect_hostname, &socket_or_pipe, ui_redirect_port, &unix_socket, &named_pipe);
- const MYSQLND_CSTRING redirect_scheme = { redirect_transport.s, redirect_transport.l };
-
- mysqlnd_azure_set_is_using_redirect(redirect_conn, 1);
-
- enum_func_status redirectState = redirect_conn->m->connect_handshake(redirect_conn, &redirect_scheme, &redirect_username, &password, &database, mysql_flags);
-
- if (redirectState == FAIL) { //handshake with new redirection MYSQLND_CONN_DATA failed, release resource and use original connection
- redirect_conn->m->dtor(redirect_conn);
- if (redirect_transport.s) {
- mnd_sprintf_free(redirect_transport.s);
- redirect_transport.s = NULL;
- }
- DBG_ENTER("[redirect]: mysql redirect handshake fails, use original connection information!");
- }
- else { //handshake with redirect_conn succeeded, close and release resource of original conn and replace it with redirect_conn
-
- //add the redirect info into cache table
- mysqlnd_azure_add_redirect_cache(conn, username.s, hostname.s, port, redirect_username.s, redirect_hostname.s, ui_redirect_port);
-
- conn->m->send_close(conn);
- conn->m->dtor(conn);
- pfc = NULL;
- //upate conn, transport, pconn for later user
- conn = redirect_conn;
- if (transport.s) {
- mnd_sprintf_free(transport.s);
- transport.s = NULL;
- }
- transport = redirect_transport;
- *pconn = redirect_conn; //use new conn outside for caller
-
- ///upate host, user, pfc for later user
- hostname = redirect_hostname;
- username = redirect_username;
- port = ui_redirect_port;
- pfc = redirect_conn->protocol_frame_codec;
-
- DBG_ENTER("[redirect]: mysql redirect handshake succeeded.");
- }
- }
- }
- else {
- DBG_ENTER("[redirect]: redirection info are equal to origin, no need to redirect");
- }
- }
- else {
- DBG_ENTER("[redirect]: already use redirection info or can not find redirection location in ok packet");
- }
}
/*end of Azure Redirection Logic*/
+after_conn:
{
SET_CONNECTION_STATE(&conn->state, CONN_READY);
@@ -534,35 +605,53 @@ MYSQLND_METHOD(mysqlnd_azure, connect)(MYSQLND * conn_handle,
mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", hostname.s);
}
- if(!MYSQLND_AZURE_G(enabled)) {
+ if(MYSQLND_AZURE_G(enableRedirect) == REDIRECT_OFF) {
DBG_ENTER("mysqlnd_azure::connect redirect disabled");
ret = org_conn_d_m.connect(*pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
}
else {
DBG_ENTER("mysqlnd_azure::connect redirect enabled");
- //first check whether the redirect info already cached
- MYSQLND_AZURE_REDIRECT_INFO* redirect_info = mysqlnd_azure_find_redirect_cache(*pconn, username.s, hostname.s, port);
- if (redirect_info != NULL) {
- DBG_ENTER("mysqlnd_azure::connect try the cached info first");
- MYSQLND_CSTRING redirect_host = { redirect_info->redirect_host, strlen(redirect_info->redirect_host) };
- MYSQLND_CSTRING redirect_user = { redirect_info->redirect_user, strlen(redirect_info->redirect_user) };
-
- mysqlnd_azure_set_is_using_redirect(*pconn, 1);
- ret = (*pconn)->m->connect(pconn, redirect_host, redirect_user, password, database, redirect_info->redirect_port, socket_or_pipe, mysql_flags);
- if (ret == FAIL) {
- //remove invalid cache
- mysqlnd_azure_remove_redirect_cache(*pconn, username.s, hostname.s, port);
-
- mysqlnd_azure_set_is_using_redirect(*pconn, 0);
- ret = (*pconn)->m->connect(pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
- }
- }
- else {
- ret = (*pconn)->m->connect(pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
- }
+ //Redirection is only possible with SSL at present. Abort the connection attempt when REDIRECT_ON. Continue with no redirection if REDIRECT_PREFERRED
+ unsigned int temp_flags = (*pconn)->m->get_updated_connect_flags(*pconn, mysql_flags);
+ if(!(temp_flags & CLIENT_SSL)) {
+ if((MYSQLND_AZURE_G(enableRedirect) == REDIRECT_ON)) {
+ SET_CLIENT_ERROR((*pconn)->error_info, -1, UNKNOWN_SQLSTATE, "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL.");
+ (*pconn)->m->free_contents(*pconn);
+ ret = FAIL;
+ }
+ else {
+ ret = org_conn_d_m.connect(*pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
+ }
+ }
+ else { //SSL is enabled
+
+ //first check whether the redirect info already cached
+ MYSQLND_AZURE_REDIRECT_INFO* redirect_info = mysqlnd_azure_find_redirect_cache(username.s, hostname.s, port);
+ if (redirect_info != NULL) {
+ DBG_ENTER("mysqlnd_azure::connect try the cached info first");
+ MYSQLND_CSTRING redirect_host = { redirect_info->redirect_host, strlen(redirect_info->redirect_host) };
+ MYSQLND_CSTRING redirect_user = { redirect_info->redirect_user, strlen(redirect_info->redirect_user) };
+
+ mysqlnd_azure_set_is_using_redirect(*pconn, 1);
+ ret = (*pconn)->m->connect(pconn, redirect_host, redirect_user, password, database, redirect_info->redirect_port, socket_or_pipe, mysql_flags);
+ if (ret == FAIL) {
+ //remove invalid cache
+ mysqlnd_azure_remove_redirect_cache(username.s, hostname.s, port);
+
+ mysqlnd_azure_set_is_using_redirect(*pconn, 0);
+ ret = (*pconn)->m->connect(pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
+ }
+ }
+ else {
+ ret = (*pconn)->m->connect(pconn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
+ }
+
+ }
}
+
(*pconn)->m->local_tx_end(*pconn, this_func, FAIL);
+
}
DBG_RETURN(ret);
}
diff --git a/mysqlnd_azure.h b/mysqlnd_azure.h
index 1d472a1..e37c467 100644
--- a/mysqlnd_azure.h
+++ b/mysqlnd_azure.h
@@ -46,9 +46,9 @@ void mysqlnd_azure_minit_register_hooks();
MYSQLND_AZURE_CONN_DATA** mysqlnd_azure_get_is_using_redirect(const MYSQLND_CONN_DATA *conn);
MYSQLND_AZURE_CONN_DATA** mysqlnd_azure_set_is_using_redirect(MYSQLND_CONN_DATA *conn, zend_bool is_using_redirect);
-enum_func_status mysqlnd_azure_add_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port, const char* redirect_user, const char* redirect_host, int redirect_port);
-enum_func_status mysqlnd_azure_remove_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port);
-MYSQLND_AZURE_REDIRECT_INFO* mysqlnd_azure_find_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port);
+enum_func_status mysqlnd_azure_add_redirect_cache(zend_bool persistent, const char* user, const char* host, int port, const char* redirect_user, const char* redirect_host, int redirect_port);
+enum_func_status mysqlnd_azure_remove_redirect_cache(const char* user, const char* host, int port);
+MYSQLND_AZURE_REDIRECT_INFO* mysqlnd_azure_find_redirect_cache(const char* user, const char* host, int port);
#if defined(ZTS) && defined(COMPILE_DL_MYSQLND_AZURE)
ZEND_TSRMLS_CACHE_EXTERN()
diff --git a/package.xml b/package.xml
index f17aa4c..b1df1fd 100644
--- a/package.xml
+++ b/package.xml
@@ -16,19 +16,25 @@
Qianqian.Bu@microsoft.comyes
- 2019-11-20
-
+ 2019-12-10
+
- 1.0.2
- 1.0.2
+ 1.1.0beta1
+ 1.1.0beta1
- stable
- stable
+ beta
+ betaPHP License
-- apply fix for database null/empty problem change in https://github.com/php/php-src/pull/4340 in MYSQLND_METHOD(mysqlnd_conn_data, connect) to MYSQLND_METHOD(mysqlnd_azure_data, connect)
+- 1. Rename option mysqlnd_azure.enabled to mysqlnd_azure.enableRedirect.
+- 2. If enableRedirect is with value "on", ssl is off, no connection will be made, return error "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL."
+- 3. If enableRedirect is with value "on", but on server side redirection is not available, and there is no last message in OK packet, abort the first connection and return error "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol."
+- 4. If enableRedirect is with value "on" and server supports redirection, but the redirected connection failed for any reason, also abort the first proxy connection. Return the error of the redirected connection.
+- 5. A new option for mysqlnd_azure.enableRedirect is introduced with name "preferred". When enableRedirect is "preferred", it will use redirection if possible.
+ If connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection.
+- 6. The cache implementation has been changed from module global to true global to improve share range for multi-thread scenario.
@@ -39,11 +45,11 @@
-
-
+
+
+
-
@@ -65,6 +71,27 @@
mysqlnd_azure
+
+
+ 1.1.0beta1
+ 1.1.0beta1
+
+
+ beta
+ beta
+
+ 2019-12-10
+ PHP License
+
+- 1. Rename option mysqlnd_azure.enabled to mysqlnd_azure.enableRedirect, and add a new option value "preferred".
+- 2. If enableRedirect is with value "on", ssl is off, no connection will be made, return error "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL."
+- 3. If enableRedirect is with value "on", but on server side redirection is not available, and there is no last message in OK packet, abort the first connection and return error "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol."
+- 4. If enableRedirect is with value "on" and server supports redirection, but the redirected connection failed for any reason, also abort the first proxy connection. Return the error of the redirected connection.
+- 5. A new option for mysqlnd_azure.enableRedirect is introduced with name "preferred". When enableRedirect is "preferred", it will use redirection if possible.
+ If connection does not use SSL, or server does not support redirection, or redirected connection fails to connect for any non-fatal reason while the proxy connection is still a valid one, it will fallback to the first proxy connection.
+- 6. The cache implementation has been changed from module global to true global to improve share range for multi-thread scenario.
+
+ 1.0.2
diff --git a/php_mysqlnd_azure.c b/php_mysqlnd_azure.c
index d485326..294431a 100644
--- a/php_mysqlnd_azure.c
+++ b/php_mysqlnd_azure.c
@@ -28,39 +28,52 @@
ZEND_DECLARE_MODULE_GLOBALS(mysqlnd_azure)
-/* {{{ PHP_GINIT_FUNCTION */
-static PHP_GINIT_FUNCTION(mysqlnd_azure)
-{
-#if defined(COMPILE_DL_MYSQLND_AZURE) && defined(ZTS)
- ZEND_TSRMLS_CACHE_UPDATE();
-#endif
- mysqlnd_azure_globals->enabled = 0;
- mysqlnd_azure_globals->redirectCache = NULL;
-}
-/* }}} */
-/* {{{ PHP_GSHUTDOWN_FUNCTION */
-static PHP_GSHUTDOWN_FUNCTION(mysqlnd_azure)
+/* {{{ OnUpdateEnableRedirect */
+static ZEND_INI_MH(OnUpdateEnableRedirect)
{
- if (mysqlnd_azure_globals->redirectCache) {
- zend_hash_destroy(mysqlnd_azure_globals->redirectCache);
- mnd_pefree(mysqlnd_azure_globals->redirectCache, 1);
- mysqlnd_azure_globals->redirectCache = NULL;
- }
+ if ((ZSTR_LEN(new_value) == 9 && strcasecmp("preferred", ZSTR_VAL(new_value)) == 0)
+ || (ZSTR_LEN(new_value) == 1 && strcasecmp("2", ZSTR_VAL(new_value)) == 0)) {
+
+ MYSQLND_AZURE_G(enableRedirect) = REDIRECT_PREFERRED;
+
+ } else if ((ZSTR_LEN(new_value) == 2 && strcasecmp("on", ZSTR_VAL(new_value)) == 0)
+ || (ZSTR_LEN(new_value) == 3 && strcasecmp("yes", ZSTR_VAL(new_value)) == 0)
+ || (ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0)
+ || (ZSTR_LEN(new_value) == 1 && strcasecmp("1", ZSTR_VAL(new_value)) == 0)) {
+
+ MYSQLND_AZURE_G(enableRedirect) = REDIRECT_ON;
+
+ } else {
+
+ MYSQLND_AZURE_G(enableRedirect) = REDIRECT_OFF;
+
+ }
+
+ return SUCCESS;
+
}
/* }}} */
+
/* {{{ PHP_INI */
-/*
- It is handy to allow users to disable any mysqlnd plugin globally - not only for debugging :-)
- Because we register our plugin in MINIT changes to mysqlnd_ed.enabled shall be bound to
- INI_SYSTEM (and PHP restarts).
-*/
PHP_INI_BEGIN()
-STD_PHP_INI_ENTRY("mysqlnd_azure.enabled", "0", PHP_INI_ALL, OnUpdateBool, enabled, zend_mysqlnd_azure_globals, mysqlnd_azure_globals)
+STD_PHP_INI_ENTRY("mysqlnd_azure.enableRedirect", "preferred", PHP_INI_ALL, OnUpdateEnableRedirect, enableRedirect, zend_mysqlnd_azure_globals, mysqlnd_azure_globals)
PHP_INI_END()
/* }}} */
+
+/* {{{ PHP_GINIT_FUNCTION */
+static PHP_GINIT_FUNCTION(mysqlnd_azure)
+{
+#if defined(COMPILE_DL_MYSQLND_AZURE) && defined(ZTS)
+ ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+ mysqlnd_azure_globals->enableRedirect = REDIRECT_PREFERRED;
+}
+/* }}} */
+
+
/* {{{ PHP_MINIT_FUNCTION
*/
static PHP_MINIT_FUNCTION(mysqlnd_azure)
@@ -70,6 +83,11 @@ static PHP_MINIT_FUNCTION(mysqlnd_azure)
/* register mysqlnd plugin */
mysqlnd_azure_minit_register_hooks();
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_lock_alloc();
+ #endif
+ redirectCache = NULL;
+
return SUCCESS;
}
@@ -79,6 +97,16 @@ static PHP_MSHUTDOWN_FUNCTION(mysqlnd_azure)
{
UNREGISTER_INI_ENTRIES();
+ if (redirectCache) {
+ zend_hash_destroy(redirectCache);
+ mnd_pefree(redirectCache, 1);
+ redirectCache = NULL;
+ }
+
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_lock_free();
+ #endif
+
return SUCCESS;
}
@@ -87,8 +115,8 @@ static PHP_MSHUTDOWN_FUNCTION(mysqlnd_azure)
PHP_MINFO_FUNCTION(mysqlnd_azure)
{
php_info_print_table_start();
- php_info_print_table_header(2, "mysqlnd_azure", "enabled");
- php_info_print_table_row(2, "enabled", MYSQLND_AZURE_G(enabled)? "Yes":"No");
+ php_info_print_table_header(2, "mysqlnd_azure", "enableRedirect");
+ php_info_print_table_row(2, "enableRedirect", MYSQLND_AZURE_G(enableRedirect) == REDIRECT_OFF ? "off" : (MYSQLND_AZURE_G(enableRedirect) == REDIRECT_ON ? "on" : "preferred"));
php_info_print_table_end();
}
/* }}} */
@@ -103,17 +131,17 @@ zend_module_entry mysqlnd_azure_module_entry = {
STANDARD_MODULE_HEADER_EX,
NULL,
mysqlnd_azure_deps,
- EXT_MYSQLND_AZURE_NAME,
+ PHP_MYSQLND_AZURE_NAME,
NULL,
PHP_MINIT(mysqlnd_azure),
PHP_MSHUTDOWN(mysqlnd_azure),
NULL,
NULL,
PHP_MINFO(mysqlnd_azure),
- EXT_MYSQLND_AZURE_VERSION,
+ PHP_MYSQLND_AZURE_VERSION,
PHP_MODULE_GLOBALS(mysqlnd_azure),
PHP_GINIT(mysqlnd_azure),
- PHP_GSHUTDOWN(mysqlnd_azure),
+ NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
diff --git a/php_mysqlnd_azure.h b/php_mysqlnd_azure.h
index 8c90a08..15887e2 100644
--- a/php_mysqlnd_azure.h
+++ b/php_mysqlnd_azure.h
@@ -28,14 +28,32 @@
extern zend_module_entry mysqlnd_azure_module_entry;
# define phpext_mysqlnd_azure_ptr &mysqlnd_azure_module_entry
-#define EXT_MYSQLND_AZURE_NAME "mysqlnd_azure"
-#define EXT_MYSQLND_AZURE_VERSION "1.0.2"
+#define PHP_MYSQLND_AZURE_NAME "mysqlnd_azure"
+#define PHP_MYSQLND_AZURE_VERSION "1.1.0beta1"
+
+/* true global environment */
+HashTable* redirectCache;
+
+#ifdef ZTS
+/* exclusive locking for redirectCache*/
+void mysqlnd_azure_redirect_cache_lock(void);
+void mysqlnd_azure_redirect_cache_unlock(void);
+void mysqlnd_azure_redirect_cache_lock_alloc(void);
+void mysqlnd_azure_redirect_cache_lock_free(void);
+#endif
+
+typedef enum _mysqlnd_azure_redirect_mode {
+ REDIRECT_OFF = 0, /* completely disabled */
+ REDIRECT_ON = 1, /* enabled with fallback */
+ REDIRECT_PREFERRED = 2 /* enabled without fallback, block if redirection fail */
+} mysqlnd_azure_redirect_mode;
+
ZEND_BEGIN_MODULE_GLOBALS(mysqlnd_azure)
- zend_bool enabled;
- HashTable* redirectCache;
+ mysqlnd_azure_redirect_mode enableRedirect;
ZEND_END_MODULE_GLOBALS(mysqlnd_azure)
+
PHPAPI ZEND_EXTERN_MODULE_GLOBALS(mysqlnd_azure)
#define MYSQLND_AZURE_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mysqlnd_azure, v)
diff --git a/redirect_cache.c b/redirect_cache.c
index 84dbd2a..46ccc35 100644
--- a/redirect_cache.c
+++ b/redirect_cache.c
@@ -29,6 +29,43 @@
#include "ext/mysqlnd/mysqlnd_statistics.h"
#include "ext/mysqlnd/mysqlnd_connection.h"
+#ifdef ZTS
+#include
+#endif
+
+#ifdef ZTS
+static MUTEX_T redirect_cache_mutex;
+
+/* {{{ mysqlnd_azure_redirect_cache_lock */
+void mysqlnd_azure_redirect_cache_lock(void)
+{
+ tsrm_mutex_lock(redirect_cache_mutex);
+}
+/* }}} */
+
+/* {{{ mysqlnd_azure_redirect_cache_unlock */
+void mysqlnd_azure_redirect_cache_unlock(void)
+{
+ tsrm_mutex_unlock(redirect_cache_mutex);
+}
+/* }}} */
+
+/* {{{ mysqlnd_azure_redirect_cache_lock_alloc */
+void mysqlnd_azure_redirect_cache_lock_alloc(void)
+{
+ redirect_cache_mutex = tsrm_mutex_alloc();
+}
+/* }}} */
+
+/* {{{ mysqlnd_azure_redirect_cache_lock_free */
+void mysqlnd_azure_redirect_cache_lock_free(void)
+{
+ tsrm_mutex_free(redirect_cache_mutex);
+}
+/* }}} */
+
+#endif
+
/* {{{ mysqlnd_azure_redirect_info_dtor */
static void mysqlnd_azure_redirect_info_dtor(zval *zv)
{
@@ -76,58 +113,79 @@ MYSQLND_AZURE_CONN_DATA** mysqlnd_azure_set_is_using_redirect(MYSQLND_CONN_DATA
/* }}} */
/* {{{ mysqlnd_azure_add_redirect_cache */
-enum_func_status mysqlnd_azure_add_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port, const char* redirect_user, const char* redirect_host, int redirect_port)
+enum_func_status mysqlnd_azure_add_redirect_cache(zend_bool persistent, const char* user, const char* host, int port, const char* redirect_user, const char* redirect_host, int redirect_port)
{
- if (MYSQLND_AZURE_G(redirectCache) == NULL) {
- MYSQLND_AZURE_G(redirectCache) = mnd_pemalloc(sizeof(HashTable), 1);
- zend_hash_init(MYSQLND_AZURE_G(redirectCache), 0, NULL, mysqlnd_azure_redirect_info_dtor, 1);
- }
-
- char *key = NULL;
+ char *key = NULL;
mnd_sprintf(&key, MAX_REDIRECT_HOST_LEN+ MAX_REDIRECT_USER_LEN+8, "%s_%s_%d", user, host, port);
- MYSQLND_AZURE_REDIRECT_INFO* redirect_info = pemalloc(sizeof(MYSQLND_AZURE_REDIRECT_INFO), conn->persistent);
- redirect_info->redirect_user = mnd_pestrndup(redirect_user, strlen(redirect_user), conn->persistent);
- redirect_info->redirect_host = mnd_pestrndup(redirect_host, strlen(redirect_host), conn->persistent);
+ MYSQLND_AZURE_REDIRECT_INFO* redirect_info = pemalloc(sizeof(MYSQLND_AZURE_REDIRECT_INFO), persistent);
+ redirect_info->redirect_user = mnd_pestrndup(redirect_user, strlen(redirect_user), persistent);
+ redirect_info->redirect_host = mnd_pestrndup(redirect_host, strlen(redirect_host), persistent);
redirect_info->redirect_port = redirect_port;
- zend_hash_str_update_ptr(MYSQLND_AZURE_G(redirectCache), key, strlen(key), redirect_info);
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_lock();
+ #endif
+ if (redirectCache == NULL) {
+ redirectCache = mnd_pemalloc(sizeof(HashTable), 1);
+ zend_hash_init(redirectCache, 0, NULL, mysqlnd_azure_redirect_info_dtor, 1);
+ }
+
+ zend_hash_str_update_ptr(redirectCache, key, strlen(key), redirect_info);
- mnd_sprintf_free(key);
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_unlock();
+ #endif
+
+ mnd_sprintf_free(key);
return PASS;
}
/* }}} */
/* {{{ mysqlnd_azure_remove_redirect_cache */
-enum_func_status mysqlnd_azure_remove_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port)
+enum_func_status mysqlnd_azure_remove_redirect_cache(const char* user, const char* host, int port)
{
- if (MYSQLND_AZURE_G(redirectCache) == NULL)
- return PASS;
+ if(redirectCache != NULL) {
+ char *key = NULL;
+ mnd_sprintf(&key, MAX_REDIRECT_HOST_LEN + MAX_REDIRECT_USER_LEN + 8, "%s_%s_%d", user, host, port);
- char *key = NULL;
- mnd_sprintf(&key, MAX_REDIRECT_HOST_LEN + MAX_REDIRECT_USER_LEN + 8, "%s_%s_%d", user, host, port);
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_lock();
+ #endif
- zend_hash_str_del(MYSQLND_AZURE_G(redirectCache), key, strlen(key));
+ zend_hash_str_del(redirectCache, key, strlen(key));
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_unlock();
+ #endif
- mnd_sprintf_free(key);
+ mnd_sprintf_free(key);
+ }
return PASS;
}
/* }}} */
/* {{{ mysqlnd_azure_find_redirect_cache */
-MYSQLND_AZURE_REDIRECT_INFO* mysqlnd_azure_find_redirect_cache(const MYSQLND_CONN_DATA* conn, const char* user, const char* host, int port)
+MYSQLND_AZURE_REDIRECT_INFO* mysqlnd_azure_find_redirect_cache(const char* user, const char* host, int port)
{
- if (MYSQLND_AZURE_G(redirectCache) == NULL)
- return NULL;
-
- char *key = NULL;
- mnd_sprintf(&key, MAX_REDIRECT_HOST_LEN + MAX_REDIRECT_USER_LEN + 8, "%s_%s_%d", user, host, port);
-
- void* zv_dest = zend_hash_str_find_ptr(MYSQLND_AZURE_G(redirectCache), key, strlen(key));
- mnd_sprintf_free(key);
-
- return (MYSQLND_AZURE_REDIRECT_INFO*)zv_dest;
+ if (redirectCache != NULL) {
+ char *key = NULL;
+ mnd_sprintf(&key, MAX_REDIRECT_HOST_LEN + MAX_REDIRECT_USER_LEN + 8, "%s_%s_%d", user, host, port);
+
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_lock();
+ #endif
+
+ void* zv_dest = zend_hash_str_find_ptr(redirectCache, key, strlen(key));
+ #ifdef ZTS
+ mysqlnd_azure_redirect_cache_unlock();
+ #endif
+ mnd_sprintf_free(key);
+
+ return (MYSQLND_AZURE_REDIRECT_INFO*)zv_dest;
+ } else {
+ return NULL;
+ }
}
-/* }}} */
+/* }}} */
\ No newline at end of file
diff --git a/tests/mysqli_azure_redirection_disabled.phpt b/tests/mysqli_azure_redirection_disabled.phpt
deleted file mode 100644
index 1d82f88..0000000
--- a/tests/mysqli_azure_redirection_disabled.phpt
+++ /dev/null
@@ -1,38 +0,0 @@
---TEST--
-Azure redirection test for servers with mysqlnd_azure.enabled=0
---INI--
-mysqlnd_azure.enabled=0
---SKIPIF--
-
---FILE--
-host_info."\n";
-echo $link->info."\n";
-if(substr($link->host_info, 0, strlen($host)) == $host)
- echo "1\n";
-else
- echo "0\n";
-mysqli_close($link);
-echo "Done\n";
-?>
---EXPECTF--
-%s
-Location: mysql://tr%d.%s:%d/user=%s@%s
-1
-Done
diff --git a/tests/mysqli_azure_redirection_enabled.phpt b/tests/mysqli_azure_redirection_enabled.phpt
deleted file mode 100644
index 8c03279..0000000
--- a/tests/mysqli_azure_redirection_enabled.phpt
+++ /dev/null
@@ -1,38 +0,0 @@
---TEST--
-Azure redirection test for servers when mysqlnd_azure.enabled
---INI--
-mysqlnd_azure.enabled=1
---SKIPIF--
-
---FILE--
-host_info."\n";
-echo $link->info."\n";
-if(substr($link->host_info, 0, strlen($host)) == $host)
- echo "1\n";
-else
- echo "0\n";
-mysqli_close($link);
-echo "Done\n";
-?>
---EXPECTF--
-tr%d.%s
-Location: mysql://tr%d.%s:%d/user=%s@%s
-0
-Done
diff --git a/tests/mysqli_azure_redirection_off.phpt b/tests/mysqli_azure_redirection_off.phpt
new file mode 100644
index 0000000..517c28d
--- /dev/null
+++ b/tests/mysqli_azure_redirection_off.phpt
@@ -0,0 +1,72 @@
+--TEST--
+Azure redirection test for servers when mysqlnd_azure.enableRedirect="off"
+--INI--
+mysqlnd_azure.enableRedirect="off"
+--SKIPIF--
+
+--FILE--
+host_info."\n";
+$last_message = $link->info;
+
+//Server supports redirection
+if(strlen($last_message) > 27 && strcmp(substr($last_message, 0, strlen("Location:")), "Location:")==0) {
+ if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[003] pass\n";
+ else
+ echo "[003] fail\n";
+}
+else { //Server does not support redirection, use the proxy connection
+ if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[003] pass\n";
+ else
+ echo "[003] fail\n";
+}
+
+mysqli_close($link);
+
+//Step 3: check connection result when not use SSL
+$link = mysqli_init();
+$ret = @mysqli_real_connect($link, $host, $user, $passwd, $db, $port, NULL, NULL);
+if (!$ret || !is_object($link))
+{
+ printf("[004] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n - [%d] %s without ssl\n",
+ $host, $user, $db, $port, $socket, mysqli_connect_errno(), mysqli_connect_error());
+ die();
+}
+
+if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[004] pass\n";
+else
+ echo "[004] fail\n";
+
+echo "Done\n";
+?>
+--EXPECTF--
+off
+%s
+[003] pass
+[004] pass
+Done
diff --git a/tests/mysqli_azure_redirection_on.phpt b/tests/mysqli_azure_redirection_on.phpt
new file mode 100644
index 0000000..a849ec1
--- /dev/null
+++ b/tests/mysqli_azure_redirection_on.phpt
@@ -0,0 +1,68 @@
+--TEST--
+Azure redirection test for servers when mysqlnd_azure.enableRedirect="on"
+--INI--
+mysqlnd_azure.enableRedirect="on"
+--SKIPIF--
+
+--FILE--
+info;
+
+//Server supports redirection
+if(strlen($last_message) > 27 && strcmp(substr($last_message, 0, strlen("Location:")), "Location:")==0) {
+ if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[003] fail\n";
+ else
+ echo "[003] pass\n";
+}
+else { //Server does not support redirection
+ $lastError = error_get_last()["message"];
+ if (strpos($lastError, "Connection aborted because redirection is not enabled on the MySQL server or the network package doesn't meet meet redirection protocol.") !== false)
+ echo "[003] pass\n";
+ else
+ echo "[003] fail\n";
+}
+
+mysqli_close($link);
+
+//Step 3: check connection result when not use SSL
+$link = mysqli_init();
+$ret = @mysqli_real_connect($link, $host, $user, $passwd, $db, $port, NULL, NULL);
+if ($ret)
+{
+ printf("[004] When enableRedirect=on, connect without SSL expects failure, got pass\n");
+ die();
+}
+
+$lastError = error_get_last()["message"];
+if (strpos($lastError, "mysqlnd_azure.enableRedirect is on, but SSL option is not set in connection string. Redirection is only possible with SSL.") !== false)
+ echo "[004] pass\n";
+ else
+ echo "[004] fail\n";
+
+echo "Done\n";
+?>
+--EXPECTF--
+on
+[003] pass
+[004] pass
+Done
diff --git a/tests/mysqli_azure_redirection_preferred.phpt b/tests/mysqli_azure_redirection_preferred.phpt
new file mode 100644
index 0000000..76f5df3
--- /dev/null
+++ b/tests/mysqli_azure_redirection_preferred.phpt
@@ -0,0 +1,72 @@
+--TEST--
+Azure redirection test for servers when mysqlnd_azure.enableRedirect="preferred"
+--INI--
+mysqlnd_azure.enableRedirect="preferred"
+--SKIPIF--
+
+--FILE--
+host_info."\n";
+$last_message = $link->info;
+
+//Server supports redirection
+if(strlen($last_message) > 27 && strcmp(substr($last_message, 0, strlen("Location:")), "Location:")==0) {
+ if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[003] fail\n";
+ else
+ echo "[003] pass\n";
+}
+else { //Server does not support redirection, use the proxy connection
+ if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[003] pass\n";
+ else
+ echo "[003] fail\n";
+}
+
+mysqli_close($link);
+
+//Step 3: check connection result when not use SSL
+$link = mysqli_init();
+$ret = @mysqli_real_connect($link, $host, $user, $passwd, $db, $port, NULL, NULL);
+if (!$ret || !is_object($link))
+{
+ printf("[004] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n - [%d] %s without ssl\n",
+ $host, $user, $db, $port, $socket, mysqli_connect_errno(), mysqli_connect_error());
+ die();
+}
+
+if(substr($link->host_info, 0, strlen($host)) == $host)
+ echo "[004] pass\n";
+else
+ echo "[004] fail\n";
+
+echo "Done\n";
+?>
+--EXPECTF--
+preferred
+%s
+[003] pass
+[004] pass
+Done
diff --git a/tests/skipifconnectfailure.inc b/tests/skipifconnectfailure.inc
deleted file mode 100644
index 87adc0d..0000000
--- a/tests/skipifconnectfailure.inc
+++ /dev/null
@@ -1,16 +0,0 @@
-info || substr( $link->info, 0, 18 ) != "Location: mysql://") {
- mysqli_close($link);
- die("skip this mysql server does not support redirection, do not find valid redirection info");
- }
- mysqli_close($link);
-?>