Skip to content
This repository was archived by the owner on Apr 27, 2021. It is now read-only.

Commit f76f8b6

Browse files
committed
Use a custom fwrite behavior, to avoid random EPIPE errors
1 parent ac6eacc commit f76f8b6

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

src/Client.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ private function doConnect($options)
220220

221221
private function doSend($connection, $content)
222222
{
223-
return fwrite($connection, $content);
223+
return $this->fwrite($connection, $content);
224224
}
225225

226226
private function doGet($connection)
@@ -254,4 +254,54 @@ private static function handleInternalError($type, $message, $file, $line)
254254
{
255255
throw new \ErrorException($message, 0, $type, $file, $line);
256256
}
257+
258+
/**
259+
* Replace fwrite behavior as api is broken in PHP.
260+
*
261+
* @see https://secure.phabricator.com/rPHU69490c53c9c2ef2002bc2dd4cecfe9a4b080b497
262+
*
263+
* @param resource $stream The stream resource
264+
* @param string $bytes Bytes written in the stream
265+
*
266+
* @return bool|int false if pipe is broken, number of bytes written otherwise
267+
*/
268+
private function fwrite($stream, $bytes)
269+
{
270+
if (!strlen($bytes)) {
271+
return 0;
272+
}
273+
$result = @fwrite($stream, $bytes);
274+
if (0 !== $result) {
275+
// In cases where some bytes are witten (`$result > 0`) or
276+
// an error occurs (`$result === false`), the behavior of fwrite() is
277+
// correct. We can return the value as-is.
278+
return $result;
279+
}
280+
// If we make it here, we performed a 0-length write. Try to distinguish
281+
// between EAGAIN and EPIPE. To do this, we're going to `stream_select()`
282+
// the stream, write to it again if PHP claims that it's writable, and
283+
// consider the pipe broken if the write fails.
284+
$read = [];
285+
$write = [$stream];
286+
$except = [];
287+
@stream_select($read, $write, $except, 0);
288+
if (!$write) {
289+
// The stream isn't writable, so we conclude that it probably really is
290+
// blocked and the underlying error was EAGAIN. Return 0 to indicate that
291+
// no data could be written yet.
292+
return 0;
293+
}
294+
// If we make it here, PHP **just** claimed that this stream is writable, so
295+
// perform a write. If the write also fails, conclude that these failures are
296+
// EPIPE or some other permanent failure.
297+
$result = @fwrite($stream, $bytes);
298+
if (0 !== $result) {
299+
// The write worked or failed explicitly. This value is fine to return.
300+
return $result;
301+
}
302+
// We performed a 0-length write, were told that the stream was writable, and
303+
// then immediately performed another 0-length write. Conclude that the pipe
304+
// is broken and return `false`.
305+
return false;
306+
}
257307
}

0 commit comments

Comments
 (0)