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.
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
-
#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)
92 93 94 95 96 |
# File 'lib/mongrel/handlers.rb', line 92 def initialize(path, listing_allowed=true, index_html="index.html") @path = File.(path) @listing_allowed=listing_allowed @index_html = index_html end |
Instance Attribute Details
#path ⇒ Object (readonly)
Returns the value of attribute path.
89 90 91 |
# File 'lib/mongrel/handlers.rb', line 89 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.
229 230 231 |
# File 'lib/mongrel/handlers.rb', line 229 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).
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/mongrel/handlers.rb', line 99 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).
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/mongrel/handlers.rb', line 201 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.
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 |
# File 'lib/mongrel/handlers.rb', line 132 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.
161 162 163 164 165 166 167 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 |
# File 'lib/mongrel/handlers.rb', line 161 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 # set the mime type from our map based on the ending dot_at = req.rindex(".") if dot_at ext = req[dot_at .. -1] if MIME_TYPES[ext] stat = File.stat(req) response.header[Const::CONTENT_TYPE] = MIME_TYPES[ext] # TODO: Confirm this works for rfc 1123 response.header[Const::LAST_MODIFIED] = HttpServer.httpdate(stat.mtime) # TODO that this is a valid way to calculate an etag response.header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino] end end response.send_status(File.size(req)) 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 # ignore these since it means the client closed off early end else response.send_body # should send nothing end end |