Class: Mongrel::DirHandler
- Inherits:
-
HttpHandler
- Object
- HttpHandler
- Mongrel::DirHandler
- Defined in:
- lib/mongrel/handlers.rb
Overview
Serves the contents of a directory. You give it the path to the root where the files are located, and it tries to find the files based on the PATH_INFO inside the directory. If the requested path is a directory then it returns a simple directory listing.
It does a simple protection against going outside it’s root path by converting all paths to an absolute expanded path, and then making sure that the final expanded path includes the root path. If it doesn’t than it simply gives a 404.
The default content type is “text/plain; charset=ISO-8859-1” but you can change it anything you want using the DirHandler.default_content_type attribute.
Constant Summary collapse
- MIME_TYPES =
{ ".css" => "text/css", ".gif" => "image/gif", ".htm" => "text/html", ".html" => "text/html", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".js" => "text/javascript", ".png" => "image/png", ".swf" => "application/x-shockwave-flash", ".txt" => "text/plain" }
- ONLY_HEAD_GET =
"Only HEAD and GET allowed.".freeze
Instance Attribute Summary collapse
-
#default_content_type ⇒ Object
Returns the value of attribute default_content_type.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Class Method Summary collapse
-
.add_mime_type(extension, type) ⇒ Object
There is a small number of default mime types for extensions, but this lets you add any others you’ll need when serving content.
Instance Method Summary collapse
-
#can_serve(path_info) ⇒ Object
Checks if the given path can be served and returns the full path (or nil if not).
-
#initialize(path, listing_allowed = true, index_html = "index.html") ⇒ DirHandler
constructor
You give it the path to the directory root and an (optional).
-
#process(request, response) ⇒ Object
Process the request to either serve a file or a directory listing if allowed (based on the listing_allowed paramter to the constructor).
-
#send_dir_listing(base, dir, response) ⇒ Object
Returns a simplistic directory listing if they’re enabled, otherwise a 403.
-
#send_file(req, response, header_only = false) ⇒ Object
Sends the contents of a file back to the user.
Constructor Details
#initialize(path, listing_allowed = true, index_html = "index.html") ⇒ DirHandler
You give it the path to the directory root and an (optional)
98 99 100 101 102 103 |
# File 'lib/mongrel/handlers.rb', line 98 def initialize(path, listing_allowed=true, index_html="index.html") @path = File.(path) @listing_allowed=listing_allowed @index_html = index_html @default_content_type = "text/plain; charset=ISO-8859-1".freeze end |
Instance Attribute Details
#default_content_type ⇒ Object
Returns the value of attribute default_content_type.
78 79 80 |
# File 'lib/mongrel/handlers.rb', line 78 def default_content_type @default_content_type end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
80 81 82 |
# File 'lib/mongrel/handlers.rb', line 80 def path @path end |
Class Method Details
.add_mime_type(extension, type) ⇒ Object
There is a small number of default mime types for extensions, but this lets you add any others you’ll need when serving content.
237 238 239 |
# File 'lib/mongrel/handlers.rb', line 237 def DirHandler::add_mime_type(extension, type) MIME_TYPES[extension] = type end |
Instance Method Details
#can_serve(path_info) ⇒ Object
Checks if the given path can be served and returns the full path (or nil if not).
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/mongrel/handlers.rb', line 106 def can_serve(path_info) req = File.(File.join(@path,path_info), @path) if req.index(@path) == 0 and File.exist? req # it exists and it's in the right location if File.directory? req # the request is for a directory index = File.join(req, @index_html) if File.exist? index # serve the index return index elsif @listing_allowed # serve the directory req else # do not serve anything return nil end else # it's a file and it's there return req end else # does not exist or isn't in the right spot return nil end end |
#process(request, response) ⇒ Object
Process the request to either serve a file or a directory listing if allowed (based on the listing_allowed paramter to the constructor).
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/mongrel/handlers.rb', line 209 def process(request, response) req_method = request.params[Const::REQUEST_METHOD] || Const::GET req = can_serve request.params[Const::PATH_INFO] if not req # not found, return a 404 response.start(404) do |head,out| out << "File not found" end else begin if File.directory? req send_dir_listing(request.params[Const::REQUEST_URI],req, response) elsif req_method == Const::HEAD send_file(req, response, true) elsif req_method == Const::GET send_file(req, response, false) else response.start(403) {|head,out| out.write(ONLY_HEAD_GET) } end rescue => details STDERR.puts "Error accessing file #{req}: #{details}" STDERR.puts details.backtrace.join("\n") end end end |
#send_dir_listing(base, dir, response) ⇒ Object
Returns a simplistic directory listing if they’re enabled, otherwise a 403. Base is the base URI from the REQUEST_URI, dir is the directory to serve on the file system (comes from can_serve()), and response is the HttpResponse object to send the results on.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/mongrel/handlers.rb', line 139 def send_dir_listing(base, dir, response) # take off any trailing / so the links come out right base.chop! if base[-1] == "/"[-1] if @listing_allowed response.start(200) do |head,out| head[Const::CONTENT_TYPE] = "text/html" out << "<html><head><title>Directory Listing</title></head><body>" Dir.entries(dir).each do |child| next if child == "." if child == ".." out << "<a href=\"#{base}/#{child}\">Up to parent..</a><br/>" else out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>" end end out << "</body></html>" end else response.start(403) do |head,out| out.write("Directory listings not allowed") end end end |
#send_file(req, response, header_only = false) ⇒ Object
Sends the contents of a file back to the user. Not terribly efficient since it’s opening and closing the file for each read.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/mongrel/handlers.rb', line 168 def send_file(req, response, header_only=false) # first we setup the headers and status then we do a very fast send on the socket directly response.status = 200 stat = File.stat(req) header = response.header # Set the last modified times as well and etag for all files header[Const::LAST_MODIFIED] = stat.mtime.httpdate # Calculated the same as apache, not sure how well the works on win32 header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino] # set the mime type from our map based on the ending dot_at = req.rindex(".") if dot_at header[Const::CONTENT_TYPE] = MIME_TYPES[req[dot_at .. -1]] || @default_content_type end # send a status with out content length response.send_status(stat.size) response.send_header if not header_only begin if $mongrel_has_sendfile File.open(req, "rb") { |f| response.socket.sendfile(f) } else File.open(req, "rb") { |f| response.socket.write(f.read) } end rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF # ignore these since it means the client closed off early STDERR.puts "Client closed socket requesting file #{req}: #$!" end else response.send_body # should send nothing end end |