| Class | Rack::Directory |
| In: |
lib/rack/directory.rb
|
| Parent: | Object |
Rack::Directory serves entries below the root given, according to the path info of the Rack request. If a directory is found, the file‘s contents will be presented in an html based index. If a file is found, the env will be passed to the specified app.
If app is not specified, a Rack::File of the same root will be used.
| DIR_FILE | = | "<tr><td class='name'><a href='%s'>%s</a></td><td class='size'>%s</td><td class='type'>%s</td><td class='mtime'>%s</td></tr>" | ||
| DIR_PAGE | = | <<-PAGE <html><head> <title>%s</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <style type='text/css'> table { width:100%%; } .name { text-align:left; } .size, .mtime { text-align:right; } .type { width:11em; } .mtime { width:15em; } </style> </head><body> <h1>%s</h1> <hr /> <table> <tr> <th class='name'>Name</th> <th class='size'>Size</th> <th class='type'>Type</th> <th class='mtime'>Last Modified</th> </tr> %s </table> <hr /> </body></html> PAGE | ||
| F | = | ::File | ||
| FILESIZE_FORMAT | = | [ ['%.1fT', 1 << 40], ['%.1fG', 1 << 30], ['%.1fM', 1 << 20], ['%.1fK', 1 << 10], ] | Stolen from Ramaze |
| files | [R] | |
| path | [RW] | |
| root | [RW] |
# File lib/rack/directory.rb, line 45
45: def initialize(root, app=nil)
46: @root = F.expand_path(root)
47: @app = app || Rack::File.new(@root)
48: end
# File lib/rack/directory.rb, line 56
56: def _call(env)
57: @env = env
58: @path_info, @script_name = env.values_at('PATH_INFO', 'SCRIPT_NAME')
59:
60: if forbidden = check_forbidden
61: forbidden
62: else
63: @path = F.join(@root, Utils.unescape(@path_info))
64: list_path
65: end
66: end
# File lib/rack/directory.rb, line 68
68: def check_forbidden
69: return unless @path_info.include? ".."
70:
71: body = "Forbidden\n"
72: size = body.respond_to?(:bytesize) ? body.bytesize : body.size
73: return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
74: end
# File lib/rack/directory.rb, line 126
126: def each
127: show_path = @path.sub(/^#{@root}/,'')
128: files = @files.map{|f| DIR_FILE % f }*"\n"
129: page = DIR_PAGE % [ show_path, show_path , files ]
130: page.each_line{|l| yield l }
131: end
# File lib/rack/directory.rb, line 120
120: def entity_not_found
121: body = "Entity not found: #{@path_info}\n"
122: size = body.respond_to?(:bytesize) ? body.bytesize : body.size
123: return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
124: end
# File lib/rack/directory.rb, line 142
142: def filesize_format(int)
143: FILESIZE_FORMAT.each do |format, size|
144: return format % (int.to_f / size) if int >= size
145: end
146:
147: int.to_s + 'B'
148: end
# File lib/rack/directory.rb, line 76
76: def list_directory
77: @files = [['../','Parent Directory','','','']]
78: glob = F.join(@path, '*')
79:
80: Dir[glob].sort.each do |node|
81: stat = stat(node)
82: next unless stat
83: basename = F.basename(node)
84: ext = F.extname(node)
85:
86: url = F.join(@script_name, @path_info, basename)
87: size = stat.size
88: type = stat.directory? ? 'directory' : Mime.mime_type(ext)
89: size = stat.directory? ? '-' : filesize_format(size)
90: mtime = stat.mtime.httpdate
91:
92: @files << [ url, basename, size, type, mtime ]
93: end
94:
95: return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
96: end
TODO: add correct response if not readable, not sure if 404 is the best
option
# File lib/rack/directory.rb, line 106
106: def list_path
107: @stat = F.stat(@path)
108:
109: if @stat.readable?
110: return @app.call(@env) if @stat.file?
111: return list_directory if @stat.directory?
112: else
113: raise Errno::ENOENT, 'No such file or directory'
114: end
115:
116: rescue Errno::ENOENT, Errno::ELOOP
117: return entity_not_found
118: end