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

AppFramework StreamResponse #13616

Merged
merged 1 commit into from Feb 27, 2015
Merged

AppFramework StreamResponse #13616

merged 1 commit into from Feb 27, 2015

Conversation

BernhardPosselt
Copy link
Contributor

First stab at the StreamResponse, see #12988

The idea is to use an interface ICallbackResponse (I'm not 100% happy with the name yet, suggestions?) that allow the response to output things in its own way, for instance stream the file using readfile

Unittests are atm lacking, plan is to

  • check if a mock of ICallbackResponse will be used by calling its callback (also unhappy with this name) method

Usage is:

$response = new StreamResponse('path/to/file');

@DeepDiver1975 @MorrisJobke @LukasReschke @PVince81 @oparoz

@BernhardPosselt BernhardPosselt added this to the 8.1-next milestone Jan 22, 2015
@DeepDiver1975
Copy link
Member

nice - THX @Raydiation

* Streams the file using readfile
*/
public function callback () {
@readfile($this->filepath);
Copy link
Member

Choose a reason for hiding this comment

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

I'm somewhat wondering whether it would make sense to throw an exception in case an error happened (i.e. readfile returned false)

Copy link
Member

Choose a reason for hiding this comment

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

We might also want to evaluate XSendFile support for this one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question, I actually don't know because there is nothing that catches the exception. ATM it will just silently fail if the content is not available which is not a good idea i think. Just throw any exception?

Ideally we'd have a 404 if the file does not exist, the question is just where and how we should throw the exception. Maybe it would be best to just leave this up to the developer, e.g.:

public function controllerMethod($fileId) {
    try {
        $filePath = $this->service->findPath($fileId);
        return new StreamResponse($filePath);
    } catch (DoesNotExistException $e) {
        return new Response(Http::STATUS_NOT_FOUND);
    }
}

otherwise we'd need custom middleware that checks the filepaths of all StreamResponses and replaces it with 404 responses which is akward I think.

As for xsendfile: a good idea but how do we handle php streams? How would we return a file that is on an FTP? It is fast but probably has a different use case because of the imposed limits and should go into a separate response that falls back to readfile if xsendfile does not exist

Copy link
Member

Choose a reason for hiding this comment

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

+1 for the exception which has to be catched by the single dev.

It is fast but probably has a different use case because of the imposed limits and should go into a separate response that falls back to readfile if xsendfile does not exist

Agreed as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Yep should be upper-case p

@oparoz oparoz mentioned this pull request Jan 23, 2015
11 tasks
@BernhardPosselt
Copy link
Contributor Author

Tests are added by using a very simple wrapper around print/header/setcookie

@BernhardPosselt
Copy link
Contributor Author

The only thing im still unhappy about is the naming

}


}
Copy link
Member

Choose a reason for hiding this comment

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

Newline at EoF

@LukasReschke
Copy link
Member

I fixed the code style issues as well as the issue with the wrong variable and squashed all commits.

@BernhardPosselt
Copy link
Contributor Author

@LukasReschke thanks!

@LukasReschke
Copy link
Member

Makes sense - works 👍

@MorrisJobke @nickvergessen Second review?

@LukasReschke
Copy link
Member

By the way, I'm wondering if it would make sense to forbid directory traversals (e.g. ../) and throw an exception instead?

What do you think, @Raydiation?

@BernhardPosselt
Copy link
Contributor Author

It makes sense to require a realpath (as in no ../../ in it) but then people will probably just work around that using realpath().

It may be easier to restrict it to certain paths. Maybe data and apps/appid/data? We can always change that by passing additional constructor parameters to change the behavior, as in whitelist more directories

@LukasReschke
Copy link
Member

It may be easier to restrict it to certain paths. Maybe data and apps/appid/data? We can always change that by passing additional constructor parameters to change the behavior, as in whitelist more directories

That would probably be a good idea. I'd say it's enough to go for data/ as the other thing can anyways served by the Webserver without any PHP processing overhead.

/**
* Very thin wrapper class to make output testable
*/
class IO {
Copy link
Contributor

Choose a reason for hiding this comment

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

OC IO, kappa 🙊
I'd prefer meaningful names, although IO might be known, InputOutput doesn't hurt either?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IO is very common, no need to fully write it, InputOutput on the other hand sounds weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems the class is only doing output, so I'm going to rename it to Output

@nickvergessen
Copy link
Contributor

Didn't test, but looks good to me

@oparoz
Copy link
Contributor

oparoz commented Feb 24, 2015

Does this work with all supported resources? Encrypted files, S3, SMB, NFS, external cloud, etc. ?
It seems to me that this class will always need to be sub-classed to define various headers (content-type, attachment, mimetype, etc.). Am I correct?

As a concrete example, I would use the exact same class, but extend StreamResponse, modify the ctor and pass in a path instead of a string.
https://github.com/interfasys/galleryplus/blob/master/http/imageresponse.php

@BernhardPosselt
Copy link
Contributor Author

@oparoz if it supports streams, yes it will work. You can simply inherit from StreamResponse and set the headers in the constructor and pass the filepath to the parent constructor. Depending on your needs you may want to use mod xsendfile though since serving the file with PHP is painfully slow.

You can do that by implementing ICallbackResponse and setting the appropriate headers and send a PR if you are interested.

@oparoz
Copy link
Contributor

oparoz commented Feb 24, 2015

@Raydiation - Thanks. I'll give it a try once this is merged then. I probably won't use mod xsendfile though as pictures are not that big.

if it supports streams, yes it will work

Will it fail gracefully if the resource is of the wrong type? To me the AppFramework is an abstraction layer. I don't want to have to think about where the resource is located. I ask for a file, I'm given a path, I send it to the browser.
Am I wrong? This can be left to discuss elsewhere btw.

@BernhardPosselt
Copy link
Contributor Author

Fail gracefully as in wont output anything ;)

@oparoz
Copy link
Contributor

oparoz commented Feb 24, 2015

Hmmm ;)
How about an error code in case of a failure or a timeout? I'm guessing it's the responsibility of the implemented call back?
I need to find time to play with this with a concrete example...

@BernhardPosselt
Copy link
Contributor Author

Ok, updated the PR:

  • IO is now called Output
  • The callback receives an instance of IOutput so the response becomes testable
  • StreamResponse does not stream the file if the status is not modified
  • StreamResponse handles the case that the file does not exist (sends a 404) or if the streaming fails (400). I've also tried to get 403 in but it seems there is no way to tell if a file is readable because is_readable($path) does not work on streams

}

/**
* @param string $path
Copy link
Contributor

Choose a reason for hiding this comment

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

Intendation is a bit shifted

// handle caching
if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) {
if (!file_exists($this->filePath)) {
$output->setHttpResponseCode(HTTP::STATUS_NOT_FOUND);
Copy link
Contributor

Choose a reason for hiding this comment

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

UPPERCASE vs Capitalcase?

@MorrisJobke
Copy link
Contributor

Looks good beside my comments

@@ -24,6 +24,10 @@

namespace OC\AppFramework;

use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\IO;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really needed? And then it's misspelled;)

@ghost
Copy link

ghost commented Feb 27, 2015

Refer to this link for build results (access rights to CI server needed):
https://ci.owncloud.org//job/pull-request-analyser-ng-simple/10051/
Test PASSed.

@LukasReschke
Copy link
Member

Squash? 🙈 – 6 commits that say "Fix indention" are not really helpful 🙈

@BernhardPosselt
Copy link
Contributor Author

Will squash once there are two thumbs up ;)

@MorrisJobke
Copy link
Contributor

Works 👍

First stab at the StreamResponse, see #12988

The idea is to use an interface ICallbackResponse (I'm not 100% happy with the name yet, suggestions?) that allow the response to output things in its own way, for instance stream the file using readfile

Unittests are atm lacking, plan is to

check if a mock of ICallbackResponse will be used by calling its callback (also unhappy with this name) method
Usage is:

$response = new StreamResponse('path/to/file');

rename io to output, add additional methods and handle error and not modified cases when using StreamResponse

fix indention and uppercasing, also handle forbidden cases

fix indention

fix indention

no forbidden, figuring out if a file is really readable is too complicated to get to work across OSes and streams

remove useless import

remove useless import

fix intendation
BernhardPosselt pushed a commit that referenced this pull request Feb 27, 2015
@BernhardPosselt BernhardPosselt merged commit 970b14d into master Feb 27, 2015
@BernhardPosselt BernhardPosselt deleted the streamresponse branch February 27, 2015 14:43
@scrutinizer-notifier
Copy link

The inspection completed: 1 new issues, 18 updated code elements

1 similar comment
@scrutinizer-notifier
Copy link

The inspection completed: 1 new issues, 18 updated code elements

@lock lock bot locked as resolved and limited conversation to collaborators Aug 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants