Suppress stream errors in mysqlnd

mysqlnd currently sets error_reporting=0 to suppress errors while
writing to streams. Unfortunately these errors are still visible
to userland error handlers, which is a source of confusion.
See for example https://bugs.php.net/bug.php?id=80412.

Instead add a stream flag that suppresses the emission of
read/write errors in the first place, and set it in mysqlnd.

I think it might be useful to have this option for userland as
well in the future, but for now this is just an internal
mechanism.

Closes GH-6458.
This commit is contained in:
Nikita Popov 2020-11-26 12:30:17 +01:00
parent a0baa09b99
commit 24a19cc232
5 changed files with 17 additions and 14 deletions

View File

@ -263,6 +263,7 @@ MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const
}
net_stream->chunk_size = vio->data->options.net_read_buffer_size;
net_stream->flags |= PHP_STREAM_FLAG_SUPPRESS_ERRORS;
}
DBG_VOID_RETURN;

View File

@ -971,7 +971,6 @@ size_t php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn, void * _packet)
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
const unsigned int error_reporting = EG(error_reporting);
size_t sent = 0;
DBG_ENTER("php_mysqlnd_cmd_write");
@ -981,10 +980,6 @@ size_t php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn, void * _packet)
*/
pfc->data->m.reset(pfc, stats, error_info);
if (error_reporting) {
EG(error_reporting) = 0;
}
MYSQLND_INC_CONN_STATISTIC(stats, STAT_PACKETS_SENT_CMD);
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
@ -1017,10 +1012,6 @@ size_t php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn, void * _packet)
}
}
end:
if (error_reporting) {
/* restore error reporting */
EG(error_reporting) = error_reporting;
}
if (!sent) {
SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}

View File

@ -181,6 +181,10 @@ struct _php_stream_wrapper {
#define PHP_STREAM_FLAG_NO_FCLOSE 0x80
/* Suppress generation of PHP warnings on stream read/write errors.
* Currently for internal use only. */
#define PHP_STREAM_FLAG_SUPPRESS_ERRORS 0x100
#define PHP_STREAM_FLAG_WAS_WRITTEN 0x80000000
struct _php_stream {

View File

@ -358,7 +358,9 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
/* TODO: Should this be treated as a proper error or not? */
return bytes_written;
}
php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
}
}
return bytes_written;
} else {
@ -426,7 +428,9 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
} else if (errno == EINTR) {
/* TODO: Should this be treated as a proper error or not? */
} else {
php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
}
/* TODO: Remove this special-case? */
if (errno != EBADF) {

View File

@ -103,10 +103,13 @@ retry:
}
}
estr = php_socket_strerror(err, NULL, 0);
php_error_docref(NULL, E_NOTICE, "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
estr = php_socket_strerror(err, NULL, 0);
php_error_docref(NULL, E_NOTICE,
"Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
(zend_long)count, err, estr);
efree(estr);
efree(estr);
}
}
if (didwrite > 0) {