2

Since the ASP.NET Development server (VS2012) doesn't let us access URL over LAN (!!) and I don't have rights to configure IIS, I am trying to use WEBrick to launch a static website, to get LAN access. I am running Ubuntu with vagrant on Windows.

In the root of my static website, I have created a file app.rb with the following content:

#app.rb

require 'rubygems'
require 'rack'

class App
  def call(env)
    return [200, {"Content-Type" => "text/html"}, "index.html"]
  end
end

Rack::Handler::WEBrick.run(App.new, :Port => 8080)

When I run the server; ruby app.rb, and browse to http://localhost:8080, it gives me this error:

ERROR NoMethodError: undefined method `each' for "index.html":String
    /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rack-1.4.5/lib/rack/handler/webrick.rb:71:in `service'
    /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/webrick/httpserver.rb:138:in `service'
    /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/webrick/httpserver.rb:94:in `run'
    /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/webrick/server.rb:191:in `block in start_thread'

Is there a way to use WEBrick or Thin to run static (HTML / JS) websites?

Edit

I followed Stuart's suggestion and changed the code to:

return [200, {"Content-Type" => "text/html"}, ["index.html"]]

Now, when I am browsing to URL, it renders "index.html" as string instead rendering the file content.

Community
  • 1
  • 1
Annie
  • 3,090
  • 9
  • 36
  • 74

2 Answers2

2

There is a simple method of serving a folder over HTTP with WEBrick.

And as for Rack, it by itself doesn't deal with serving files. You have to read the file you want to serve over HTTP and give Rack the contents of the file and for that you could try this quick-and-dirty solution:

def call(env)
  contents = File.open("index.html") { |f| f.read }
  return [200, {"Content-Type" => "text/html"}, [contents]]
end
FilipK
  • 626
  • 1
  • 4
  • 13
  • For the same reason @Annie originally hit, which I explained in my answer, you would need to wrap `contents` in an Array (e.g., `[contents]`). When I tried your answer verbatim, I got: `NoMethodError: undefined method \`each' for #` – Stuart M May 07 '13 at 17:48
  • Thanks, I copy pasted the code block in app.rb and ran the server it worked! For anyone following the same path, if you need to set the fixed port change this line ` port = 12000 + (dir.hash % 1000)` to ` port = your_port_number`, else it would launch the server on 12XXX port (as you can see in the code). – Annie May 08 '13 at 06:39
1

Your response body must be wrapped in an Array:

    return [200, {"Content-Type" => "text/html"}, ["Hello, World!"]]

Note the [ and ] surrounding "Hello, World!". For an explanation of why the Array wrapper is needed, see:

Why is rack response body an array not a string?

And in particular, this section of the Python WSGI specification:

For large files, however, or for specialized uses of HTTP streaming (such as multipart "server push"), an application may need to provide output in smaller blocks (e.g. to avoid loading a large file into memory). It's also sometimes the case that part of a response may be time-consuming to produce, but it would be useful to send ahead the portion of the response that precedes it.

In these cases, applications will usually return an iterator (often a generator-iterator) that produces the output in a block-by-block fashion. These blocks may be broken to coincide with mulitpart boundaries (for "server push"), or just before time-consuming tasks (such as reading another block of an on-disk file).

That spec refers to Python, but the analogue in a Ruby Rack application is wrapping the HTTP response body in an Array (or any Enumerable which responds to the each method).

EDIT: To address the original question's edit, about how to specifically serve an index.html file, you can do that by reading the file and outputting its contents in the Rack response:

    return [200, {"Content-Type" => "text/html"}, [File.read("index.html")]]

EDIT 2: While the above will work for showing index.html only, if you want to serve an entire directory of static resources (JS, CSS, etc.) you can try using the Rack::Static middleware. See its documentation for details, but I think what you're trying to do could be accomplished with this:

require 'rack/static'
use Rack::Static, :urls => {"/" => 'index.html'}
Community
  • 1
  • 1
Stuart M
  • 11,458
  • 6
  • 45
  • 59
  • Thanks for the suggestion. Check my edited question. How can I render the contents of HTML file? – Annie May 07 '13 at 10:16
  • I've edited my answer above to specifically address serving that file – Stuart M May 07 '13 at 17:43
  • Thanks a lot, it worked! The content of index.html are displayed. But the images, JS and CSS files are loaded with the *same content* as of index.HTML! Any idea, how to deliver the requested resources? – Annie May 08 '13 at 06:22
  • @Annie - that's because this approach explicitly reads contents of a single file (`File.read("index.html")`). If you want to use WEBrick to serve a collection of files from a folder, see the link in my answer. – FilipK May 08 '13 at 06:31
  • @Annie Or you could use the built-in [Rack::Static](http://rubydoc.info/gems/rack/Rack/Static) middleware, see my **Edit 2** – Stuart M May 08 '13 at 06:34
  • @StuartM, the code in Edit2 gave me this error `app.rb:4:in '
    ': undefined method 'use' for main:Object (NoMethodError)`
    – Annie May 08 '13 at 06:47
  • Oh, I forgot you're not in a Rackup file. It is likely simpler to just use Webrick directly as @FilipK suggests. – Stuart M May 08 '13 at 06:51