Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 33 additions & 17 deletions src/ReadableResourceStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,8 @@ public function read(?Cancellation $cancellation = null, ?int $limit = null): ?s

\assert($this->resource !== null);

\set_error_handler(self::$errorHandler);

try {
// Attempt a direct read because PHP may buffer data, e.g. in TLS buffers.
if ($this->useSingleRead) {
$data = \fread($this->resource, $limit);
} else {
$data = \stream_get_contents($this->resource, $limit);
}
} finally {
\restore_error_handler();
}

\assert(
$data !== false,
"Trying to read from a previously fclose()'d resource. Do NOT manually fclose() resources the loop still has a reference to."
);
// Attempt a direct read because PHP may buffer data, e.g. in TLS buffers.
$data = $this->readDirectly($limit);

if ($data === '') {
if (\feof($this->resource)) {
Expand All @@ -208,6 +193,13 @@ public function read(?Cancellation $cancellation = null, ?int $limit = null): ?s
return null;
}

// On a TLS stream feof() calls SSL_peek(), which moves a record that has just arrived from the socket
// into the OpenSSL buffer. The socket is then no longer readable, so the watcher would never be invoked
// by drivers polling the descriptor (all except StreamSelectDriver, as stream_select() checks SSL_pending()).
$data = $this->readDirectly($limit);
}

if ($data === '') {
$this->chunkSize = $limit;
EventLoop::enable($this->callbackId);
$this->suspension = EventLoop::getSuspension();
Expand Down Expand Up @@ -334,6 +326,30 @@ public function __destruct()
}
}

private function readDirectly(int $limit): string
{
\assert($this->resource !== null);

\set_error_handler(self::$errorHandler);

try {
if ($this->useSingleRead) {
$data = \fread($this->resource, $limit);
} else {
$data = \stream_get_contents($this->resource, $limit);
}
} finally {
\restore_error_handler();
}

\assert(
$data !== false,
"Trying to read from a previously fclose()'d resource. Do NOT manually fclose() resources the loop still has a reference to."
);

return $data;
}

/**
* Nulls reference to resource, marks stream unreadable, and succeeds any pending read with null.
*/
Expand Down
Loading