Skip to content

Commit 10eb0b3

Browse files
committed
Fixed bug #79000
Don't report EAGAIN/EWOULDBLOCK as errors for fwrite on non-blocking socket streams. This matches behavior for fread, as well as behavior for plain file streams. Closes GH-5026.
1 parent d4ba1fd commit 10eb0b3

File tree

3 files changed

+46
-17
lines changed

3 files changed

+46
-17
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ PHP NEWS
2828
- Spl:
2929
. Fixed bug #78976 (SplFileObject::fputcsv returns -1 on failure). (cmb)
3030

31+
- Standard:
32+
. Fixed bug #79000 (Non-blocking socket stream reports EAGAIN as error).
33+
(Nikita)
34+
3135
18 Dec 2019, PHP 7.4.1
3236

3337
- Core:
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Bug #79000: Non-blocking socket stream reports EAGAIN as error
3+
--SKIPIF--
4+
<?php
5+
if (PHP_OS_FAMILY == 'Windows') die('skip Not for Windows');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
[$sock1, $sock2] = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
11+
$str = str_repeat('a', 1000000);
12+
stream_set_blocking($sock1, false);
13+
var_dump(fwrite($sock1, $str));
14+
var_dump(fwrite($sock1, $str));
15+
16+
?>
17+
--EXPECTF--
18+
int(%d)
19+
int(%d)

main/streams/xp_socket.c

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,30 +75,36 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun
7575
didwrite = send(sock->socket, buf, XP_SOCK_BUF_SIZE(count), (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
7676

7777
if (didwrite <= 0) {
78-
int err = php_socket_errno();
7978
char *estr;
79+
int err = php_socket_errno();
80+
if (err == EWOULDBLOCK || err == EAGAIN) {
81+
if (sock->is_blocked) {
82+
int retval;
8083

81-
if (sock->is_blocked && (err == EWOULDBLOCK || err == EAGAIN)) {
82-
int retval;
83-
84-
sock->timeout_event = 0;
84+
sock->timeout_event = 0;
8585

86-
do {
87-
retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
86+
do {
87+
retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
8888

89-
if (retval == 0) {
90-
sock->timeout_event = 1;
91-
break;
92-
}
89+
if (retval == 0) {
90+
sock->timeout_event = 1;
91+
break;
92+
}
9393

94-
if (retval > 0) {
95-
/* writable now; retry */
96-
goto retry;
97-
}
94+
if (retval > 0) {
95+
/* writable now; retry */
96+
goto retry;
97+
}
9898

99-
err = php_socket_errno();
100-
} while (err == EINTR);
99+
err = php_socket_errno();
100+
} while (err == EINTR);
101+
} else {
102+
/* EWOULDBLOCK/EAGAIN is not an error for a non-blocking stream.
103+
* Report zero byte write instead. */
104+
return 0;
105+
}
101106
}
107+
102108
estr = php_socket_strerror(err, NULL, 0);
103109
php_error_docref(NULL, E_NOTICE, "send of " ZEND_LONG_FMT " bytes failed with errno=%d %s",
104110
(zend_long)count, err, estr);

0 commit comments

Comments
 (0)