Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mount static files for Rack

Tags:

file

ruby

rack

thin

I want to serv static files as well as dynamic contents through Rack. Previously, I used WEBrick without using Rack, with code like this, and it worked:

    @s = WEBrick::HTTPServer.new(
        Port: 3000,
        BindAddress: "localhost",
        Logger: WEBrick::Log.new(File::NULL),
        AccessLog: [nil, nil]
    )
    %w[INT TERM].each{|signal| trap(signal){@s.shutdown}}
    @s.mount("/", self)
    @s.mount("/resource/", WEBrick::HTTPServlet::FileHandler, "/")
    @s.start

In the above, requests to localhost:3000 will be responded by dynamic contents, and requests to localhost:3000/resource/path_to_file will be responded by a static file located at /path_to_file on my computer.

Now, I want to switch to a system using Rack with Thin server. I wrote the following, but could not get the static files served. How should it be changed to make it work?

    Rack::Handler::Thin.run(->env{
        h = Rack::Utils.parse_nested_query(env["QUERY_STRING"])
        # I tried the following three lines one at a time, but neither worked.
        use(Rack::Static, urls: "/resource/", root: "/") # First try
        Rack::File.new("/resource").call(env) # Second try
        Rack::Directory.new("/resource").call(env) # Third try
        [200, {}, [some_dyamically_generated_content]]
    }, Port: 3000)

I know that ther is a similar question: How to serve static files via Rack?, but I could not make it work. I am not sure how to use Rack::Static, Rack::File, or Rack::Directory. Please teach me.

like image 274
sawa Avatar asked Jan 10 '12 10:01

sawa


2 Answers

You need to use Rack::Builder in order to use the use SomeMiddleware syntax in a normal Ruby script (it's normally used in a config.ru file). You also need run for your application. Note that the urls key to Rack::Static takes an array, not a single string:

require 'rack'
Rack::Handler::Thin.run(Rack::Builder.new {
    use(Rack::Static, urls: ["/resource/"], root: "/")
    run ->env{[200, {}, [some_dyamically_generated_content]]}
}, Port: 3000)

Here Rack::Builder is taking your application, ->env{[200, {}, [some_dyamically_generated_content]]}, adding the Rack::Static middleware to it and creating a new combined application which is then passed to Thin to run.

Rack::Static is a middleware component that you can add to existing rack applications. Rack::File and Rack::Directory are both themselves rack applications, not middleware (Rack::Static uses Rack::File internally, as does Rack::Directory by default). You could achieve the same effect as above using Rack::File and the map command:

require 'rack'
Rack::Handler::Thin.run(Rack::Builder.new {
    map "/resource/" do
      run Rack::File.new "/"
    end
    map "/" do
      run ->env{[200, {}, [some_dyamically_generated_content]]}
    end
}, Port: 3000)

The more common way to do this would be to put the contents of the block passed to Rack::Bundler.new in a config.ru file:

use(Rack::Static, urls: ["/resource/"], root: "/") 
run ->env{[200, {}, [some_dyamically_generated_content]]}

You can then run this with thin start, which should find config.ru if run from the same directory, or you can use the -R option to specify the file. The rackup command can also be used, rackup -s thin if you want to specify Thin as the server.

like image 78
matt Avatar answered Nov 15 '22 05:11

matt


If you don't require authentication/authorization before serving the files you should be able to add files into /public and have them be served by the underlying server. So if your app layout looks like this

my_app/
  - app.rb
  - config.ru
  - public/
    - a_static_file.png
    - another_static_file.png

Once you start your app (using thin, webrick, or apache or whatever underlying server) you can access the static files from http://localhost:4567/a_static_file.png

like image 24
jacobsimeon Avatar answered Nov 15 '22 05:11

jacobsimeon