diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 29b46f908..1e8b6cc69 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -567,11 +567,13 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) { switch((SSL_get_error(ssl, rc))) { case SSL_ERROR_WANT_READ: - if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1) + /* use low timeout, see ma_tls_read */ + if (pvio->methods->wait_io_or_timeout(pvio, TRUE, 5) < 1) try_connect= 0; break; case SSL_ERROR_WANT_WRITE: - if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1) + /* use low timeout, see ma_tls_read */ + if (pvio->methods->wait_io_or_timeout(pvio, TRUE, 5) < 1) try_connect= 0; break; default: @@ -655,7 +657,10 @@ ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length) int error= SSL_get_error((SSL *)ctls->ssl, rc); if (error != SSL_ERROR_WANT_READ) break; - if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1) + /* To get a more precise error message than "resource temporary + unavailable" (=errno 11) after read timeout occured, we check + the socket status using a very small timeout (=5 ms) */ + if (pvio->methods->wait_io_or_timeout(pvio, TRUE, 5) < 1) break; } if (rc <= 0) @@ -676,7 +681,8 @@ ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length) int error= SSL_get_error((SSL *)ctls->ssl, rc); if (error != SSL_ERROR_WANT_WRITE) break; - if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1) + /* use low timeout, see ma_tls_read */ + if (pvio->methods->wait_io_or_timeout(pvio, TRUE, 5) < 1) break; } if (rc <= 0) diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index e32f7fba6..82930fe9f 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -2374,7 +2374,41 @@ static int test_parsec(MYSQL *my) return OK; } +/* Test for PR250 */ +int test_tls_timeout(MYSQL *unused __attribute__((unused))) +{ + unsigned int connect_timeout= 5; + unsigned int read_write_timeout= 10; + int rc; + time_t start, elapsed; + + MYSQL *mysql= mysql_init(NULL); + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&connect_timeout); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS, 1)) + { + mysql_close(mysql); + return FAIL; + } + + start= time(NULL); + rc= mysql_query(mysql, "SET @a:=SLEEP(12)"); + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + FAIL_IF((unsigned int)elapsed > read_write_timeout + 1, "timeout ignored"); + + FAIL_IF(!rc, "expected timeout error"); + diag("Error: %s", mysql_error(mysql)); + + mysql_close(mysql); + return OK; +} + + struct my_tests_st my_tests[] = { + {"test_tls_timeout", test_tls_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_parsec", test_parsec, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc505", test_conc505, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL}, @@ -2423,6 +2457,7 @@ struct my_tests_st my_tests[] = { {"test_connection_timeout", test_connection_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_connection_timeout2", test_connection_timeout2, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_connection_timeout3", test_connection_timeout3, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_tls_timeout", test_tls_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} };