Serves files from a directory
- A
- C
- D
- E
- G
- N
- P
- R
- S
- T
- W
HandlerTable | = | Hash.new |
Allow custom handling of requests for files with suffix
by
class handler
Creates a FileHandler servlet on
server
that serves files starting at directory
root
If options
is a Hash the
following keys are allowed:
- :AcceptableLanguages
-
Array of languages allowed for accept-language
- :DirectoryCallback
-
Allows preprocessing of directory requests
- :FancyIndexing
-
If true, show an index for directories
- :FileCallback
-
Allows preprocessing of file requests
- :HandlerCallback
-
Allows preprocessing of requests
- :HandlerTable
-
Maps file suffixes to file handlers. DefaultFileHandler is used by default but any servlet can be used.
- :NondisclosureName
-
Do not show files matching this array of globs
- :UserDir
-
Directory inside ~user to serve content from for /~user requests. Only works if mounted on /
If options
is true or false then :FancyIndexing
is enabled or disabled respectively.
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 170 def initialize(server, root, options={}, default=Config::FileHandler) @config = server.config @logger = @config[:Logger] @root = File.expand_path(root) if options == true || options == false options = { :FancyIndexing => options } end @options = default.dup.update(options) end
Remove custom handling of requests for files with suffix
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 180 def service(req, res) # if this class is mounted on "/" and /~username is requested. # we're going to override path informations before invoking service. if defined?(Etc) && @options[:UserDir] && req.script_name.empty? if %r^(/~([^/]+))| =~ req.path_info script_name, user = $1, $2 path_info = $' begin passwd = Etc::getpwnam(user) @root = File::join(passwd.dir, @options[:UserDir]) req.script_name = script_name req.path_info = path_info rescue @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed" end end end prevent_directory_traversal(req, res) super(req, res) end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 323 def check_filename(req, res, name) if nondisclosure_name?(name) || windows_ambiguous_name?(name) @logger.warn("the request refers nondisclosure name `#{name}'.") raise HTTPStatus::NotFound, "`#{req.path}' not found." end end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 265 def exec_handler(req, res) raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root if set_filename(req, res) handler = get_handler(req, res) call_callback(:HandlerCallback, req, res) h = handler.get_instance(@config, res.filename) h.service(req, res) return true end call_callback(:HandlerCallback, req, res) return false end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 278 def get_handler(req, res) suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename if @options[:AcceptableLanguages].include?($2.downcase) suffix2 = $1.downcase end end handler_table = @options[:HandlerTable] return handler_table[suffix1] || handler_table[suffix2] || HandlerTable[suffix1] || HandlerTable[suffix2] || DefaultFileHandler end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 242 def prevent_directory_traversal(req, res) # Preventing directory traversal on Windows platforms; # Backslashes (0x5c) in path_info are not interpreted as special # character in URI notation. So the value of path_info should be # normalize before accessing to the filesystem. # dirty hack for filesystem encoding; in nature, File.expand_path # should not be used for path normalization. [Bug #3345] path = req.path_info.dup.force_encoding(Encoding.find("filesystem")) if trailing_pathsep?(req.path_info) # File.expand_path removes the trailing path separator. # Adding a character is a workaround to save it. # File.expand_path("/aaa/") #=> "/aaa" # File.expand_path("/aaa/" + "x") #=> "/aaa/x" expanded = File.expand_path(path + "x") expanded.chop! # remove trailing "x" else expanded = File.expand_path(path) end expanded.force_encoding(req.path_info.encoding) req.path_info = expanded end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 348 def search_file(req, res, basename) langs = @options[:AcceptableLanguages] path = res.filename + basename if File.file?(path) return basename elsif langs.size > 0 req.accept_language.each{|lang| path_with_lang = path + ".#{lang}" if langs.member?(lang) && File.file?(path_with_lang) return basename + ".#{lang}" end } (langs - req.accept_language).each{|lang| path_with_lang = path + ".#{lang}" if File.file?(path_with_lang) return basename + ".#{lang}" end } end return nil end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 391 def set_dir_list(req, res) redirect_to_directory_uri(req, res) unless @options[:FancyIndexing] raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'" end local_path = res.filename list = Dir::entries(local_path).collect{|name| next if name == "." || name == ".." next if nondisclosure_name?(name) next if windows_ambiguous_name?(name) st = (File::stat(File.join(local_path, name)) rescue nil) if st.nil? [ name, nil, -1 ] elsif st.directory? [ name + "/", st.mtime, -1 ] else [ name, st.mtime, st.size ] end } list.compact! if d0 = req.query["N"]; idx = 0 elsif d0 = req.query["M"]; idx = 1 elsif d0 = req.query["S"]; idx = 2 else d0 = "A" ; idx = 0 end d1 = (d0 == "A") ? "D" : "A" if d0 == "A" list.sort!{|a,b| a[idx] <=> b[idx] } else list.sort!{|a,b| b[idx] <=> a[idx] } end res['content-type'] = "text/html" res.body = <<-_end_of_html_ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <HTML> <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD> <BODY> <H1>Index of #{HTMLUtils::escape(req.path)}</H1> _end_of_html_ res.body << "<PRE>\n" res.body << " <A HREF=\"?N=#{d1}\">Name</A> " res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> " res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n" res.body << "<HR>\n" list.unshift [ "..", File::mtime(local_path+"/.."), -1 ] list.each{ |name, time, size| if name == ".." dname = "Parent Directory" elsif name.bytesize > 25 dname = name.sub(/^(.{23})(?:.*)/, '\1..') else dname = name end s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{HTMLUtils::escape(dname)}</A>" s << " " * (30 - dname.bytesize) s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22) s << (size >= 0 ? size.to_s : "-") << "\n" res.body << s } res.body << "</PRE><HR>" res.body << <<-_end_of_html_ <ADDRESS> #{HTMLUtils::escape(@config[:ServerSoftware])}<BR> at #{req.host}:#{req.port} </ADDRESS> </BODY> </HTML> _end_of_html_ end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 291 def set_filename(req, res) res.filename = @root.dup path_info = req.path_info.scan(%r/[^/]*|) path_info.unshift("") # dummy for checking @root dir while base = path_info.first break if base == "/" break unless File.directory?(File.expand_path(res.filename + base)) shift_path_info(req, res, path_info) call_callback(:DirectoryCallback, req, res) end if base = path_info.first if base == "/" if file = search_index_file(req, res) shift_path_info(req, res, path_info, file) call_callback(:FileCallback, req, res) return true end shift_path_info(req, res, path_info) elsif file = search_file(req, res, base) shift_path_info(req, res, path_info, file) call_callback(:FileCallback, req, res) return true else raise HTTPStatus::NotFound, "`#{req.path}' not found." end end return false end
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 330 def shift_path_info(req, res, path_info, base=nil) tmp = path_info.shift base = base || tmp req.path_info = path_info.join req.script_name << base res.filename = File.expand_path(res.filename + base) check_filename(req, res, File.basename(res.filename)) end
RFC3253: Versioning Extensions to WebDAV
(Web Distributed Authoring and Versioning)
VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE ACTIVITY
# File ../ruby/lib/webrick/httpservlet/filehandler.rb, line 233 def trailing_pathsep?(path) # check for trailing path separator: # File.dirname("/aaaa/bbbb/") #=> "/aaaa") # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb") # File.dirname("/aaaa/bbbb") #=> "/aaaa") # File.dirname("/aaaa/bbbbx") #=> "/aaaa") return File.dirname(path) != File.dirname(path+"x") end