Stream from the venti server instead of reading into memory.
[webvac] / config.ru
1 %w(
2         watts
3         webvac
4 ).each &method(:require)
5
6 Encoding.default_external = Encoding::BINARY
7
8 module WebVac
9         class R < Watts::Resource
10                 # This is a stupid feature.
11                 # Fortunes lifted shamelessly from http://quotes.cat-v.org/programming/
12                 Fortunes = <<-EOFORTUNES.gsub(/\t+/, '').split(/\n+/)
13                         There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.
14                         When in doubt, use brute force.
15                         Deleted code is debugged code.
16                         Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
17                         The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.
18                         Controlling complexity is the essence of computer programming.
19                         UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things.
20                         Beware of those who say that their software does not suck, for they are either fools or liars.
21                         Unix is simple. It just takes a genius to understand its simplicity.
22                         If you want to go somewhere, goto is the best way to get there.
23                         I object to doing things that computers can do.
24                         A good way to have good ideas is by being unoriginal.
25                         a program is like a poem: you cannot write a poem without writing it.
26                 EOFORTUNES
27
28                 def headers t, path, score, contents
29                         name = (request.params['name'] || File.basename(path)).
30                                 gsub('"', "'")
31                         {
32                                 'Content-Disposition' => "filename=\"#{name}\"",
33                                 'Fortune' => Fortunes.sample,
34                         }.merge!(t.metadata(score) || {}).tap { |h|
35                                 # Now that we stream, this condition never happens.
36                                 # Maybe it's a good idea to wrap the IO object and fill in
37                                 # the extra data as needed.
38                                 if contents
39                                         h['Content-Type'] ||= t.guess_mime(contents) rescue nil
40                                         h['Content-Length'] ||= contents.bytesize.to_s
41                                 end
42                         }
43                 end
44         end
45
46         class Root < R
47                 auto_head
48                 get {
49                         "++++++++++++[>+++++++++<-]>.+++.---."
50                 }
51         end
52
53         class Serv < R
54                 # We can cheat a little with closures because there's just one "real"
55                 # endpoint.  No need to memoize.
56
57                 config = Config.load
58                 vac = Vac.new config
59                 tab = Table.new config
60
61                 head { |*|
62                         p = config.path_fixup(CGI.unescape(env['REQUEST_PATH']))
63                         s = tab.path2score p
64                         return [404, {}, ["404 Not found\nNo such path: #{p}\n"]] unless s
65                         [200, headers(tab, p, s, nil), []]
66                 }
67                 get { |*|
68                         p = config.path_fixup(CGI.unescape(env['REQUEST_PATH']))
69                         s = tab.path2score p
70                         return [404, {}, ["404 Not found\nNo such path: #{p}\n"]] unless s
71                         ct = Time.parse(env['HTTP_IF_MODIFIED_SINCE']) rescue nil
72                         hs = headers(tab, p, s, nil)
73                         if ct && ct.to_i > 0
74                                 mt = Time.parse(hs['Last-Modified']) rescue nil
75                                 return [304, hs, []] if mt && mt > ct
76                         end
77                         [200, hs, vac.load_io(s)]
78                 }
79         end
80
81         class App < Watts::App
82                 resource('/', Root) {
83                         resource(:filename, Serv)
84                         resource(['media', :l1], Serv) { resource(:l2, Serv) }
85                 }
86         end
87 end
88
89 run WebVac::App.new