diff options
Diffstat (limited to 'ruby_1_8_5/lib/webrick/httpservlet')
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/abstract.rb | 71 | ||||
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb | 45 | ||||
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb | 104 | ||||
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb | 54 | ||||
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb | 398 | ||||
-rw-r--r-- | ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb | 33 |
6 files changed, 705 insertions, 0 deletions
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb b/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb new file mode 100644 index 0000000000..03861e8fc7 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb @@ -0,0 +1,71 @@ +# +# 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 + + class AbstractServlet + def self.get_instance(config, *options) + self.new(config, *options) + end + + def initialize(server, *options) + @server = @config = server + @logger = @server[:Logger] + @options = options + end + + 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 + + def do_GET(req, res) + raise HTTPStatus::NotFound, "not found." + end + + def do_HEAD(req, res) + do_GET(req, res) + end + + def do_OPTIONS(req, res) + m = self.methods.grep(/^do_[A-Z]+$/) + m.collect!{|i| i.sub(/do_/, "") } + m.sort! + res["allow"] = m.join(",") + end + + private + + def redirect_to_directory_uri(req, res) + if req.path[-1] != ?/ + location = req.path + "/" + if req.query_string && req.query_string.size > 0 + location << "?" << req.query_string + end + res.set_redirect(HTTPStatus::MovedPermanently, location) + end + end + end + + end +end diff --git a/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb b/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb new file mode 100644 index 0000000000..1069a68d58 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb @@ -0,0 +1,45 @@ +# +# 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.size + end + return buf +end + +STDIN.binmode + +buf = "" +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 interpreter = ARGV[0] + exec(interpreter, ENV["SCRIPT_FILENAME"]) + # NOTREACHED +end +exec ENV["SCRIPT_FILENAME"] diff --git a/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb new file mode 100644 index 0000000000..a35b59edb8 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb @@ -0,0 +1,104 @@ +# +# 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 + + 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\"" + + def initialize(server, name) + super + @script_filename = name + @tempdir = server[:TempDir] + @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}" + end + + def do_GET(req, res) + data = nil + status = -1 + + cgi_in = IO::popen(@cgicmd, "wb") + cgi_out = Tempfile.new("webrick.cgiout.", @tempdir) + cgi_err = Tempfile.new("webrick.cgierr.", @tempdir) + 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.size) + cgi_in.write(cgi_out.path) + cgi_in.write("%8d" % cgi_err.path.size) + cgi_in.write(cgi_err.path) + cgi_in.write("%8d" % dump.size) + cgi_in.write(dump) + + if req.body and req.body.size > 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.size > 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]+/on, 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?('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 + end + + end +end diff --git a/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb new file mode 100644 index 0000000000..b9d5e65b65 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb @@ -0,0 +1,54 @@ +# +# 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 + + class ERBHandler < AbstractServlet + def initialize(server, name) + super + @script_filename = name + end + + 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 => ex + raise + rescue Exception => ex + @logger.error(ex) + raise HTTPStatus::InternalServerError, ex.message + end + end + + alias do_POST do_GET + + private + def evaluate(erb, servlet_request, servlet_response) + Module.new.module_eval{ + meta_vars = servlet_request.meta_vars + query = servlet_request.query + erb.result(binding) + } + end + end + end +end diff --git a/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb new file mode 100644 index 0000000000..410cc6f9a9 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb @@ -0,0 +1,398 @@ +# +# 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 + + class DefaultFileHandler < AbstractServlet + def initialize(server, local_path) + super + @local_path = local_path + end + + 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: #{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'] = "#{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 + end + + class FileHandler < AbstractServlet + HandlerTable = Hash.new + + def self.add_handler(suffix, handler) + HandlerTable[suffix] = handler + end + + def self.remove_handler(suffix) + HandlerTable.delete(suffix) + end + + 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 + + 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 + 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 exec_handler(req, res) + raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root + if set_filename(req, res) + handler = get_handler(req) + 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) + suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase + suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase + handler_table = @options[:HandlerTable] + return handler_table[suffix1] || handler_table[suffix2] || + HandlerTable[suffix1] || HandlerTable[suffix2] || + DefaultFileHandler + end + + def set_filename(req, res) + res.filename = @root.dup + path_info = req.path_info.scan(%r|/[^/]*|) + + path_info.unshift("") # dummy for checking @root dir + while base = path_info.first + check_filename(req, res, base) + break if base == "/" + break unless File.directory?(res.filename + base) + shift_path_info(req, res, path_info) + call_callback(:DirectoryCallback, req, res) + end + + if base = path_info.first + check_filename(req, res, base) + if base == "/" + if file = search_index_file(req, res) + shift_path_info(req, res, path_info, file) + call_callback(:FileCallback, req, res) + return true + end + shift_path_info(req, res, path_info) + elsif file = search_file(req, res, base) + shift_path_info(req, res, path_info, file) + call_callback(:FileCallback, req, res) + return true + else + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + + return false + end + + def check_filename(req, res, name) + @options[:NondisclosureName].each{|pattern| + if File.fnmatch("/#{pattern}", name) + @logger.warn("the request refers nondisclosure name `#{name}'.") + raise HTTPStatus::NotFound, "`#{req.path}' not found." + end + } + end + + def shift_path_info(req, res, path_info, base=nil) + tmp = path_info.shift + base = base || tmp + req.path_info = path_info.join + req.script_name << base + res.filename << base + end + + def search_index_file(req, res) + @config[:DirectoryIndex].each{|index| + if file = search_file(req, res, "/"+index) + return file + end + } + return nil + end + + def search_file(req, res, basename) + langs = @options[:AcceptableLanguages] + path = res.filename + basename + if File.file?(path) + return basename + elsif langs.size > 0 + req.accept_language.each{|lang| + path_with_lang = path + ".#{lang}" + if langs.member?(lang) && File.file?(path_with_lang) + return basename + ".#{lang}" + end + } + (langs - req.accept_language).each{|lang| + path_with_lang = path + ".#{lang}" + if File.file?(path_with_lang) + return basename + ".#{lang}" + end + } + end + return nil + end + + def call_callback(callback_name, req, res) + if cb = @options[callback_name] + cb.call(req, res) + end + end + + def nondisclosure_name?(name) + @options[:NondisclosureName].each{|pattern| + if File.fnmatch(pattern, name) + 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) + st = (File::stat(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! + + if d0 = req.query["N"]; idx = 0 + elsif d0 = req.query["M"]; idx = 1 + elsif d0 = req.query["S"]; idx = 2 + else d0 = "A" ; idx = 0 + end + 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 + + res['content-type'] = "text/html" + + res.body = <<-_end_of_html_ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD> + <BODY> + <H1>Index of #{HTMLUtils::escape(req.path)}</H1> + _end_of_html_ + + res.body << "<PRE>\n" + res.body << " <A HREF=\"?N=#{d1}\">Name</A> " + 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 + ".." } + else + dname = name + end + s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>" + s << " " * (30 - dname.size) + 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_ + <ADDRESS> + #{HTMLUtils::escape(@config[:ServerSoftware])}<BR> + at #{req.host}:#{req.port} + </ADDRESS> + </BODY> +</HTML> + _end_of_html_ + end + + end + end +end diff --git a/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb new file mode 100644 index 0000000000..783cb27896 --- /dev/null +++ b/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb @@ -0,0 +1,33 @@ +# +# 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 + + class ProcHandler < AbstractServlet + 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 + end + + end +end |