Middleware for serving static files

157 views
Skip to first unread message

Magnus Holm

unread,
Dec 20, 2011, 3:32:29 AM12/20/11
to rack-devel
Problem:

I want to serve public/style.css as /style.css. How can I do this with
a middleware?

Rack::Static is out because it requires a prefix.

Rack::Cascade + Rack::File does the job, but Rack::Cascade doesn't
work as a middleware.

Any plans for either allowing Rack::Static to not use a prefix or make
Rack::Cascade work as a middleware too?
--

The reason I need it as a middleware is because Camping allows you to
inject middlewares in your application:

module MyApp
use Rack::Static
end

This is pretty nifty because you can keep everything on one file
(controllers, views, middlewares), but really breaks down because
there's no way to serve static files through a middleware.

Right now I'm using:

def (Before="").new(a,*o)Rack::Cascade.new(o<<a)end

module MyApp
use Before, Rack::File.new('public')
end

But it would certainly be better if Rack supported this by default…

Lee Hambley

unread,
Dec 20, 2011, 3:41:13 AM12/20/11
to rack-...@googlegroups.com
Make your own middleware that detects a URI if `/style.css`, and respond with `/files/style.css` using Rack::Static?

Magnus Holm

unread,
Dec 20, 2011, 3:45:53 AM12/20/11
to rack-...@googlegroups.com
On Tue, Dec 20, 2011 at 09:41, Lee Hambley <lee.h...@gmail.com> wrote:
> Make your own middleware that detects a URI if `/style.css`, and respond
> with `/files/style.css` using Rack::Static?

Meh, the Before-solution is better then. It isn't hard to solve the
problem, I'm just wondering why Rack doesn't give us a way to do it
out-of-box. It seems like a pretty basic functionality…

Lee Hambley

unread,
Dec 20, 2011, 3:52:22 AM12/20/11
to rack-...@googlegroups.com
Magnus, Rack does, it's Rack::Static… I can't think of a good reason not to have a directory like `/s/` to store those assets. Otherwise how should Rack know which URLs to route to where? The only information it has is from the URL that is requested, and some environmental headers.

You'd be well served to consider how you'll solve this problem in production, letting your ruby stack serve static files is going to be a performance disaster, that's an unspoken rule we all know about, so even when you get into production, you're going to need a way to configure Apache/Passenger/nginx/unicorn/rainbows to serve these assets, and again the way will probably be based on a URL filter which will serve assets directly, and not proxy them to your application's backend.

Final note, I think we're taking the list off-topic, the list is intended for Rack developers to discuss and announce changes; I've never seen a support thread here, and would recommend stack overflow.

Magnus Holm

unread,
Dec 20, 2011, 4:07:44 AM12/20/11
to rack-...@googlegroups.com
On Tue, Dec 20, 2011 at 09:52, Lee Hambley <lee.h...@gmail.com> wrote:
> Magnus, Rack does, it's Rack::Static… I can't think of a good reason not to
> have a directory like `/s/` to store those assets. Otherwise how should Rack
> know which URLs to route to where? The only information it has is from the
> URL that is requested, and some environmental headers.

Rack::Cascade + Rack::File solves this just fine.

> You'd be well served to consider how you'll solve this problem in
> production, letting your ruby stack serve static files is going to be a
> performance disaster, that's an unspoken rule we all know about, so even
> when you get into production, you're going to need a way to configure
> Apache/Passenger/nginx/unicorn/rainbows to serve these assets,

Yes, in production I'm using Nginx to serve the static, but what about
development?

> and again the
> way will probably be based on a URL filter which will serve assets directly,
> and not proxy them to your application's backend.

Ehm, no? Both Nginx and Apache will serve static files if they exist
in the document root, and proxy to the application backend otherwise.
I just want to mirror this setup in development.

> Final note, I think we're taking the list off-topic, the list is intended
> for Rack developers to discuss and announce changes; I've never seen a
> support thread here, and would recommend stack overflow.

Again: This isn't about solving my problem, it's about making Rack
solve this problem.

James Tucker

unread,
Dec 20, 2011, 9:49:14 PM12/20/11
to rack-...@googlegroups.com
Magnus,

Does this do what you want?

def call(env)
[ 200, {'Content-Type' => 'text/plain'}, [env['HTTP_HOST']] ]
end

use Rack::Static, :urls => Dir['*'].map { |f| "/#{f}" }
run self

This does cache the allowed list on boot.

If not, a patch to support what you want is reasonably trivial, however, one has to be careful to not open up the system to discovery attacks, file traversals, and timing issues. It's not quite as simple as tacking an || File.exists?(path) onto the end of the can_serve method.

Cheers,

James

Reply all
Reply to author
Forward
0 new messages