Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Abort Streamer-processing when user-connection aborted #42352

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

a-bran
Copy link

@a-bran a-bran commented Dec 18, 2023

Summary

Streamer, which builds compressed archives to stream to the browser (e.g. for downloading folders), didnt have checks when the browser aborts the connection (e.g. download cancelled).

This PR adds checks at certain positions:

  • before file or folder is processed in addDirRecursive to avoid opening files or further traversing directories, even though writing is impossible
  • before file is given to streamer in addFileFromStream
  • FileStream is wrapped to close file-Stream when connection aborted while reading
    • This happens after 1-3 stream-read-calls as PHP connection_status() only updates when data is attempted to be send to the browser (e.g. with flush())

Todo

  • How the abort is handled while another system is processing streams could maybe be a bit more elegant or moved somewhere else?

Signed-off-by: Alexander Brandscheidt <[email protected]>
Copy link
Contributor

@kesselb kesselb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 👍

@@ -126,6 +127,7 @@ public function addDirRecursive(string $dir, string $internalDir = ''): void {
/** @var LoggerInterface $logger */
$logger = \OC::$server->query(LoggerInterface::class);
foreach ($files as $file) {
if(connection_status() !== CONNECTION_NORMAL) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(connection_status() !== CONNECTION_NORMAL) return;
if(connection_aborted() === 1) {
break;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can even skip the === 1:

Suggested change
if(connection_status() !== CONNECTION_NORMAL) return;
if (connection_aborted()) {
break;
}

Same for the following calls to connection_status

$stream = CallbackWrapper::wrap($stream,
function ($count) use ($stream) {
if (connection_status() !== CONNECTION_NORMAL) {
fclose($stream);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure if closing the stream inside the read callback is a good idea.

I suggested using exceptions to bubble up the state properly here: #8161 (comment)

Copy link
Author

@a-bran a-bran Dec 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mhh, I thought we can handle this gracefully.
The reading system has to check fefo or if fread succeeded anyways and can then finalize.

In this case e.g. ZipStreamer doesnt catch exceptions so they just bubble to the top and then after everything finished the file is closed by PHP garbage collection.
On theother hand, graceful handling would let ZipStreamer finalize the Zip stream which is a waste of processing.

If we want an exception (so we force cleanup, if any, to execute) we should be consistent and do this in any other case aswell. What do you think?

@@ -161,6 +163,15 @@ public function addDirRecursive(string $dir, string $internalDir = ''): void {
* @return bool $success
*/
public function addFileFromStream($stream, string $internalName, int|float $size, $time): bool {
if(connection_status() !== CONNECTION_NORMAL) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(connection_status() !== CONNECTION_NORMAL) return false;
if(connection_aborted() === 1) {
return false;
}

lib/private/Streamer.php Fixed Show fixed Hide fixed
@a-bran
Copy link
Author

a-bran commented Jan 20, 2024

@kesselb Hey, some time has passed, sorry if i am bothing you by pinging.
Do you have some inputs on the comments i left in response?

This was referenced Mar 12, 2024
This was referenced Mar 20, 2024
@skjnldsv skjnldsv mentioned this pull request Mar 28, 2024
81 tasks
This was referenced Apr 4, 2024
@blizzz blizzz modified the milestones: Nextcloud 29, Nextcloud 30 Apr 8, 2024
@skjnldsv skjnldsv added 2. developing Work in progress stale Ticket or PR with no recent activity and removed 3. to review Waiting for reviews labels Jul 27, 2024
This was referenced Jul 30, 2024
This was referenced Aug 5, 2024
@skjnldsv skjnldsv mentioned this pull request Aug 13, 2024
@skjnldsv skjnldsv removed this from the Nextcloud 30 milestone Aug 14, 2024
@kesselb kesselb self-requested a review August 30, 2024 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2. developing Work in progress bug stale Ticket or PR with no recent activity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Huge download: File gets completely read even after download is cancelled
6 participants