diff options
| author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 | 
|---|---|---|
| committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 | 
| commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
| tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/webrick/httpservlet | |
Fresh start
Diffstat (limited to 'jni/ruby/lib/webrick/httpservlet')
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/abstract.rb | 153 | ||||
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/cgi_runner.rb | 46 | ||||
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/cgihandler.rb | 120 | ||||
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/erbhandler.rb | 87 | ||||
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/filehandler.rb | 521 | ||||
| -rw-r--r-- | jni/ruby/lib/webrick/httpservlet/prochandler.rb | 46 | 
6 files changed, 973 insertions, 0 deletions
| diff --git a/jni/ruby/lib/webrick/httpservlet/abstract.rb b/jni/ruby/lib/webrick/httpservlet/abstract.rb new file mode 100644 index 0000000..d3b00ab --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/abstract.rb @@ -0,0 +1,153 @@ +# +# httpservlet.rb -- HTTPServlet Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $ + +require 'thread' + +require 'webrick/htmlutils' +require 'webrick/httputils' +require 'webrick/httpstatus' + +module WEBrick +  module HTTPServlet +    class HTTPServletError < StandardError; end + +    ## +    # AbstractServlet allows HTTP server modules to be reused across multiple +    # servers and allows encapsulation of functionality. +    # +    # By default a servlet will respond to GET, HEAD (through an alias to GET) +    # and OPTIONS requests. +    # +    # By default a new servlet is initialized for every request.  A servlet +    # instance can be reused by overriding ::get_instance in the +    # AbstractServlet subclass. +    # +    # == A Simple Servlet +    # +    #  class Simple < WEBrick::HTTPServlet::AbstractServlet +    #    def do_GET request, response +    #      status, content_type, body = do_stuff_with request +    # +    #      response.status = status +    #      response['Content-Type'] = content_type +    #      response.body = body +    #    end +    # +    #    def do_stuff_with request +    #      return 200, 'text/plain', 'you got a page' +    #    end +    #  end +    # +    # This servlet can be mounted on a server at a given path: +    # +    #   server.mount '/simple', Simple +    # +    # == Servlet Configuration +    # +    # Servlets can be configured via initialize.  The first argument is the +    # HTTP server the servlet is being initialized for. +    # +    #  class Configurable < Simple +    #    def initialize server, color, size +    #      super server +    #      @color = color +    #      @size = size +    #    end +    # +    #    def do_stuff_with request +    #      content = "<p " \ +    #                %q{style="color: #{@color}; font-size: #{@size}"} \ +    #                ">Hello, World!" +    # +    #      return 200, "text/html", content +    #    end +    #  end +    # +    # This servlet must be provided two arguments at mount time: +    # +    #   server.mount '/configurable', Configurable, 'red', '2em' + +    class AbstractServlet + +      ## +      # Factory for servlet instances that will handle a request from +server+ +      # using +options+ from the mount point.  By default a new servlet +      # instance is created for every call. + +      def self.get_instance(server, *options) +        self.new(server, *options) +      end + +      ## +      # Initializes a new servlet for +server+ using +options+ which are +      # stored as-is in +@options+.  +@logger+ is also provided. + +      def initialize(server, *options) +        @server = @config = server +        @logger = @server[:Logger] +        @options = options +      end + +      ## +      # Dispatches to a +do_+ method based on +req+ if such a method is +      # available.  (+do_GET+ for a GET request).  Raises a MethodNotAllowed +      # exception if the method is not implemented. + +      def service(req, res) +        method_name = "do_" + req.request_method.gsub(/-/, "_") +        if respond_to?(method_name) +          __send__(method_name, req, res) +        else +          raise HTTPStatus::MethodNotAllowed, +                "unsupported method `#{req.request_method}'." +        end +      end + +      ## +      # Raises a NotFound exception + +      def do_GET(req, res) +        raise HTTPStatus::NotFound, "not found." +      end + +      ## +      # Dispatches to do_GET + +      def do_HEAD(req, res) +        do_GET(req, res) +      end + +      ## +      # Returns the allowed HTTP request methods + +      def do_OPTIONS(req, res) +        m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1} +        m.sort! +        res["allow"] = m.join(",") +      end + +      private + +      ## +      # Redirects to a path ending in / + +      def redirect_to_directory_uri(req, res) +        if req.path[-1] != ?/ +          location = WEBrick::HTTPUtils.escape_path(req.path + "/") +          if req.query_string && req.query_string.bytesize > 0 +            location << "?" << req.query_string +          end +          res.set_redirect(HTTPStatus::MovedPermanently, location) +        end +      end +    end + +  end +end diff --git a/jni/ruby/lib/webrick/httpservlet/cgi_runner.rb b/jni/ruby/lib/webrick/httpservlet/cgi_runner.rb new file mode 100644 index 0000000..32ecb6f --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/cgi_runner.rb @@ -0,0 +1,46 @@ +# +# cgi_runner.rb -- CGI launcher. +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $ + +def sysread(io, size) +  buf = "" +  while size > 0 +    tmp = io.sysread(size) +    buf << tmp +    size -= tmp.bytesize +  end +  return buf +end + +STDIN.binmode + +len = sysread(STDIN, 8).to_i +out = sysread(STDIN, len) +STDOUT.reopen(open(out, "w")) + +len = sysread(STDIN, 8).to_i +err = sysread(STDIN, len) +STDERR.reopen(open(err, "w")) + +len  = sysread(STDIN, 8).to_i +dump = sysread(STDIN, len) +hash = Marshal.restore(dump) +ENV.keys.each{|name| ENV.delete(name) } +hash.each{|k, v| ENV[k] = v if v } + +dir = File::dirname(ENV["SCRIPT_FILENAME"]) +Dir::chdir dir + +if ARGV[0] +  argv = ARGV.dup +  argv << ENV["SCRIPT_FILENAME"] +  exec(*argv) +  # NOTREACHED +end +exec ENV["SCRIPT_FILENAME"] diff --git a/jni/ruby/lib/webrick/httpservlet/cgihandler.rb b/jni/ruby/lib/webrick/httpservlet/cgihandler.rb new file mode 100644 index 0000000..3210041 --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/cgihandler.rb @@ -0,0 +1,120 @@ +# +# cgihandler.rb -- CGIHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $ + +require 'rbconfig' +require 'tempfile' +require 'webrick/config' +require 'webrick/httpservlet/abstract' + +module WEBrick +  module HTTPServlet + +    ## +    # Servlet for handling CGI scripts +    # +    # Example: +    # +    #  server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler, +    #               '/path/to/my_script') + +    class CGIHandler < AbstractServlet +      Ruby = RbConfig.ruby # :nodoc: +      CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc: + +      ## +      # Creates a new CGI script servlet for the script at +name+ + +      def initialize(server, name) +        super(server, name) +        @script_filename = name +        @tempdir = server[:TempDir] +        @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}" +      end + +      # :stopdoc: + +      def do_GET(req, res) +        cgi_in = IO::popen(@cgicmd, "wb") +        cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY) +        cgi_out.set_encoding("ASCII-8BIT") +        cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY) +        cgi_err.set_encoding("ASCII-8BIT") +        begin +          cgi_in.sync = true +          meta = req.meta_vars +          meta["SCRIPT_FILENAME"] = @script_filename +          meta["PATH"] = @config[:CGIPathEnv] +          if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM +            meta["SystemRoot"] = ENV["SystemRoot"] +          end +          dump = Marshal.dump(meta) + +          cgi_in.write("%8d" % cgi_out.path.bytesize) +          cgi_in.write(cgi_out.path) +          cgi_in.write("%8d" % cgi_err.path.bytesize) +          cgi_in.write(cgi_err.path) +          cgi_in.write("%8d" % dump.bytesize) +          cgi_in.write(dump) + +          if req.body and req.body.bytesize > 0 +            cgi_in.write(req.body) +          end +        ensure +          cgi_in.close +          status = $?.exitstatus +          sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM +          data = cgi_out.read +          cgi_out.close(true) +          if errmsg = cgi_err.read +            if errmsg.bytesize > 0 +              @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg) +            end +          end +          cgi_err.close(true) +        end + +        if status != 0 +          @logger.error("CGIHandler: #{@script_filename} exit with #{status}") +        end + +        data = "" unless data +        raw_header, body = data.split(/^[\xd\xa]+/, 2) +        raise HTTPStatus::InternalServerError, +          "Premature end of script headers: #{@script_filename}" if body.nil? + +        begin +          header = HTTPUtils::parse_header(raw_header) +          if /^(\d+)/ =~ header['status'][0] +            res.status = $1.to_i +            header.delete('status') +          end +          if header.has_key?('location') +            # RFC 3875 6.2.3, 6.2.4 +            res.status = 302 unless (300...400) === res.status +          end +          if header.has_key?('set-cookie') +            header['set-cookie'].each{|k| +              res.cookies << Cookie.parse_set_cookie(k) +            } +            header.delete('set-cookie') +          end +          header.each{|key, val| res[key] = val.join(", ") } +        rescue => ex +          raise HTTPStatus::InternalServerError, ex.message +        end +        res.body = body +      end +      alias do_POST do_GET + +      # :startdoc: +    end + +  end +end diff --git a/jni/ruby/lib/webrick/httpservlet/erbhandler.rb b/jni/ruby/lib/webrick/httpservlet/erbhandler.rb new file mode 100644 index 0000000..1b8a82d --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/erbhandler.rb @@ -0,0 +1,87 @@ +# +# erbhandler.rb -- ERBHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $ + +require 'webrick/httpservlet/abstract.rb' + +require 'erb' + +module WEBrick +  module HTTPServlet + +    ## +    # ERBHandler evaluates an ERB file and returns the result.  This handler +    # is automatically used if there are .rhtml files in a directory served by +    # the FileHandler. +    # +    # ERBHandler supports GET and POST methods. +    # +    # The ERB file is evaluated with the local variables +servlet_request+ and +    # +servlet_response+ which are a WEBrick::HTTPRequest and +    # WEBrick::HTTPResponse respectively. +    # +    # Example .rhtml file: +    # +    #   Request to <%= servlet_request.request_uri %> +    # +    #   Query params <%= servlet_request.query.inspect %> + +    class ERBHandler < AbstractServlet + +      ## +      # Creates a new ERBHandler on +server+ that will evaluate and serve the +      # ERB file +name+ + +      def initialize(server, name) +        super(server, name) +        @script_filename = name +      end + +      ## +      # Handles GET requests + +      def do_GET(req, res) +        unless defined?(ERB) +          @logger.warn "#{self.class}: ERB not defined." +          raise HTTPStatus::Forbidden, "ERBHandler cannot work." +        end +        begin +          data = open(@script_filename){|io| io.read } +          res.body = evaluate(ERB.new(data), req, res) +          res['content-type'] ||= +            HTTPUtils::mime_type(@script_filename, @config[:MimeTypes]) +        rescue StandardError +          raise +        rescue Exception => ex +          @logger.error(ex) +          raise HTTPStatus::InternalServerError, ex.message +        end +      end + +      ## +      # Handles POST requests + +      alias do_POST do_GET + +      private + +      ## +      # Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as +      # local variables. + +      def evaluate(erb, servlet_request, servlet_response) +        Module.new.module_eval{ +          servlet_request.meta_vars +          servlet_request.query +          erb.result(binding) +        } +      end +    end +  end +end diff --git a/jni/ruby/lib/webrick/httpservlet/filehandler.rb b/jni/ruby/lib/webrick/httpservlet/filehandler.rb new file mode 100644 index 0000000..cc9db4a --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/filehandler.rb @@ -0,0 +1,521 @@ +# +# filehandler.rb -- FileHandler Module +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2003 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $ + +require 'thread' +require 'time' + +require 'webrick/htmlutils' +require 'webrick/httputils' +require 'webrick/httpstatus' + +module WEBrick +  module HTTPServlet + +    ## +    # Servlet for serving a single file.  You probably want to use the +    # FileHandler servlet instead as it handles directories and fancy indexes. +    # +    # Example: +    # +    #   server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler, +    #                '/path/to/my_page.txt') +    # +    # This servlet handles If-Modified-Since and Range requests. + +    class DefaultFileHandler < AbstractServlet + +      ## +      # Creates a DefaultFileHandler instance for the file at +local_path+. + +      def initialize(server, local_path) +        super(server, local_path) +        @local_path = local_path +      end + +      # :stopdoc: + +      def do_GET(req, res) +        st = File::stat(@local_path) +        mtime = st.mtime +        res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i) + +        if not_modified?(req, res, mtime, res['etag']) +          res.body = '' +          raise HTTPStatus::NotModified +        elsif req['range'] +          make_partial_content(req, res, @local_path, st.size) +          raise HTTPStatus::PartialContent +        else +          mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes]) +          res['content-type'] = mtype +          res['content-length'] = st.size +          res['last-modified'] = mtime.httpdate +          res.body = open(@local_path, "rb") +        end +      end + +      def not_modified?(req, res, mtime, etag) +        if ir = req['if-range'] +          begin +            if Time.httpdate(ir) >= mtime +              return true +            end +          rescue +            if HTTPUtils::split_header_value(ir).member?(res['etag']) +              return true +            end +          end +        end + +        if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime +          return true +        end + +        if (inm = req['if-none-match']) && +           HTTPUtils::split_header_value(inm).member?(res['etag']) +          return true +        end + +        return false +      end + +      def make_partial_content(req, res, filename, filesize) +        mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes]) +        unless ranges = HTTPUtils::parse_range_header(req['range']) +          raise HTTPStatus::BadRequest, +            "Unrecognized range-spec: \"#{req['range']}\"" +        end +        open(filename, "rb"){|io| +          if ranges.size > 1 +            time = Time.now +            boundary = "#{time.sec}_#{time.usec}_#{Process::pid}" +            body = '' +            ranges.each{|range| +              first, last = prepare_range(range, filesize) +              next if first < 0 +              io.pos = first +              content = io.read(last-first+1) +              body << "--" << boundary << CRLF +              body << "Content-Type: #{mtype}" << CRLF +              body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF +              body << CRLF +              body << content +              body << CRLF +            } +            raise HTTPStatus::RequestRangeNotSatisfiable if body.empty? +            body << "--" << boundary << "--" << CRLF +            res["content-type"] = "multipart/byteranges; boundary=#{boundary}" +            res.body = body +          elsif range = ranges[0] +            first, last = prepare_range(range, filesize) +            raise HTTPStatus::RequestRangeNotSatisfiable if first < 0 +            if last == filesize - 1 +              content = io.dup +              content.pos = first +            else +              io.pos = first +              content = io.read(last-first+1) +            end +            res['content-type'] = mtype +            res['content-range'] = "bytes #{first}-#{last}/#{filesize}" +            res['content-length'] = last - first + 1 +            res.body = content +          else +            raise HTTPStatus::BadRequest +          end +        } +      end + +      def prepare_range(range, filesize) +        first = range.first < 0 ? filesize + range.first : range.first +        return -1, -1 if first < 0 || first >= filesize +        last = range.last < 0 ? filesize + range.last : range.last +        last = filesize - 1 if last >= filesize +        return first, last +      end + +      # :startdoc: +    end + +    ## +    # Serves a directory including fancy indexing and a variety of other +    # options. +    # +    # Example: +    # +    #   server.mount '/assets', WEBrick::FileHandler, '/path/to/assets' + +    class FileHandler < AbstractServlet +      HandlerTable = Hash.new # :nodoc: + +      ## +      # Allow custom handling of requests for files with +suffix+ by class +      # +handler+ + +      def self.add_handler(suffix, handler) +        HandlerTable[suffix] = handler +      end + +      ## +      # Remove custom handling of requests for files with +suffix+ + +      def self.remove_handler(suffix) +        HandlerTable.delete(suffix) +      end + +      ## +      # Creates a FileHandler servlet on +server+ that serves files starting +      # at directory +root+ +      # +      # +options+ may be a Hash containing keys from +      # WEBrick::Config::FileHandler or +true+ or +false+. +      # +      # If +options+ is true or false then +:FancyIndexing+ is enabled or +      # disabled respectively. + +      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 + +      # :stopdoc: + +      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 + +      def do_GET(req, res) +        unless exec_handler(req, res) +          set_dir_list(req, res) +        end +      end + +      def do_POST(req, res) +        unless exec_handler(req, res) +          raise HTTPStatus::NotFound, "`#{req.path}' not found." +        end +      end + +      def do_OPTIONS(req, res) +        unless exec_handler(req, res) +          super(req, res) +        end +      end + +      # ToDo +      # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV +      # +      # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE +      # LOCK UNLOCK + +      # RFC3253: Versioning Extensions to WebDAV +      #          (Web Distributed Authoring and Versioning) +      # +      # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT +      # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY + +      private + +      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 + +      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 + +      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 + +      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 + +      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 + +      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 + +      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 + +      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) +        end +      end + +      def windows_ambiguous_name?(name) +        return true if /[. ]+\z/ =~ name +        return true if /::\$DATA\z/ =~ name +        return false +      end + +      def nondisclosure_name?(name) +        @options[:NondisclosureName].each{|pattern| +          if File.fnmatch(pattern, name, File::FNM_CASEFOLD) +            return true +          end +        } +        return false +      end + +      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! + +        query = req.query + +        d0 = nil +        idx = nil +        %w[N M S].each_with_index do |q, i| +          if d = query.delete(q) +            idx ||= i +            d0 ||= d +          end +        end +        d0 ||= "A" +        idx ||= 0 +        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 + +        namewidth = query["NameWidth"] +        if namewidth == "*" +          namewidth = nil +        elsif !namewidth or (namewidth = namewidth.to_i) < 2 +          namewidth = 25 +        end +        query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")} + +        type = "text/html" +        case enc = Encoding.find('filesystem') +        when Encoding::US_ASCII, Encoding::ASCII_8BIT +        else +          type << "; charset=\"#{enc.name}\"" +        end +        res['content-type'] = type + +        title = "Index of #{HTMLUtils::escape(req.path)}" +        res.body = <<-_end_of_html_ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +  <HEAD> +    <TITLE>#{title}</TITLE> +    <style type="text/css"> +    <!-- +    .name, .mtime { text-align: left; } +    .size { text-align: right; } +    td { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } +    table { border-collapse: collapse; } +    tr th { border-bottom: 2px groove; } +    //--> +    </style> +  </HEAD> +  <BODY> +    <H1>#{title}</H1> +        _end_of_html_ + +        res.body << "<TABLE width=\"100%\"><THEAD><TR>\n" +        res.body << "<TH class=\"name\"><A HREF=\"?N=#{d1}#{query}\">Name</A></TH>" +        res.body << "<TH class=\"mtime\"><A HREF=\"?M=#{d1}#{query}\">Last modified</A></TH>" +        res.body << "<TH class=\"size\"><A HREF=\"?S=#{d1}#{query}\">Size</A></TH>\n" +        res.body << "</TR></THEAD>\n" +        res.body << "<TBODY>\n" + +        query.sub!(/\A&/, '?') +        list.unshift [ "..", File::mtime(local_path+"/.."), -1 ] +        list.each{ |name, time, size| +          if name == ".." +            dname = "Parent Directory" +          elsif namewidth and name.size > namewidth +            dname = name[0...(namewidth - 2)] << '..' +          else +            dname = name +          end +          s =  "<TR><TD class=\"name\"><A HREF=\"#{HTTPUtils::escape(name)}#{query if name.end_with?('/')}\">#{HTMLUtils::escape(dname)}</A></TD>" +          s << "<TD class=\"mtime\">" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << "</TD>" +          s << "<TD class=\"size\">" << (size >= 0 ? size.to_s : "-") << "</TD></TR>\n" +          res.body << s +        } +        res.body << "</TBODY></TABLE>" +        res.body << "<HR>" + +        res.body << <<-_end_of_html_ +    <ADDRESS> +     #{HTMLUtils::escape(@config[:ServerSoftware])}<BR> +     at #{req.host}:#{req.port} +    </ADDRESS> +  </BODY> +</HTML> +        _end_of_html_ +      end + +      # :startdoc: +    end +  end +end diff --git a/jni/ruby/lib/webrick/httpservlet/prochandler.rb b/jni/ruby/lib/webrick/httpservlet/prochandler.rb new file mode 100644 index 0000000..2f5aa66 --- /dev/null +++ b/jni/ruby/lib/webrick/httpservlet/prochandler.rb @@ -0,0 +1,46 @@ +# +# prochandler.rb -- ProcHandler Class +# +# Author: IPR -- Internet Programming with Ruby -- writers +# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou +# Copyright (c) 2002 Internet Programming with Ruby writers. All rights +# reserved. +# +# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $ + +require 'webrick/httpservlet/abstract.rb' + +module WEBrick +  module HTTPServlet + +    ## +    # Mounts a proc at a path that accepts a request and response. +    # +    # Instead of mounting this servlet with WEBrick::HTTPServer#mount use +    # WEBrick::HTTPServer#mount_proc: +    # +    #   server.mount_proc '/' do |req, res| +    #     res.body = 'it worked!' +    #     res.status = 200 +    #   end + +    class ProcHandler < AbstractServlet +      # :stopdoc: +      def get_instance(server, *options) +        self +      end + +      def initialize(proc) +        @proc = proc +      end + +      def do_GET(request, response) +        @proc.call(request, response) +      end + +      alias do_POST do_GET +      # :startdoc: +    end + +  end +end | 
