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 83 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 89 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 109 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 119 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 181 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 197 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 96 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 130 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 171 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 163 def stat(path) ::File.stat(path) rescue Errno::ENOENT, Errno::ELOOP return nil end