rubysl-webrick 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +14 -6
- data/.travis.yml +5 -6
- data/lib/rubysl/webrick/version.rb +1 -1
- data/lib/rubysl/webrick/webrick.rb +199 -2
- data/lib/webrick/accesslog.rb +96 -5
- data/lib/webrick/cgi.rb +80 -29
- data/lib/webrick/compat.rb +20 -0
- data/lib/webrick/config.rb +59 -5
- data/lib/webrick/cookie.rb +66 -5
- data/lib/webrick/htmlutils.rb +4 -1
- data/lib/webrick/httpauth.rb +53 -3
- data/lib/webrick/httpauth/authenticator.rb +53 -16
- data/lib/webrick/httpauth/basicauth.rb +45 -2
- data/lib/webrick/httpauth/digestauth.rb +82 -17
- data/lib/webrick/httpauth/htdigest.rb +38 -1
- data/lib/webrick/httpauth/htgroup.rb +32 -0
- data/lib/webrick/httpauth/htpasswd.rb +40 -2
- data/lib/webrick/httpauth/userdb.rb +27 -4
- data/lib/webrick/httpproxy.rb +197 -112
- data/lib/webrick/httprequest.rb +268 -50
- data/lib/webrick/httpresponse.rb +170 -33
- data/lib/webrick/https.rb +26 -3
- data/lib/webrick/httpserver.rb +75 -7
- data/lib/webrick/httpservlet/abstract.rb +88 -6
- data/lib/webrick/httpservlet/cgi_runner.rb +5 -4
- data/lib/webrick/httpservlet/cgihandler.rb +37 -18
- data/lib/webrick/httpservlet/erbhandler.rb +40 -7
- data/lib/webrick/httpservlet/filehandler.rb +116 -28
- data/lib/webrick/httpservlet/prochandler.rb +17 -4
- data/lib/webrick/httpstatus.rb +86 -18
- data/lib/webrick/httputils.rb +131 -23
- data/lib/webrick/httpversion.rb +28 -2
- data/lib/webrick/log.rb +72 -5
- data/lib/webrick/server.rb +158 -33
- data/lib/webrick/ssl.rb +78 -9
- data/lib/webrick/utils.rb +151 -5
- data/lib/webrick/version.rb +5 -1
- data/rubysl-webrick.gemspec +0 -1
- metadata +12 -24
| @@ -18,17 +18,88 @@ module WEBrick | |
| 18 18 | 
             
              module HTTPServlet
         | 
| 19 19 | 
             
                class HTTPServletError < StandardError; end
         | 
| 20 20 |  | 
| 21 | 
            +
                ##
         | 
| 22 | 
            +
                # AbstractServlet allows HTTP server modules to be reused across multiple
         | 
| 23 | 
            +
                # servers and allows encapsulation of functionality.
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # By default a servlet will respond to GET, HEAD (through an alias to GET)
         | 
| 26 | 
            +
                # and OPTIONS requests.
         | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # By default a new servlet is initialized for every request.  A servlet
         | 
| 29 | 
            +
                # instance can be reused by overriding ::get_instance in the
         | 
| 30 | 
            +
                # AbstractServlet subclass.
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # == A Simple Servlet
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                #  class Simple < WEBrick::HTTPServlet::AbstractServlet
         | 
| 35 | 
            +
                #    def do_GET request, response
         | 
| 36 | 
            +
                #      status, content_type, body = do_stuff_with request
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                #      response.status = status
         | 
| 39 | 
            +
                #      response['Content-Type'] = content_type
         | 
| 40 | 
            +
                #      response.body = body
         | 
| 41 | 
            +
                #    end
         | 
| 42 | 
            +
                #
         | 
| 43 | 
            +
                #    def do_stuff_with request
         | 
| 44 | 
            +
                #      return 200, 'text/plain', 'you got a page'
         | 
| 45 | 
            +
                #    end
         | 
| 46 | 
            +
                #  end
         | 
| 47 | 
            +
                #
         | 
| 48 | 
            +
                # This servlet can be mounted on a server at a given path:
         | 
| 49 | 
            +
                #
         | 
| 50 | 
            +
                #   server.mount '/simple', Simple
         | 
| 51 | 
            +
                #
         | 
| 52 | 
            +
                # == Servlet Configuration
         | 
| 53 | 
            +
                #
         | 
| 54 | 
            +
                # Servlets can be configured via initialize.  The first argument is the
         | 
| 55 | 
            +
                # HTTP server the servlet is being initialized for.
         | 
| 56 | 
            +
                #
         | 
| 57 | 
            +
                #  class Configurable < Simple
         | 
| 58 | 
            +
                #    def initialize server, color, size
         | 
| 59 | 
            +
                #      super server
         | 
| 60 | 
            +
                #      @color = color
         | 
| 61 | 
            +
                #      @size = size
         | 
| 62 | 
            +
                #    end
         | 
| 63 | 
            +
                #
         | 
| 64 | 
            +
                #    def do_stuff_with request
         | 
| 65 | 
            +
                #      content = "<p " \
         | 
| 66 | 
            +
                #                %q{style="color: #{@color}; font-size: #{@size}"} \
         | 
| 67 | 
            +
                #                ">Hello, World!"
         | 
| 68 | 
            +
                #
         | 
| 69 | 
            +
                #      return 200, "text/html", content
         | 
| 70 | 
            +
                #    end
         | 
| 71 | 
            +
                #  end
         | 
| 72 | 
            +
                #
         | 
| 73 | 
            +
                # This servlet must be provided two arguments at mount time:
         | 
| 74 | 
            +
                #
         | 
| 75 | 
            +
                #   server.mount '/configurable', Configurable, 'red', '2em'
         | 
| 76 | 
            +
             | 
| 21 77 | 
             
                class AbstractServlet
         | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 78 | 
            +
             | 
| 79 | 
            +
                  ##
         | 
| 80 | 
            +
                  # Factory for servlet instances that will handle a request from +server+
         | 
| 81 | 
            +
                  # using +options+ from the mount point.  By default a new servlet
         | 
| 82 | 
            +
                  # instance is created for every call.
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  def self.get_instance(server, *options)
         | 
| 85 | 
            +
                    self.new(server, *options)
         | 
| 24 86 | 
             
                  end
         | 
| 25 87 |  | 
| 88 | 
            +
                  ##
         | 
| 89 | 
            +
                  # Initializes a new servlet for +server+ using +options+ which are
         | 
| 90 | 
            +
                  # stored as-is in +@options+.  +@logger+ is also provided.
         | 
| 91 | 
            +
             | 
| 26 92 | 
             
                  def initialize(server, *options)
         | 
| 27 93 | 
             
                    @server = @config = server
         | 
| 28 94 | 
             
                    @logger = @server[:Logger]
         | 
| 29 95 | 
             
                    @options = options
         | 
| 30 96 | 
             
                  end
         | 
| 31 97 |  | 
| 98 | 
            +
                  ##
         | 
| 99 | 
            +
                  # Dispatches to a +do_+ method based on +req+ if such a method is
         | 
| 100 | 
            +
                  # available.  (+do_GET+ for a GET request).  Raises a MethodNotAllowed
         | 
| 101 | 
            +
                  # exception if the method is not implemented.
         | 
| 102 | 
            +
             | 
| 32 103 | 
             
                  def service(req, res)
         | 
| 33 104 | 
             
                    method_name = "do_" + req.request_method.gsub(/-/, "_")
         | 
| 34 105 | 
             
                    if respond_to?(method_name)
         | 
| @@ -39,27 +110,38 @@ module WEBrick | |
| 39 110 | 
             
                    end
         | 
| 40 111 | 
             
                  end
         | 
| 41 112 |  | 
| 113 | 
            +
                  ##
         | 
| 114 | 
            +
                  # Raises a NotFound exception
         | 
| 115 | 
            +
             | 
| 42 116 | 
             
                  def do_GET(req, res)
         | 
| 43 117 | 
             
                    raise HTTPStatus::NotFound, "not found."
         | 
| 44 118 | 
             
                  end
         | 
| 45 119 |  | 
| 120 | 
            +
                  ##
         | 
| 121 | 
            +
                  # Dispatches to do_GET
         | 
| 122 | 
            +
             | 
| 46 123 | 
             
                  def do_HEAD(req, res)
         | 
| 47 124 | 
             
                    do_GET(req, res)
         | 
| 48 125 | 
             
                  end
         | 
| 49 126 |  | 
| 127 | 
            +
                  ##
         | 
| 128 | 
            +
                  # Returns the allowed HTTP request methods
         | 
| 129 | 
            +
             | 
| 50 130 | 
             
                  def do_OPTIONS(req, res)
         | 
| 51 | 
            -
                    m = self.methods.grep( | 
| 52 | 
            -
                    m.collect!{|i| i.sub(/do_/, "") }
         | 
| 131 | 
            +
                    m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1}
         | 
| 53 132 | 
             
                    m.sort!
         | 
| 54 133 | 
             
                    res["allow"] = m.join(",")
         | 
| 55 134 | 
             
                  end
         | 
| 56 135 |  | 
| 57 136 | 
             
                  private
         | 
| 58 137 |  | 
| 138 | 
            +
                  ##
         | 
| 139 | 
            +
                  # Redirects to a path ending in /
         | 
| 140 | 
            +
             | 
| 59 141 | 
             
                  def redirect_to_directory_uri(req, res)
         | 
| 60 142 | 
             
                    if req.path[-1] != ?/
         | 
| 61 | 
            -
                      location = req.path + "/"
         | 
| 62 | 
            -
                      if req.query_string && req.query_string. | 
| 143 | 
            +
                      location = WEBrick::HTTPUtils.escape_path(req.path + "/")
         | 
| 144 | 
            +
                      if req.query_string && req.query_string.bytesize > 0
         | 
| 63 145 | 
             
                        location << "?" << req.query_string
         | 
| 64 146 | 
             
                      end
         | 
| 65 147 | 
             
                      res.set_redirect(HTTPStatus::MovedPermanently, location)
         | 
| @@ -13,14 +13,13 @@ def sysread(io, size) | |
| 13 13 | 
             
              while size > 0
         | 
| 14 14 | 
             
                tmp = io.sysread(size)
         | 
| 15 15 | 
             
                buf << tmp
         | 
| 16 | 
            -
                size -= tmp. | 
| 16 | 
            +
                size -= tmp.bytesize
         | 
| 17 17 | 
             
              end
         | 
| 18 18 | 
             
              return buf
         | 
| 19 19 | 
             
            end
         | 
| 20 20 |  | 
| 21 21 | 
             
            STDIN.binmode
         | 
| 22 22 |  | 
| 23 | 
            -
            buf = ""
         | 
| 24 23 | 
             
            len = sysread(STDIN, 8).to_i
         | 
| 25 24 | 
             
            out = sysread(STDIN, len)
         | 
| 26 25 | 
             
            STDOUT.reopen(open(out, "w"))
         | 
| @@ -38,8 +37,10 @@ hash.each{|k, v| ENV[k] = v if v } | |
| 38 37 | 
             
            dir = File::dirname(ENV["SCRIPT_FILENAME"])
         | 
| 39 38 | 
             
            Dir::chdir dir
         | 
| 40 39 |  | 
| 41 | 
            -
            if  | 
| 42 | 
            -
               | 
| 40 | 
            +
            if ARGV[0]
         | 
| 41 | 
            +
              argv = ARGV.dup
         | 
| 42 | 
            +
              argv << ENV["SCRIPT_FILENAME"]
         | 
| 43 | 
            +
              exec(*argv)
         | 
| 43 44 | 
             
              # NOTREACHED
         | 
| 44 45 | 
             
            end
         | 
| 45 46 | 
             
            exec ENV["SCRIPT_FILENAME"]
         | 
| @@ -1,11 +1,11 @@ | |
| 1 | 
            -
            # | 
| 1 | 
            +
            #
         | 
| 2 2 | 
             
            # cgihandler.rb -- CGIHandler Class
         | 
| 3 | 
            -
            # | 
| 3 | 
            +
            #
         | 
| 4 4 | 
             
            # Author: IPR -- Internet Programming with Ruby -- writers
         | 
| 5 5 | 
             
            # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
         | 
| 6 6 | 
             
            # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
         | 
| 7 7 | 
             
            # reserved.
         | 
| 8 | 
            -
            # | 
| 8 | 
            +
            #
         | 
| 9 9 | 
             
            # $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
         | 
| 10 10 |  | 
| 11 11 | 
             
            require 'rbconfig'
         | 
| @@ -16,26 +16,39 @@ require 'webrick/httpservlet/abstract' | |
| 16 16 | 
             
            module WEBrick
         | 
| 17 17 | 
             
              module HTTPServlet
         | 
| 18 18 |  | 
| 19 | 
            +
                ##
         | 
| 20 | 
            +
                # Servlet for handling CGI scripts
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # Example:
         | 
| 23 | 
            +
                #
         | 
| 24 | 
            +
                #  server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler,
         | 
| 25 | 
            +
                #               '/path/to/my_script')
         | 
| 26 | 
            +
             | 
| 19 27 | 
             
                class CGIHandler < AbstractServlet
         | 
| 20 | 
            -
                  Ruby =  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                   | 
| 28 | 
            +
                  Ruby = RbConfig.ruby # :nodoc:
         | 
| 29 | 
            +
                  CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  ##
         | 
| 32 | 
            +
                  # Creates a new CGI script servlet for the script at +name+
         | 
| 24 33 |  | 
| 25 34 | 
             
                  def initialize(server, name)
         | 
| 26 | 
            -
                    super
         | 
| 35 | 
            +
                    super(server, name)
         | 
| 27 36 | 
             
                    @script_filename = name
         | 
| 28 37 | 
             
                    @tempdir = server[:TempDir]
         | 
| 29 38 | 
             
                    @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
         | 
| 30 39 | 
             
                  end
         | 
| 31 40 |  | 
| 41 | 
            +
                  # :stopdoc:
         | 
| 42 | 
            +
             | 
| 32 43 | 
             
                  def do_GET(req, res)
         | 
| 33 44 | 
             
                    data = nil
         | 
| 34 45 | 
             
                    status = -1
         | 
| 35 46 |  | 
| 36 47 | 
             
                    cgi_in = IO::popen(@cgicmd, "wb")
         | 
| 37 | 
            -
                    cgi_out = Tempfile.new("webrick.cgiout.", @tempdir)
         | 
| 38 | 
            -
                     | 
| 48 | 
            +
                    cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
         | 
| 49 | 
            +
                    cgi_out.set_encoding("ASCII-8BIT")
         | 
| 50 | 
            +
                    cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY)
         | 
| 51 | 
            +
                    cgi_err.set_encoding("ASCII-8BIT")
         | 
| 39 52 | 
             
                    begin
         | 
| 40 53 | 
             
                      cgi_in.sync = true
         | 
| 41 54 | 
             
                      meta = req.meta_vars
         | 
| @@ -46,14 +59,14 @@ module WEBrick | |
| 46 59 | 
             
                      end
         | 
| 47 60 | 
             
                      dump = Marshal.dump(meta)
         | 
| 48 61 |  | 
| 49 | 
            -
                      cgi_in.write("%8d" % cgi_out.path. | 
| 62 | 
            +
                      cgi_in.write("%8d" % cgi_out.path.bytesize)
         | 
| 50 63 | 
             
                      cgi_in.write(cgi_out.path)
         | 
| 51 | 
            -
                      cgi_in.write("%8d" % cgi_err.path. | 
| 64 | 
            +
                      cgi_in.write("%8d" % cgi_err.path.bytesize)
         | 
| 52 65 | 
             
                      cgi_in.write(cgi_err.path)
         | 
| 53 | 
            -
                      cgi_in.write("%8d" % dump. | 
| 66 | 
            +
                      cgi_in.write("%8d" % dump.bytesize)
         | 
| 54 67 | 
             
                      cgi_in.write(dump)
         | 
| 55 68 |  | 
| 56 | 
            -
                      if req.body and req.body. | 
| 69 | 
            +
                      if req.body and req.body.bytesize > 0
         | 
| 57 70 | 
             
                        cgi_in.write(req.body)
         | 
| 58 71 | 
             
                      end
         | 
| 59 72 | 
             
                    ensure
         | 
| @@ -63,19 +76,19 @@ module WEBrick | |
| 63 76 | 
             
                      data = cgi_out.read
         | 
| 64 77 | 
             
                      cgi_out.close(true)
         | 
| 65 78 | 
             
                      if errmsg = cgi_err.read
         | 
| 66 | 
            -
                        if errmsg. | 
| 79 | 
            +
                        if errmsg.bytesize > 0
         | 
| 67 80 | 
             
                          @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
         | 
| 68 81 | 
             
                        end
         | 
| 69 | 
            -
                      end | 
| 82 | 
            +
                      end
         | 
| 70 83 | 
             
                      cgi_err.close(true)
         | 
| 71 84 | 
             
                    end
         | 
| 72 | 
            -
             | 
| 85 | 
            +
             | 
| 73 86 | 
             
                    if status != 0
         | 
| 74 87 | 
             
                      @logger.error("CGIHandler: #{@script_filename} exit with #{status}")
         | 
| 75 88 | 
             
                    end
         | 
| 76 89 |  | 
| 77 90 | 
             
                    data = "" unless data
         | 
| 78 | 
            -
                    raw_header, body = data.split(/^[\xd\xa] | 
| 91 | 
            +
                    raw_header, body = data.split(/^[\xd\xa]+/, 2)
         | 
| 79 92 | 
             
                    raise HTTPStatus::InternalServerError,
         | 
| 80 93 | 
             
                      "Premature end of script headers: #{@script_filename}" if body.nil?
         | 
| 81 94 |  | 
| @@ -85,6 +98,10 @@ module WEBrick | |
| 85 98 | 
             
                        res.status = $1.to_i
         | 
| 86 99 | 
             
                        header.delete('status')
         | 
| 87 100 | 
             
                      end
         | 
| 101 | 
            +
                      if header.has_key?('location')
         | 
| 102 | 
            +
                        # RFC 3875 6.2.3, 6.2.4
         | 
| 103 | 
            +
                        res.status = 302 unless (300...400) === res.status
         | 
| 104 | 
            +
                      end
         | 
| 88 105 | 
             
                      if header.has_key?('set-cookie')
         | 
| 89 106 | 
             
                        header['set-cookie'].each{|k|
         | 
| 90 107 | 
             
                          res.cookies << Cookie.parse_set_cookie(k)
         | 
| @@ -98,6 +115,8 @@ module WEBrick | |
| 98 115 | 
             
                    res.body = body
         | 
| 99 116 | 
             
                  end
         | 
| 100 117 | 
             
                  alias do_POST do_GET
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  # :startdoc:
         | 
| 101 120 | 
             
                end
         | 
| 102 121 |  | 
| 103 122 | 
             
              end
         | 
| @@ -1,11 +1,11 @@ | |
| 1 | 
            -
            # | 
| 1 | 
            +
            #
         | 
| 2 2 | 
             
            # erbhandler.rb -- ERBHandler Class
         | 
| 3 | 
            -
            # | 
| 3 | 
            +
            #
         | 
| 4 4 | 
             
            # Author: IPR -- Internet Programming with Ruby -- writers
         | 
| 5 5 | 
             
            # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
         | 
| 6 6 | 
             
            # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
         | 
| 7 7 | 
             
            # reserved.
         | 
| 8 | 
            -
            # | 
| 8 | 
            +
            #
         | 
| 9 9 | 
             
            # $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
         | 
| 10 10 |  | 
| 11 11 | 
             
            require 'webrick/httpservlet/abstract.rb'
         | 
| @@ -15,12 +15,37 @@ require 'erb' | |
| 15 15 | 
             
            module WEBrick
         | 
| 16 16 | 
             
              module HTTPServlet
         | 
| 17 17 |  | 
| 18 | 
            +
                ##
         | 
| 19 | 
            +
                # ERBHandler evaluates an ERB file and returns the result.  This handler
         | 
| 20 | 
            +
                # is automatically used if there are .rhtml files in a directory served by
         | 
| 21 | 
            +
                # the FileHandler.
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                # ERBHandler supports GET and POST methods.
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # The ERB file is evaluated with the local variables +servlet_request+ and
         | 
| 26 | 
            +
                # +servlet_response+ which are a WEBrick::HTTPRequest and
         | 
| 27 | 
            +
                # WEBrick::HTTPResponse respectively.
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                # Example .rhtml file:
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                #   Request to <%= servlet_request.request_uri %>
         | 
| 32 | 
            +
                #
         | 
| 33 | 
            +
                #   Query params <%= servlet_request.query.inspect %>
         | 
| 34 | 
            +
             | 
| 18 35 | 
             
                class ERBHandler < AbstractServlet
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  ##
         | 
| 38 | 
            +
                  # Creates a new ERBHandler on +server+ that will evaluate and serve the
         | 
| 39 | 
            +
                  # ERB file +name+
         | 
| 40 | 
            +
             | 
| 19 41 | 
             
                  def initialize(server, name)
         | 
| 20 | 
            -
                    super
         | 
| 42 | 
            +
                    super(server, name)
         | 
| 21 43 | 
             
                    @script_filename = name
         | 
| 22 44 | 
             
                  end
         | 
| 23 45 |  | 
| 46 | 
            +
                  ##
         | 
| 47 | 
            +
                  # Handles GET requests
         | 
| 48 | 
            +
             | 
| 24 49 | 
             
                  def do_GET(req, res)
         | 
| 25 50 | 
             
                    unless defined?(ERB)
         | 
| 26 51 | 
             
                      @logger.warn "#{self.class}: ERB not defined."
         | 
| @@ -29,7 +54,7 @@ module WEBrick | |
| 29 54 | 
             
                    begin
         | 
| 30 55 | 
             
                      data = open(@script_filename){|io| io.read }
         | 
| 31 56 | 
             
                      res.body = evaluate(ERB.new(data), req, res)
         | 
| 32 | 
            -
                      res['content-type']  | 
| 57 | 
            +
                      res['content-type'] ||=
         | 
| 33 58 | 
             
                        HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
         | 
| 34 59 | 
             
                    rescue StandardError => ex
         | 
| 35 60 | 
             
                      raise
         | 
| @@ -39,13 +64,21 @@ module WEBrick | |
| 39 64 | 
             
                    end
         | 
| 40 65 | 
             
                  end
         | 
| 41 66 |  | 
| 67 | 
            +
                  ##
         | 
| 68 | 
            +
                  # Handles POST requests
         | 
| 69 | 
            +
             | 
| 42 70 | 
             
                  alias do_POST do_GET
         | 
| 43 71 |  | 
| 44 72 | 
             
                  private
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  ##
         | 
| 75 | 
            +
                  # Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as
         | 
| 76 | 
            +
                  # local variables.
         | 
| 77 | 
            +
             | 
| 45 78 | 
             
                  def evaluate(erb, servlet_request, servlet_response)
         | 
| 46 79 | 
             
                    Module.new.module_eval{
         | 
| 47 | 
            -
                       | 
| 48 | 
            -
                       | 
| 80 | 
            +
                      servlet_request.meta_vars
         | 
| 81 | 
            +
                      servlet_request.query
         | 
| 49 82 | 
             
                      erb.result(binding)
         | 
| 50 83 | 
             
                    }
         | 
| 51 84 | 
             
                  end
         | 
| @@ -18,12 +18,29 @@ require 'webrick/httpstatus' | |
| 18 18 | 
             
            module WEBrick
         | 
| 19 19 | 
             
              module HTTPServlet
         | 
| 20 20 |  | 
| 21 | 
            +
                ##
         | 
| 22 | 
            +
                # Servlet for serving a single file.  You probably want to use the
         | 
| 23 | 
            +
                # FileHandler servlet instead as it handles directories and fancy indexes.
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # Example:
         | 
| 26 | 
            +
                #
         | 
| 27 | 
            +
                #   server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
         | 
| 28 | 
            +
                #                '/path/to/my_page.txt')
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                # This servlet handles If-Modified-Since and Range requests.
         | 
| 31 | 
            +
             | 
| 21 32 | 
             
                class DefaultFileHandler < AbstractServlet
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  ##
         | 
| 35 | 
            +
                  # Creates a DefaultFileHandler instance for the file at +local_path+.
         | 
| 36 | 
            +
             | 
| 22 37 | 
             
                  def initialize(server, local_path)
         | 
| 23 | 
            -
                    super
         | 
| 38 | 
            +
                    super(server, local_path)
         | 
| 24 39 | 
             
                    @local_path = local_path
         | 
| 25 40 | 
             
                  end
         | 
| 26 41 |  | 
| 42 | 
            +
                  # :stopdoc:
         | 
| 43 | 
            +
             | 
| 27 44 | 
             
                  def do_GET(req, res)
         | 
| 28 45 | 
             
                    st = File::stat(@local_path)
         | 
| 29 46 | 
             
                    mtime = st.mtime
         | 
| @@ -32,7 +49,7 @@ module WEBrick | |
| 32 49 | 
             
                    if not_modified?(req, res, mtime, res['etag'])
         | 
| 33 50 | 
             
                      res.body = ''
         | 
| 34 51 | 
             
                      raise HTTPStatus::NotModified
         | 
| 35 | 
            -
                    elsif req['range'] | 
| 52 | 
            +
                    elsif req['range']
         | 
| 36 53 | 
             
                      make_partial_content(req, res, @local_path, st.size)
         | 
| 37 54 | 
             
                      raise HTTPStatus::PartialContent
         | 
| 38 55 | 
             
                    else
         | 
| @@ -87,7 +104,7 @@ module WEBrick | |
| 87 104 | 
             
                          content = io.read(last-first+1)
         | 
| 88 105 | 
             
                          body << "--" << boundary << CRLF
         | 
| 89 106 | 
             
                          body << "Content-Type: #{mtype}" << CRLF
         | 
| 90 | 
            -
                          body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF
         | 
| 107 | 
            +
                          body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
         | 
| 91 108 | 
             
                          body << CRLF
         | 
| 92 109 | 
             
                          body << content
         | 
| 93 110 | 
             
                          body << CRLF
         | 
| @@ -107,7 +124,7 @@ module WEBrick | |
| 107 124 | 
             
                          content = io.read(last-first+1)
         | 
| 108 125 | 
             
                        end
         | 
| 109 126 | 
             
                        res['content-type'] = mtype
         | 
| 110 | 
            -
                        res['content-range'] = "#{first}-#{last}/#{filesize}"
         | 
| 127 | 
            +
                        res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
         | 
| 111 128 | 
             
                        res['content-length'] = last - first + 1
         | 
| 112 129 | 
             
                        res.body = content
         | 
| 113 130 | 
             
                      else
         | 
| @@ -123,19 +140,46 @@ module WEBrick | |
| 123 140 | 
             
                    last = filesize - 1 if last >= filesize
         | 
| 124 141 | 
             
                    return first, last
         | 
| 125 142 | 
             
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  # :startdoc:
         | 
| 126 145 | 
             
                end
         | 
| 127 146 |  | 
| 147 | 
            +
                ##
         | 
| 148 | 
            +
                # Serves a directory including fancy indexing and a variety of other
         | 
| 149 | 
            +
                # options.
         | 
| 150 | 
            +
                #
         | 
| 151 | 
            +
                # Example:
         | 
| 152 | 
            +
                #
         | 
| 153 | 
            +
                #   server.mount '/assets', WEBrick::FileHandler, '/path/to/assets'
         | 
| 154 | 
            +
             | 
| 128 155 | 
             
                class FileHandler < AbstractServlet
         | 
| 129 | 
            -
                  HandlerTable = Hash.new
         | 
| 156 | 
            +
                  HandlerTable = Hash.new # :nodoc:
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  ##
         | 
| 159 | 
            +
                  # Allow custom handling of requests for files with +suffix+ by class
         | 
| 160 | 
            +
                  # +handler+
         | 
| 130 161 |  | 
| 131 162 | 
             
                  def self.add_handler(suffix, handler)
         | 
| 132 163 | 
             
                    HandlerTable[suffix] = handler
         | 
| 133 164 | 
             
                  end
         | 
| 134 165 |  | 
| 166 | 
            +
                  ##
         | 
| 167 | 
            +
                  # Remove custom handling of requests for files with +suffix+
         | 
| 168 | 
            +
             | 
| 135 169 | 
             
                  def self.remove_handler(suffix)
         | 
| 136 170 | 
             
                    HandlerTable.delete(suffix)
         | 
| 137 171 | 
             
                  end
         | 
| 138 172 |  | 
| 173 | 
            +
                  ##
         | 
| 174 | 
            +
                  # Creates a FileHandler servlet on +server+ that serves files starting
         | 
| 175 | 
            +
                  # at directory +root+
         | 
| 176 | 
            +
                  #
         | 
| 177 | 
            +
                  # +options+ may be a Hash containing keys from
         | 
| 178 | 
            +
                  # WEBrick::Config::FileHandler or +true+ or +false+.
         | 
| 179 | 
            +
                  #
         | 
| 180 | 
            +
                  # If +options+ is true or false then +:FancyIndexing+ is enabled or
         | 
| 181 | 
            +
                  # disabled respectively.
         | 
| 182 | 
            +
             | 
| 139 183 | 
             
                  def initialize(server, root, options={}, default=Config::FileHandler)
         | 
| 140 184 | 
             
                    @config = server.config
         | 
| 141 185 | 
             
                    @logger = @config[:Logger]
         | 
| @@ -146,6 +190,8 @@ module WEBrick | |
| 146 190 | 
             
                    @options = default.dup.update(options)
         | 
| 147 191 | 
             
                  end
         | 
| 148 192 |  | 
| 193 | 
            +
                  # :stopdoc:
         | 
| 194 | 
            +
             | 
| 149 195 | 
             
                  def service(req, res)
         | 
| 150 196 | 
             
                    # if this class is mounted on "/" and /~username is requested.
         | 
| 151 197 | 
             
                    # we're going to override path informations before invoking service.
         | 
| @@ -163,6 +209,7 @@ module WEBrick | |
| 163 209 | 
             
                        end
         | 
| 164 210 | 
             
                      end
         | 
| 165 211 | 
             
                    end
         | 
| 212 | 
            +
                    prevent_directory_traversal(req, res)
         | 
| 166 213 | 
             
                    super(req, res)
         | 
| 167 214 | 
             
                  end
         | 
| 168 215 |  | 
| @@ -198,10 +245,42 @@ module WEBrick | |
| 198 245 |  | 
| 199 246 | 
             
                  private
         | 
| 200 247 |  | 
| 248 | 
            +
                  def trailing_pathsep?(path)
         | 
| 249 | 
            +
                    # check for trailing path separator:
         | 
| 250 | 
            +
                    #   File.dirname("/aaaa/bbbb/")      #=> "/aaaa")
         | 
| 251 | 
            +
                    #   File.dirname("/aaaa/bbbb/x")     #=> "/aaaa/bbbb")
         | 
| 252 | 
            +
                    #   File.dirname("/aaaa/bbbb")       #=> "/aaaa")
         | 
| 253 | 
            +
                    #   File.dirname("/aaaa/bbbbx")      #=> "/aaaa")
         | 
| 254 | 
            +
                    return File.dirname(path) != File.dirname(path+"x")
         | 
| 255 | 
            +
                  end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                  def prevent_directory_traversal(req, res)
         | 
| 258 | 
            +
                    # Preventing directory traversal on Windows platforms;
         | 
| 259 | 
            +
                    # Backslashes (0x5c) in path_info are not interpreted as special
         | 
| 260 | 
            +
                    # character in URI notation. So the value of path_info should be
         | 
| 261 | 
            +
                    # normalize before accessing to the filesystem.
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                    # dirty hack for filesystem encoding; in nature, File.expand_path
         | 
| 264 | 
            +
                    # should not be used for path normalization.  [Bug #3345]
         | 
| 265 | 
            +
                    path = req.path_info.dup.force_encoding(Encoding.find("filesystem"))
         | 
| 266 | 
            +
                    if trailing_pathsep?(req.path_info)
         | 
| 267 | 
            +
                      # File.expand_path removes the trailing path separator.
         | 
| 268 | 
            +
                      # Adding a character is a workaround to save it.
         | 
| 269 | 
            +
                      #  File.expand_path("/aaa/")        #=> "/aaa"
         | 
| 270 | 
            +
                      #  File.expand_path("/aaa/" + "x")  #=> "/aaa/x"
         | 
| 271 | 
            +
                      expanded = File.expand_path(path + "x")
         | 
| 272 | 
            +
                      expanded.chop!  # remove trailing "x"
         | 
| 273 | 
            +
                    else
         | 
| 274 | 
            +
                      expanded = File.expand_path(path)
         | 
| 275 | 
            +
                    end
         | 
| 276 | 
            +
                    expanded.force_encoding(req.path_info.encoding)
         | 
| 277 | 
            +
                    req.path_info = expanded
         | 
| 278 | 
            +
                  end
         | 
| 279 | 
            +
             | 
| 201 280 | 
             
                  def exec_handler(req, res)
         | 
| 202 281 | 
             
                    raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
         | 
| 203 282 | 
             
                    if set_filename(req, res)
         | 
| 204 | 
            -
                      handler = get_handler(req)
         | 
| 283 | 
            +
                      handler = get_handler(req, res)
         | 
| 205 284 | 
             
                      call_callback(:HandlerCallback, req, res)
         | 
| 206 285 | 
             
                      h = handler.get_instance(@config, res.filename)
         | 
| 207 286 | 
             
                      h.service(req, res)
         | 
| @@ -211,9 +290,13 @@ module WEBrick | |
| 211 290 | 
             
                    return false
         | 
| 212 291 | 
             
                  end
         | 
| 213 292 |  | 
| 214 | 
            -
                  def get_handler(req)
         | 
| 215 | 
            -
                    suffix1 = (/\.(\w+) | 
| 216 | 
            -
                     | 
| 293 | 
            +
                  def get_handler(req, res)
         | 
| 294 | 
            +
                    suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
         | 
| 295 | 
            +
                    if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
         | 
| 296 | 
            +
                      if @options[:AcceptableLanguages].include?($2.downcase)
         | 
| 297 | 
            +
                        suffix2 = $1.downcase
         | 
| 298 | 
            +
                      end
         | 
| 299 | 
            +
                    end
         | 
| 217 300 | 
             
                    handler_table = @options[:HandlerTable]
         | 
| 218 301 | 
             
                    return handler_table[suffix1] || handler_table[suffix2] ||
         | 
| 219 302 | 
             
                           HandlerTable[suffix1] || HandlerTable[suffix2] ||
         | 
| @@ -226,15 +309,13 @@ module WEBrick | |
| 226 309 |  | 
| 227 310 | 
             
                    path_info.unshift("")  # dummy for checking @root dir
         | 
| 228 311 | 
             
                    while base = path_info.first
         | 
| 229 | 
            -
                      check_filename(req, res, base)
         | 
| 230 312 | 
             
                      break if base == "/"
         | 
| 231 | 
            -
                      break unless File.directory?(res.filename + base)
         | 
| 313 | 
            +
                      break unless File.directory?(File.expand_path(res.filename + base))
         | 
| 232 314 | 
             
                      shift_path_info(req, res, path_info)
         | 
| 233 315 | 
             
                      call_callback(:DirectoryCallback, req, res)
         | 
| 234 316 | 
             
                    end
         | 
| 235 317 |  | 
| 236 318 | 
             
                    if base = path_info.first
         | 
| 237 | 
            -
                      check_filename(req, res, base)
         | 
| 238 319 | 
             
                      if base == "/"
         | 
| 239 320 | 
             
                        if file = search_index_file(req, res)
         | 
| 240 321 | 
             
                          shift_path_info(req, res, path_info, file)
         | 
| @@ -255,12 +336,10 @@ module WEBrick | |
| 255 336 | 
             
                  end
         | 
| 256 337 |  | 
| 257 338 | 
             
                  def check_filename(req, res, name)
         | 
| 258 | 
            -
                     | 
| 259 | 
            -
                       | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
                      end
         | 
| 263 | 
            -
                    }
         | 
| 339 | 
            +
                    if nondisclosure_name?(name) || windows_ambiguous_name?(name)
         | 
| 340 | 
            +
                      @logger.warn("the request refers nondisclosure name `#{name}'.")
         | 
| 341 | 
            +
                      raise HTTPStatus::NotFound, "`#{req.path}' not found."
         | 
| 342 | 
            +
                    end
         | 
| 264 343 | 
             
                  end
         | 
| 265 344 |  | 
| 266 345 | 
             
                  def shift_path_info(req, res, path_info, base=nil)
         | 
| @@ -268,7 +347,8 @@ module WEBrick | |
| 268 347 | 
             
                    base = base || tmp
         | 
| 269 348 | 
             
                    req.path_info = path_info.join
         | 
| 270 349 | 
             
                    req.script_name << base
         | 
| 271 | 
            -
                    res.filename  | 
| 350 | 
            +
                    res.filename = File.expand_path(res.filename + base)
         | 
| 351 | 
            +
                    check_filename(req, res, File.basename(res.filename))
         | 
| 272 352 | 
             
                  end
         | 
| 273 353 |  | 
| 274 354 | 
             
                  def search_index_file(req, res)
         | 
| @@ -308,9 +388,15 @@ module WEBrick | |
| 308 388 | 
             
                    end
         | 
| 309 389 | 
             
                  end
         | 
| 310 390 |  | 
| 391 | 
            +
                  def windows_ambiguous_name?(name)
         | 
| 392 | 
            +
                    return true if /[. ]+\z/ =~ name
         | 
| 393 | 
            +
                    return true if /::\$DATA\z/ =~ name
         | 
| 394 | 
            +
                    return false
         | 
| 395 | 
            +
                  end
         | 
| 396 | 
            +
             | 
| 311 397 | 
             
                  def nondisclosure_name?(name)
         | 
| 312 398 | 
             
                    @options[:NondisclosureName].each{|pattern|
         | 
| 313 | 
            -
                      if File.fnmatch(pattern, name)
         | 
| 399 | 
            +
                      if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
         | 
| 314 400 | 
             
                        return true
         | 
| 315 401 | 
             
                      end
         | 
| 316 402 | 
             
                    }
         | 
| @@ -326,7 +412,8 @@ module WEBrick | |
| 326 412 | 
             
                    list = Dir::entries(local_path).collect{|name|
         | 
| 327 413 | 
             
                      next if name == "." || name == ".."
         | 
| 328 414 | 
             
                      next if nondisclosure_name?(name)
         | 
| 329 | 
            -
                       | 
| 415 | 
            +
                      next if windows_ambiguous_name?(name)
         | 
| 416 | 
            +
                      st = (File::stat(File.join(local_path, name)) rescue nil)
         | 
| 330 417 | 
             
                      if st.nil?
         | 
| 331 418 | 
             
                        [ name, nil, -1 ]
         | 
| 332 419 | 
             
                      elsif st.directory?
         | 
| @@ -365,25 +452,25 @@ module WEBrick | |
| 365 452 | 
             
                    res.body << "<A HREF=\"?M=#{d1}\">Last modified</A>         "
         | 
| 366 453 | 
             
                    res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
         | 
| 367 454 | 
             
                    res.body << "<HR>\n"
         | 
| 368 | 
            -
             | 
| 369 | 
            -
                    list.unshift [ "..", File::mtime(local_path+" | 
| 455 | 
            +
             | 
| 456 | 
            +
                    list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
         | 
| 370 457 | 
             
                    list.each{ |name, time, size|
         | 
| 371 458 | 
             
                      if name == ".."
         | 
| 372 459 | 
             
                        dname = "Parent Directory"
         | 
| 373 | 
            -
                      elsif name. | 
| 374 | 
            -
                        dname = name.sub(/^(.{23})( | 
| 460 | 
            +
                      elsif name.bytesize > 25
         | 
| 461 | 
            +
                        dname = name.sub(/^(.{23})(?:.*)/, '\1..')
         | 
| 375 462 | 
             
                      else
         | 
| 376 463 | 
             
                        dname = name
         | 
| 377 464 | 
             
                      end
         | 
| 378 | 
            -
                      s =  " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
         | 
| 379 | 
            -
                      s << " " * (30 - dname. | 
| 465 | 
            +
                      s =  " <A HREF=\"#{HTTPUtils::escape(name)}\">#{HTMLUtils::escape(dname)}</A>"
         | 
| 466 | 
            +
                      s << " " * (30 - dname.bytesize)
         | 
| 380 467 | 
             
                      s << (time ? time.strftime("%Y/%m/%d %H:%M      ") : " " * 22)
         | 
| 381 468 | 
             
                      s << (size >= 0 ? size.to_s : "-") << "\n"
         | 
| 382 469 | 
             
                      res.body << s
         | 
| 383 470 | 
             
                    }
         | 
| 384 471 | 
             
                    res.body << "</PRE><HR>"
         | 
| 385 472 |  | 
| 386 | 
            -
                    res.body << <<-_end_of_html_ | 
| 473 | 
            +
                    res.body << <<-_end_of_html_
         | 
| 387 474 | 
             
                <ADDRESS>
         | 
| 388 475 | 
             
                 #{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
         | 
| 389 476 | 
             
                 at #{req.host}:#{req.port}
         | 
| @@ -393,6 +480,7 @@ module WEBrick | |
| 393 480 | 
             
                    _end_of_html_
         | 
| 394 481 | 
             
                  end
         | 
| 395 482 |  | 
| 483 | 
            +
                  # :startdoc:
         | 
| 396 484 | 
             
                end
         | 
| 397 485 | 
             
              end
         | 
| 398 486 | 
             
            end
         |