class Rack::Directory
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::Files of the same root
will be
used.
Constants
- DIR_FILE
- DIR_PAGE_FOOTER
- DIR_PAGE_HEADER
- FILESIZE_FORMAT
Stolen from Ramaze
Attributes
The root of the directory hierarchy. Only requests for files and directories inside of the root directory are supported.
Public Class Methods
Set the root directory and application for serving files.
# File lib/rack/directory.rb, line 76 def initialize(root, app = nil) @root = ::File.expand_path(root) @app = app || Files.new(@root) @head = Head.new(method(:get)) end
Public Instance Methods
# File lib/rack/directory.rb, line 82 def call(env) # strip body if this is a HEAD call @head.call env end
Rack response to use for requests with invalid paths, or nil if path is valid.
# File lib/rack/directory.rb, line 102 def check_bad_request(path_info) return if Utils.valid_path?(path_info) body = "Bad Request\n" [400, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => body.bytesize.to_s, "X-Cascade" => "pass" }, [body]] end
Rack response to use for requests with paths outside the root, or nil if path is inside the root.
# File lib/rack/directory.rb, line 112 def check_forbidden(path_info) return unless path_info.include? ".." return if ::File.expand_path(::File.join(@root, path_info)).start_with?(@root) body = "Forbidden\n" [403, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => body.bytesize.to_s, "X-Cascade" => "pass" }, [body]] end
Rack response to use for unreadable and non-file, non-directory entries.
# File lib/rack/directory.rb, line 174 def entity_not_found(path_info) body = "Entity not found: #{path_info}\n" [404, { CONTENT_TYPE => "text/plain", CONTENT_LENGTH => body.bytesize.to_s, "X-Cascade" => "pass" }, [body]] end
Provide human readable file sizes
# File lib/rack/directory.rb, line 190 def filesize_format(int) FILESIZE_FORMAT.each do |format, size| return format % (int.to_f / size) if int >= size end "#{int}B" end
Internals of request handling. Similar to call but does not remove body for HEAD requests.
# File lib/rack/directory.rb, line 89 def get(env) script_name = env[SCRIPT_NAME] path_info = Utils.unescape_path(env[PATH_INFO]) if client_error_response = check_bad_request(path_info) || check_forbidden(path_info) client_error_response else path = ::File.join(@root, path_info) list_path(env, path, path_info, script_name) end end
Rack response to use for directories under the root.
# File lib/rack/directory.rb, line 123 def list_directory(path_info, path, script_name) url_head = (script_name.split('/') + path_info.split('/')).map do |part| Utils.escape_path part end # Globbing not safe as path could contain glob metacharacters body = DirectoryBody.new(@root, path, ->(basename) do stat = stat(::File.join(path, basename)) next unless stat url = ::File.join(*url_head + [Utils.escape_path(basename)]) mtime = stat.mtime.httpdate if stat.directory? type = 'directory' size = '-' url << '/' if basename == '..' basename = 'Parent Directory' else basename << '/' end else type = Mime.mime_type(::File.extname(basename)) size = filesize_format(stat.size) end [ url, basename, size, type, mtime ] end) [ 200, { CONTENT_TYPE => 'text/html; charset=utf-8' }, body ] end
Rack response to use for files and directories under the root. Unreadable and non-file, non-directory entries will get a 404 response.
# File lib/rack/directory.rb, line 164 def list_path(env, path, path_info, script_name) if (stat = stat(path)) && stat.readable? return @app.call(env) if stat.file? return list_directory(path_info, path, script_name) if stat.directory? end entity_not_found(path_info) end
File::Stat for the given path, but return nil for missing/bad entries.
# File lib/rack/directory.rb, line 156 def stat(path) ::File.stat(path) rescue Errno::ENOENT, Errno::ELOOP return nil end