`https` streams on Ubuntu 22.04 might emit PHP warnings during `fread()`
TimWolla opened this issue · comments
PHP version: 8.1.2
Description
Because of php/php-src#8369 a https
stream might emit a Warning when calling fread()
on the underlying stream. If a user of PSR-7 uses an error handler that converts warnings into Exceptions then the call to ->read()
might violate read()
's contract to throw a RuntimeException
is cases of failure.
How to reproduce
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
error_reporting(E_ALL);
function exception_error_handler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
require('vendor/autoload.php');
$client = new Client([
'stream' => true,
]);
$request = new Request(
'GET',
'https://www.umwelt.niedersachsen.de/assets/image/1200/216904',
);
$response = $client->send($request, [
'timeout' => 1,
]);
$body = $response->getBody();
while (!$body->eof()) {
$v = $body->read(2048);
var_dump(gettype($v));
}
# var_dump($response);
Possible Solution
The call to fread()
:
Line 232 in 58a607d
can be prefixed with @
to fix this issue. Note that fread()
won't return false
for this warning.. Alternatively a custom error handler might be attached before the call and detached afterwards.
Additional context
root@ubuntu-22:~/guzzle# php test.php
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
PHP Fatal error: Uncaught ErrorException: fread(): SSL operation failed with code 1. OpenSSL Error messages:
error:0A000126:SSL routines::unexpected eof while reading in /root/guzzle/vendor/guzzlehttp/psr7/src/Stream.php:232
Stack trace:
#0 [internal function]: exception_error_handler()
#1 /root/guzzle/vendor/guzzlehttp/psr7/src/Stream.php(232): fread()
#2 /root/guzzle/test.php(35): GuzzleHttp\Psr7\Stream->read()
#3 {main}
thrown in /root/guzzle/vendor/guzzlehttp/psr7/src/Stream.php on line 232
root@ubuntu-22:~/guzzle# php test.php
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
string(6) "string"
PHP Warning: fread(): SSL operation failed with code 1. OpenSSL Error messages:
error:0A000126:SSL routines::unexpected eof while reading in /root/guzzle/vendor/guzzlehttp/psr7/src/Stream.php on line 232
string(6) "string"
Hmm, that's unfortunate.
might violate read()'s contract to throw a RuntimeException is cases of failure.
Throwing something that extends Error or RuntimeException is not a violation of a contact. I always take the view those those type of throwables are always fair game to be thrown at any time, just like with Java.
Throwing something that extends Error or RuntimeException is not a violation of a contact. I always take the view those those type of throwables are always fair game to be thrown at any time, just like with Java.
Don't want to nitpick here more than necessary, but ErrorException
is just extends Exception
and thus neither RuntimeException
, nor Error
, probably for historical reasons.
For RuntimeException I generally agree, but the cause of the warning in this case matches Java's IOException / EOFException more closely and that one is a checked exception in Java I believe.
Generally speaking the PHP Warning can be triggered by the remote server at will and thus it is unfixable by the user of the psr7 implementation, other than by catching \Throwable
(which generally should be avoided, as this might mask other errors). Thus I believe it is up to guzzle/psr7 to ensure that the warning does not leave the read()
method (either by @
or by a custom error handler).
diff --git i/src/Stream.php w/src/Stream.php
index d389427..5baecfa 100644
--- i/src/Stream.php
+++ w/src/Stream.php
@@ -229,7 +229,12 @@ class Stream implements StreamInterface
return '';
}
- $string = fread($this->stream, $length);
+ try {
+ $string = fread($this->stream, $length);
+ } catch (\Exception $e) {
+ throw new \RuntimeException('Unable to read from stream', 0, $e);
+ }
+
if (false === $string) {
throw new \RuntimeException('Unable to read from stream');
}
might be an acceptable fix as well. This ensures that any exception is then rewrapped as a RuntimeException
, which is required by the PSR-7 standard:
* @throws \RuntimeException if an error occurs.
Yeh, that would be a good solution.
Resolved with #505