granian icon indicating copy to clipboard operation
granian copied to clipboard

Feature request: static file serving support

Open matthiask opened this issue 1 year ago • 5 comments

From #97

Nginx serves static files

Now that can be (IMO) a more interesting feature request for Granian.

I think it would be very useful to have some sort of static file serving functionality.

Either something like nginx's try_files where granian could first check a folder for a match, and if not, fall back to the RSGI/ASGI/WSGI app, or maybe just support mapping URL paths to folders.

A plus would be if granian could optionally serve those files with far-future expiry headers. nginx does the following when using expires max:

The max parameter sets “Expires” to the value “Thu, 31 Dec 2037 23:55:55 GMT”, and “Cache-Control” to 10 years.

From https://nginx.org/en/docs/http/ngx_http_headers_module.html

This is the only feature which is missing for me to make granian a one-stop solution to serve everything with a single ingress, no sidecars or similar.

(I'd be willing to work on this with some guidance, or fund it if that's helpful.)

matthiask avatar Nov 27 '24 08:11 matthiask

I think this would be a useful feature for a lot of deployments out there, I definitely agree this should be implemented in the near future. I don't think this will be in time for 1.7, but possibly we can target 1.8.

In terms of implementation, here are some notes I can think of at the moment:

  • we would need 2 additional configuration parameters, one for the folder path to serve, one for the URL path to expose that folder to (might be smth like --static-path-mount my_app/static --static-patch-route /static)
  • this potentially affects only the build_service! macro here https://github.com/emmett-framework/granian/blob/a6cfc2f1f9d94ae7f57d6400cfee071eac328aa5/src/workers.rs#L169-L180 thus the best implementation would probably be to add a new macro and switch which macro gets used by the accept loop based on wether a static path is defined (on worker boot)
  • the main attention point should be on the security of that endpoint: the implementation should do some type of checks to avoid exploiting that path going outside of the target folder (eg: avoid /static/../../../some_file_in_root_path)
  • the expiration can probably be an additional param, I don't see any particular difficulties there

gi0baro avatar Nov 27 '24 11:11 gi0baro

IHMO it would be more useful if instead the file path could be passed dynamically from python, with something like nginx's X-Accel-Redirect header. That way the file access can be controlled with logic, and the file can be even created on the fly.

leiserfg avatar Feb 13 '25 08:02 leiserfg

@leiserfg That exists already in the form of the pathsend extension: https://asgi.readthedocs.io/en/latest/extensions.html#path-send

matthiask avatar Feb 13 '25 08:02 matthiask

But isn't it ASGI only (It's hard for me to move out of WSGI)? Also, by leveraging X-Accel-Redirect it would be possible to return not only local files, for instance it would be possible to get the credentials for an s3 private file and use X-Accel-Redirect to "proxyfy" that request.

leiserfg avatar Feb 13 '25 08:02 leiserfg

But isn't it ASGI only (It's hard for me to move out of WSGI)? Also, by leveraging X-Accel-Redirect it would be possible to return not only local files, for instance it would be possible to get the credentials for an s3 private file and use X-Accel-Redirect to "proxyfy" that request.

That was discussed in #156. Given is quite a different thing from what this issue is about (one does not exclude the other), if you like to have X-Accel support for WSGI, feel free to open a separated issue for that feature requests, providing details on how you expect it to work.

gi0baro avatar Feb 13 '25 09:02 gi0baro