summaryrefslogtreecommitdiff
path: root/lib/webrick/httpservlet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/webrick/httpservlet')
-rw-r--r--lib/webrick/httpservlet/abstract.rb92
-rw-r--r--lib/webrick/httpservlet/cgi_runner.rb5
-rw-r--r--lib/webrick/httpservlet/cgihandler.rb36
-rw-r--r--lib/webrick/httpservlet/erbhandler.rb47
-rw-r--r--lib/webrick/httpservlet/filehandler.rb59
-rw-r--r--lib/webrick/httpservlet/prochandler.rb8
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