Class: Rack::Directory
- Inherits:
-
Object
- Object
- Rack::Directory
- Defined in:
- lib/rack/directory.rb
Overview
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.
Defined Under Namespace
Classes: DirectoryBody
Constant Summary collapse
- 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>\n"
- DIR_PAGE_HEADER =
<<-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> PAGE
- DIR_PAGE_FOOTER =
<<-PAGE </table> <hr /> </body></html> PAGE
- FILESIZE_FORMAT =
Stolen from Ramaze
[ ['%.1fT', 1 << 40], ['%.1fG', 1 << 30], ['%.1fM', 1 << 20], ['%.1fK', 1 << 10], ]
Instance Attribute Summary collapse
-
#root ⇒ Object
readonly
The root of the directory hierarchy.
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#check_bad_request(path_info) ⇒ Object
Rack response to use for requests with invalid paths, or nil if path is valid.
-
#check_forbidden(path_info) ⇒ Object
Rack response to use for requests with paths outside the root, or nil if path is inside the root.
-
#entity_not_found(path_info) ⇒ Object
Rack response to use for unreadable and non-file, non-directory entries.
-
#filesize_format(int) ⇒ Object
Provide human readable file sizes.
-
#get(env) ⇒ Object
Internals of request handling.
-
#initialize(root, app = nil) ⇒ Directory
constructor
Set the root directory and application for serving files.
-
#list_directory(path_info, path, script_name) ⇒ Object
Rack response to use for directories under the root.
-
#list_path(env, path, path_info, script_name) ⇒ Object
Rack response to use for files and directories under the root.
-
#stat(path) ⇒ Object
File::Stat for the given path, but return nil for missing/bad entries.
Constructor Details
Instance Attribute Details
#root ⇒ Object (readonly)
The root of the directory hierarchy. Only requests for files and directories inside of the root directory are supported.
80 81 82 |
# File 'lib/rack/directory.rb', line 80 def root @root end |
Instance Method Details
#call(env) ⇒ Object
89 90 91 92 |
# File 'lib/rack/directory.rb', line 89 def call(env) # strip body if this is a HEAD call @head.call env end |
#check_bad_request(path_info) ⇒ Object
Rack response to use for requests with invalid paths, or nil if path is valid.
109 110 111 112 113 114 115 116 |
# 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 |
#check_forbidden(path_info) ⇒ Object
Rack response to use for requests with paths outside the root, or nil if path is inside the root.
119 120 121 122 123 124 125 126 127 |
# File 'lib/rack/directory.rb', line 119 def check_forbidden(path_info) return unless path_info.include? ".." return if ::File.(::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 |
#entity_not_found(path_info) ⇒ Object
Rack response to use for unreadable and non-file, non-directory entries.
181 182 183 184 185 186 |
# 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 |
#filesize_format(int) ⇒ Object
Provide human readable file sizes
197 198 199 200 201 202 203 |
# 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 |
#get(env) ⇒ Object
Internals of request handling. Similar to call but does not remove body for HEAD requests.
96 97 98 99 100 101 102 103 104 105 106 |
# 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 |
#list_directory(path_info, path, script_name) ⇒ Object
Rack response to use for directories under the root.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# 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 |
#list_path(env, path, path_info, script_name) ⇒ Object
Rack response to use for files and directories under the root. Unreadable and non-file, non-directory entries will get a 404 response.
171 172 173 174 175 176 177 178 |
# 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 |
#stat(path) ⇒ Object
File::Stat for the given path, but return nil for missing/bad entries.
163 164 165 166 167 |
# File 'lib/rack/directory.rb', line 163 def stat(path) ::File.stat(path) rescue Errno::ENOENT, Errno::ELOOP return nil end |