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.
74 75 76 |
# File 'lib/rack/directory.rb', line 74 def root @root end |
Instance Method Details
#call(env) ⇒ Object
83 84 85 86 |
# File 'lib/rack/directory.rb', line 83 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.
103 104 105 106 107 108 109 110 |
# File 'lib/rack/directory.rb', line 103 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.
113 114 115 116 117 118 119 120 121 |
# File 'lib/rack/directory.rb', line 113 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.
175 176 177 178 179 180 |
# File 'lib/rack/directory.rb', line 175 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
191 192 193 194 195 196 197 |
# File 'lib/rack/directory.rb', line 191 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.
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rack/directory.rb', line 90 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.
124 125 126 127 128 129 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 |
# File 'lib/rack/directory.rb', line 124 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.
165 166 167 168 169 170 171 172 |
# File 'lib/rack/directory.rb', line 165 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.
157 158 159 160 161 |
# File 'lib/rack/directory.rb', line 157 def stat(path) ::File.stat(path) rescue Errno::ENOENT, Errno::ELOOP return nil end |