diff options
Diffstat (limited to 'lib/webrick/httpservlet')
| -rw-r--r-- | lib/webrick/httpservlet/abstract.rb | 92 | ||||
| -rw-r--r-- | lib/webrick/httpservlet/cgi_runner.rb | 5 | ||||
| -rw-r--r-- | lib/webrick/httpservlet/cgihandler.rb | 36 | ||||
| -rw-r--r-- | lib/webrick/httpservlet/erbhandler.rb | 47 | ||||
| -rw-r--r-- | lib/webrick/httpservlet/filehandler.rb | 59 | ||||
| -rw-r--r-- | lib/webrick/httpservlet/prochandler.rb | 8 |
6 files changed, 198 insertions, 49 deletions
diff --git a/lib/webrick/httpservlet/abstract.rb b/lib/webrick/httpservlet/abstract.rb index 5375c4622d..0d5c5ae48d 100644 --- a/lib/webrick/httpservlet/abstract.rb +++ b/lib/webrick/httpservlet/abstract.rb @@ -18,17 +18,88 @@ 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 Configureable < 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 - def self.get_instance(config, *options) - self.new(config, *options) + + ## + # 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) @@ -39,27 +110,38 @@ module WEBrick 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(/^do_[A-Z]+$/) - m.collect!{|i| i.sub(/do_/, "") } + 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.size > 0 + if req.query_string && req.query_string.bytesize > 0 location << "?" << req.query_string end res.set_redirect(HTTPStatus::MovedPermanently, location) diff --git a/lib/webrick/httpservlet/cgi_runner.rb b/lib/webrick/httpservlet/cgi_runner.rb index 006abd458e..32ecb6fe00 100644 --- a/lib/webrick/httpservlet/cgi_runner.rb +++ b/lib/webrick/httpservlet/cgi_runner.rb @@ -13,14 +13,13 @@ def sysread(io, size) while size > 0 tmp = io.sysread(size) buf << tmp - size -= tmp.size + size -= tmp.bytesize end return buf end STDIN.binmode -buf = "" len = sysread(STDIN, 8).to_i out = sysread(STDIN, len) STDOUT.reopen(open(out, "w")) @@ -38,7 +37,7 @@ hash.each{|k, v| ENV[k] = v if v } dir = File::dirname(ENV["SCRIPT_FILENAME"]) Dir::chdir dir -if interpreter = ARGV[0] +if ARGV[0] argv = ARGV.dup argv << ENV["SCRIPT_FILENAME"] exec(*argv) diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb index d5895e5285..1976ae6948 100644 --- a/lib/webrick/httpservlet/cgihandler.rb +++ b/lib/webrick/httpservlet/cgihandler.rb @@ -1,11 +1,11 @@ -# +# # 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' @@ -17,13 +17,11 @@ module WEBrick module HTTPServlet class CGIHandler < AbstractServlet - Ruby = File::join(::Config::CONFIG['bindir'], - ::Config::CONFIG['ruby_install_name']) - Ruby << ::Config::CONFIG['EXEEXT'] - CGIRunner = "\"#{Ruby}\" \"#{Config::LIBDIR}/httpservlet/cgi_runner.rb\"" + Ruby = RbConfig.ruby + CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" def initialize(server, name) - super + super(server, name) @script_filename = name @tempdir = server[:TempDir] @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}" @@ -34,8 +32,10 @@ module WEBrick status = -1 cgi_in = IO::popen(@cgicmd, "wb") - cgi_out = Tempfile.new("webrick.cgiout.", @tempdir) - cgi_err = Tempfile.new("webrick.cgierr.", @tempdir) + 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 @@ -46,14 +46,14 @@ module WEBrick end dump = Marshal.dump(meta) - cgi_in.write("%8d" % cgi_out.path.size) + cgi_in.write("%8d" % cgi_out.path.bytesize) cgi_in.write(cgi_out.path) - cgi_in.write("%8d" % cgi_err.path.size) + cgi_in.write("%8d" % cgi_err.path.bytesize) cgi_in.write(cgi_err.path) - cgi_in.write("%8d" % dump.size) + cgi_in.write("%8d" % dump.bytesize) cgi_in.write(dump) - if req.body and req.body.size > 0 + if req.body and req.body.bytesize > 0 cgi_in.write(req.body) end ensure @@ -63,19 +63,19 @@ module WEBrick data = cgi_out.read cgi_out.close(true) if errmsg = cgi_err.read - if errmsg.size > 0 + if errmsg.bytesize > 0 @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg) end - 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]+/on, 2) + raw_header, body = data.split(/^[\xd\xa]+/, 2) raise HTTPStatus::InternalServerError, "Premature end of script headers: #{@script_filename}" if body.nil? diff --git a/lib/webrick/httpservlet/erbhandler.rb b/lib/webrick/httpservlet/erbhandler.rb index b9d5e65b65..34b4b9e68b 100644 --- a/lib/webrick/httpservlet/erbhandler.rb +++ b/lib/webrick/httpservlet/erbhandler.rb @@ -1,11 +1,11 @@ -# +# # 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' @@ -15,12 +15,37 @@ 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 + 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." @@ -29,7 +54,7 @@ module WEBrick begin data = open(@script_filename){|io| io.read } res.body = evaluate(ERB.new(data), req, res) - res['content-type'] = + res['content-type'] ||= HTTPUtils::mime_type(@script_filename, @config[:MimeTypes]) rescue StandardError => ex raise @@ -39,13 +64,21 @@ module WEBrick 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{ - meta_vars = servlet_request.meta_vars - query = servlet_request.query + servlet_request.meta_vars + servlet_request.query erb.result(binding) } end diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb index 24f59d7142..8736f5773a 100644 --- a/lib/webrick/httpservlet/filehandler.rb +++ b/lib/webrick/httpservlet/filehandler.rb @@ -20,7 +20,7 @@ module WEBrick class DefaultFileHandler < AbstractServlet def initialize(server, local_path) - super + super(server, local_path) @local_path = local_path end @@ -32,7 +32,7 @@ module WEBrick if not_modified?(req, res, mtime, res['etag']) res.body = '' raise HTTPStatus::NotModified - elsif req['range'] + elsif req['range'] make_partial_content(req, res, @local_path, st.size) raise HTTPStatus::PartialContent else @@ -87,7 +87,7 @@ module WEBrick content = io.read(last-first+1) body << "--" << boundary << CRLF body << "Content-Type: #{mtype}" << CRLF - body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF + body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF body << CRLF body << content body << CRLF @@ -107,7 +107,7 @@ module WEBrick content = io.read(last-first+1) end res['content-type'] = mtype - res['content-range'] = "#{first}-#{last}/#{filesize}" + res['content-range'] = "bytes #{first}-#{last}/#{filesize}" res['content-length'] = last - first + 1 res.body = content else @@ -125,17 +125,48 @@ module WEBrick end end + ## + # Serves files from a directory + class FileHandler < AbstractServlet HandlerTable = Hash.new + ## + # 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+ + # + # 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. + def initialize(server, root, options={}, default=Config::FileHandler) @config = server.config @logger = @config[:Logger] @@ -214,16 +245,20 @@ module WEBrick # 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(req.path_info + "x") + expanded = File.expand_path(path + "x") expanded.chop! # remove trailing "x" else - expanded = File.expand_path(req.path_info) + expanded = File.expand_path(path) end + expanded.force_encoding(req.path_info.encoding) req.path_info = expanded end @@ -402,25 +437,25 @@ module WEBrick 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.size > 25 - dname = name.sub(/^(.{23})(.*)/){ $1 + ".." } + elsif name.bytesize > 25 + dname = name.sub(/^(.{23})(?:.*)/, '\1..') else dname = name end - s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>" - s << " " * (30 - dname.size) + 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_ + res.body << <<-_end_of_html_ <ADDRESS> #{HTMLUtils::escape(@config[:ServerSoftware])}<BR> at #{req.host}:#{req.port} diff --git a/lib/webrick/httpservlet/prochandler.rb b/lib/webrick/httpservlet/prochandler.rb index 783cb27896..2be3c854c1 100644 --- a/lib/webrick/httpservlet/prochandler.rb +++ b/lib/webrick/httpservlet/prochandler.rb @@ -1,11 +1,11 @@ -# +# # 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' @@ -16,7 +16,7 @@ module WEBrick class ProcHandler < AbstractServlet def get_instance(server, *options) self - end + end def initialize(proc) @proc = proc |
