From 385edf1e5c6dd5465067bae293844e640e9887a2 Mon Sep 17 00:00:00 2001 From: gotoyuzo Date: Tue, 12 Oct 2004 12:26:39 +0000 Subject: * lib/webrick/config.rb: add WEBrick::Config::FileHandler[:AcceptableLanguages]. * lib/webrick/httpservlet/filehandler.rb (WEBrick::HTTPServlet::FileHandler#set_filename): search files having suffix of language-name which Accept-Language header field includes if :AcceptableLanguages options is present. * lib/webrick/httpservlet/filehandler.rb (WEBrick::HTTPServlet::FileHandler#get_servlet): new method to search servlet correspond to the suffix of filename. * lib/webrick/httprequest.rb: add attributes access methods: accept, accept_charset, accept_encoding, accept_language, content_length and content_type. * lib/webrick/httpresponse.rb: add attribute access methods: content_length, content_length=, content_type and content_type=. * lib/webrick/httputils.rb (WEBrick::HTTPUtils.mime_types): use the second suffix to detect media type. (the first suffix may be a language name.) * lib/webrick/httputils.rb (WEBrick::HTTPUtils.parse_qvalues): add method to parse Accept header field. it returns an Array of values sorted by the qvalues. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@7033 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/webrick/config.rb | 1 + lib/webrick/httprequest.rb | 16 +++++ lib/webrick/httpresponse.rb | 20 ++++++- lib/webrick/httpservlet/filehandler.rb | 105 +++++++++++++++++++++++++-------- lib/webrick/httputils.rb | 25 ++++++-- 5 files changed, 136 insertions(+), 31 deletions(-) (limited to 'lib/webrick') diff --git a/lib/webrick/config.rb b/lib/webrick/config.rb index 9d2365e40e..7a8fc7df0b 100644 --- a/lib/webrick/config.rb +++ b/lib/webrick/config.rb @@ -72,6 +72,7 @@ module WEBrick :DirectoryCallback => nil, :FileCallback => nil, :UserDir => "public_html", + :AcceptableLanguages => [] # ["en", "ja", ... ] } BasicAuth = { diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb index 8943a037ff..576a750869 100644 --- a/lib/webrick/httprequest.rb +++ b/lib/webrick/httprequest.rb @@ -32,6 +32,8 @@ module WEBrick # Header and entity body attr_reader :raw_header, :header, :cookies + attr_reader :accept, :accept_charset + attr_reader :accept_encoding, :accept_language # Misc attr_accessor :user @@ -56,6 +58,8 @@ module WEBrick @raw_header = Array.new @header = nil @cookies = [] + @accept = @accept_charset = + @accept_encoding = @accept_language = nil @body = "" @addr = @peeraddr = nil @@ -83,6 +87,10 @@ module WEBrick @header['cookie'].each{|cookie| @cookies += Cookie::parse(cookie) } + @accept = HTTPUtils.parse_qvalues(self['accept']) + @accept_charset = HTTPUtils.parse_qvalues(self['accept-charset']) + @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding']) + @accept_language = HTTPUtils.parse_qvalues(self['accept-language']) end return if @request_method == "CONNECT" return if @unparsed_uri == "*" @@ -124,6 +132,14 @@ module WEBrick @query end + def content_length + return Integer(self['content-length']) + end + + def content_type + return self['content-type'] + end + def [](header_name) if @header value = @header[header_name.downcase] diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb index a0e7155ce6..717329fef3 100644 --- a/lib/webrick/httpresponse.rb +++ b/lib/webrick/httpresponse.rb @@ -63,6 +63,24 @@ module WEBrick @header[field.downcase] = value.to_s end + def content_length + if len = self['content-length'] + return Integer(len) + end + end + + def content_length=(len) + self['content-length'] = len.to_s + end + + def content_type + self['content-type'] + end + + def content_type=(type) + self['content-type'] = type + end + def each @header.each{|k, v| yield(k, v) } end @@ -250,7 +268,7 @@ module WEBrick _write_data(socket, "0#{CRLF}#{CRLF}") else size = @header['content-length'].to_i - _send_file(socket, @body, 0, size.to_i) + _send_file(socket, @body, 0, size) @sent_size = size end @body.close diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb index ab118ba5c9..1c48734987 100644 --- a/lib/webrick/httpservlet/filehandler.rb +++ b/lib/webrick/httpservlet/filehandler.rb @@ -126,7 +126,7 @@ module WEBrick end class FileHandler < AbstractServlet - HandlerTable = Hash.new(DefaultFileHandler) + HandlerTable = Hash.new def self.add_handler(suffix, handler) HandlerTable[suffix] = handler @@ -201,8 +201,7 @@ module WEBrick def exec_handler(req, res) raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root if set_filename(req, res) - suffix = (/\.(\w+)$/ =~ res.filename) && $1 - handler = @options[:HandlerTable][suffix] || HandlerTable[suffix] + handler = get_handler(req) call_callback(:HandlerCallback, req, res) h = handler.get_instance(@config, res.filename) h.service(req, res) @@ -212,41 +211,95 @@ module WEBrick return false end + def get_handler(req) + suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase + suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase + handler_table = @options[:HandlerTable] + return handler_table[suffix1] || handler_table[suffix2] || + HandlerTable[suffix1] || HandlerTable[suffix2] || + DefaultFileHandler + end + def set_filename(req, res) - handler = nil res.filename = @root.dup path_info = req.path_info.scan(%r|/[^/]*|) - while name = path_info.shift - if name == "/" - indices = @config[:DirectoryIndex] - index = indices.find{|i| FileTest::file?("#{res.filename}/#{i}") } - name = "/#{index}" if index - end - res.filename << name - req.script_name << name - req.path_info = path_info.join - - if File::fnmatch("/#{@options[:NondisclosureName]}", name) - @logger.log(Log::WARN, - "the request refers nondisclosure name `#{name}'.") - raise HTTPStatus::Forbidden, "`#{req.path}' not found." - end - st = (File::stat(res.filename) rescue nil) - raise HTTPStatus::NotFound, "`#{req.path}' not found." unless st - raise HTTPStatus::Forbidden, - "no access permission to `#{req.path}'." unless st.readable? + path_info.unshift("") # dummy for checking @root dir + while base = path_info.first + check_filename(base) + break if base == "/" + break unless File.directory?(res.filename + base) + shift_path_info(req, res, path_info) + call_callback(:DirectoryCallback, req, res) + end - if st.directory? - call_callback(:DirectoryCallback, req, res) - else + if base = path_info.first + check_filename(base) + 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 + def check_filename(name) + if File.fnmatch("/#{@options[:NondisclosureName]}", name) + @logger.warn("the request refers nondisclosure name `#{name}'.") + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + + 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 << base + end + + def search_index_file(req, res) + @config[:DirectoryIndex].each{|index| + if file = search_file(req, res, "/"+index) + return file + end + } + return nil + end + + 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 + def call_callback(callback_name, req, res) if cb = @options[callback_name] cb.call(req, res) diff --git a/lib/webrick/httputils.rb b/lib/webrick/httputils.rb index bae281ca1a..18e3e25887 100644 --- a/lib/webrick/httputils.rb +++ b/lib/webrick/httputils.rb @@ -115,10 +115,9 @@ module WEBrick module_function :load_mime_types def mime_type(filename, mime_tab) - if suffix = (/\.(\w+)$/ =~ filename && $1) - mtype = mime_tab[suffix.downcase] - end - mtype || "application/octet-stream" + suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase) + suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase) + mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream" end module_function :mime_type @@ -175,6 +174,24 @@ module WEBrick end module_function :parse_range_header + def parse_qvalues(value) + tmp = [] + if value + parts = value.split(/,\s*/) + parts.each {|part| + if m = %r{^([^\s,]+?)(?:;\s*q=([\d]+(?:\.[\d]+)))?$}.match(part) + lang = m[1] + q = (m[2] or 1).to_f + tmp.push([lang, q]) + end + } + tmp = tmp.sort_by{|lang, q| -q} + tmp.collect!{|lang, q| lang} + end + return tmp + end + module_function :parse_qvalues + ##### def dequote(str) -- cgit v1.2.3