2003-07-23 12:51:36 -04:00
|
|
|
#
|
|
|
|
# httpserver.rb -- HTTPServer Class
|
|
|
|
#
|
|
|
|
# Author: IPR -- Internet Programming with Ruby -- writers
|
|
|
|
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
|
|
|
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
|
|
|
|
# reserved.
|
|
|
|
#
|
|
|
|
# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
|
|
|
|
|
|
|
|
require 'webrick/server'
|
|
|
|
require 'webrick/httputils'
|
|
|
|
require 'webrick/httpstatus'
|
|
|
|
require 'webrick/httprequest'
|
|
|
|
require 'webrick/httpresponse'
|
|
|
|
require 'webrick/httpservlet'
|
|
|
|
require 'webrick/accesslog'
|
|
|
|
|
|
|
|
module WEBrick
|
|
|
|
class HTTPServerError < ServerError; end
|
|
|
|
|
|
|
|
class HTTPServer < ::WEBrick::GenericServer
|
|
|
|
def initialize(config={}, default=Config::HTTP)
|
|
|
|
super
|
|
|
|
@http_version = HTTPVersion::convert(@config[:HTTPVersion])
|
|
|
|
|
|
|
|
@mount_tab = MountTable.new
|
|
|
|
if @config[:DocumentRoot]
|
|
|
|
mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot],
|
|
|
|
@config[:DocumentRootOptions])
|
|
|
|
end
|
|
|
|
|
|
|
|
unless @config[:AccessLog]
|
|
|
|
@config[:AccessLog] = [
|
2003-09-08 05:52:34 -04:00
|
|
|
[ $stderr, AccessLog::COMMON_LOG_FORMAT ],
|
|
|
|
[ $stderr, AccessLog::REFERER_LOG_FORMAT ]
|
2003-07-23 12:51:36 -04:00
|
|
|
]
|
|
|
|
end
|
2004-01-24 11:48:52 -05:00
|
|
|
|
|
|
|
@virtual_hosts = Array.new
|
2003-07-23 12:51:36 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def run(sock)
|
|
|
|
while true
|
|
|
|
res = HTTPResponse.new(@config)
|
|
|
|
req = HTTPRequest.new(@config)
|
2004-01-24 11:48:52 -05:00
|
|
|
server = self
|
2003-07-23 12:51:36 -04:00
|
|
|
begin
|
|
|
|
req.parse(sock)
|
|
|
|
res.request_method = req.request_method
|
|
|
|
res.request_uri = req.request_uri
|
|
|
|
res.request_http_version = req.http_version
|
2003-11-25 11:02:45 -05:00
|
|
|
res.keep_alive = req.keep_alive?
|
2004-01-24 11:48:52 -05:00
|
|
|
server = lookup_server(req) || self
|
2004-03-07 11:06:43 -05:00
|
|
|
if callback = server[:RequestCallback]
|
|
|
|
callback.call(req, res)
|
|
|
|
elsif callback = server[:RequestHandler]
|
|
|
|
msg = ":RequestHandler is deprecated, please use :RequestCallback"
|
|
|
|
@logger.warn(msg)
|
|
|
|
callback.call(req, res)
|
2003-07-23 12:51:36 -04:00
|
|
|
end
|
2004-01-24 11:48:52 -05:00
|
|
|
server.service(req, res)
|
2003-07-23 12:51:36 -04:00
|
|
|
rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
|
|
|
|
res.set_error(ex)
|
|
|
|
rescue HTTPStatus::Error => ex
|
2004-03-07 11:06:43 -05:00
|
|
|
@logger.error(ex.message)
|
2003-07-23 12:51:36 -04:00
|
|
|
res.set_error(ex)
|
|
|
|
rescue HTTPStatus::Status => ex
|
|
|
|
res.status = ex.code
|
2003-08-19 02:00:36 -04:00
|
|
|
rescue StandardError => ex
|
2003-07-23 12:51:36 -04:00
|
|
|
@logger.error(ex)
|
|
|
|
res.set_error(ex, true)
|
|
|
|
ensure
|
|
|
|
if req.request_line
|
|
|
|
req.fixup()
|
|
|
|
res.send_response(sock)
|
2004-01-24 11:48:52 -05:00
|
|
|
server.access_log(@config, req, res)
|
2003-07-23 12:51:36 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
break if @http_version < "1.1"
|
|
|
|
break unless req.keep_alive?
|
|
|
|
break unless res.keep_alive?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def service(req, res)
|
|
|
|
if req.unparsed_uri == "*"
|
|
|
|
if req.request_method == "OPTIONS"
|
|
|
|
do_OPTIONS(req, res)
|
|
|
|
raise HTTPStatus::OK
|
|
|
|
end
|
|
|
|
raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
|
|
|
|
end
|
|
|
|
|
|
|
|
servlet, options, script_name, path_info = search_servlet(req.path)
|
|
|
|
raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
|
|
|
|
req.script_name = script_name
|
|
|
|
req.path_info = path_info
|
|
|
|
si = servlet.get_instance(self, *options)
|
|
|
|
@logger.debug(format("%s is invoked.", si.class.name))
|
|
|
|
si.service(req, res)
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_OPTIONS(req, res)
|
|
|
|
res["allow"] = "GET,HEAD,POST,OPTIONS"
|
|
|
|
end
|
|
|
|
|
|
|
|
def mount(dir, servlet, *options)
|
|
|
|
@logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
|
|
|
|
@mount_tab[dir] = [ servlet, options ]
|
|
|
|
end
|
|
|
|
|
|
|
|
def mount_proc(dir, proc=nil, &block)
|
|
|
|
proc ||= block
|
|
|
|
raise HTTPServerError, "must pass a proc or block" unless proc
|
|
|
|
mount(dir, HTTPServlet::ProcHandler.new(proc))
|
|
|
|
end
|
|
|
|
|
|
|
|
def unmount(dir)
|
|
|
|
@logger.debug(sprintf("unmount %s.", inspect, dir))
|
|
|
|
@mount_tab.delete(dir)
|
|
|
|
end
|
|
|
|
alias umount unmount
|
|
|
|
|
|
|
|
def search_servlet(path)
|
|
|
|
script_name, path_info = @mount_tab.scan(path)
|
|
|
|
servlet, options = @mount_tab[script_name]
|
|
|
|
if servlet
|
|
|
|
[ servlet, options, script_name, path_info ]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2004-01-24 11:48:52 -05:00
|
|
|
def virtual_host(server)
|
|
|
|
@virtual_hosts << server
|
2004-03-21 08:17:24 -05:00
|
|
|
@virtual_hosts = @virtual_hosts.sort_by{|s|
|
|
|
|
num = 0
|
|
|
|
num -= 4 if s[:BindAddress]
|
|
|
|
num -= 2 if s[:Port]
|
|
|
|
num -= 1 if s[:ServerName]
|
|
|
|
num
|
|
|
|
}
|
2004-01-24 11:48:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def lookup_server(req)
|
2004-03-07 11:06:43 -05:00
|
|
|
@virtual_hosts.find{|s|
|
|
|
|
(s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
|
2004-03-21 08:17:24 -05:00
|
|
|
(s[:Port].nil? || req.port == s[:Port]) &&
|
2004-03-07 11:06:43 -05:00
|
|
|
((s[:ServerName].nil? || req.host == s[:ServerName]) ||
|
2004-03-21 08:17:24 -05:00
|
|
|
(!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
|
2004-01-24 11:48:52 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2003-07-23 12:51:36 -04:00
|
|
|
def access_log(config, req, res)
|
|
|
|
param = AccessLog::setup_params(config, req, res)
|
|
|
|
@config[:AccessLog].each{|logger, fmt|
|
2003-09-08 12:57:25 -04:00
|
|
|
logger << AccessLog::format(fmt+"\n", param)
|
2003-07-23 12:51:36 -04:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
class MountTable
|
|
|
|
def initialize
|
|
|
|
@tab = Hash.new
|
|
|
|
compile
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](dir)
|
|
|
|
dir = normalize(dir)
|
|
|
|
@tab[dir]
|
|
|
|
end
|
|
|
|
|
|
|
|
def []=(dir, val)
|
|
|
|
dir = normalize(dir)
|
|
|
|
@tab[dir] = val
|
|
|
|
compile
|
|
|
|
val
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete(dir)
|
|
|
|
dir = normalize(dir)
|
|
|
|
res = @tab.delete(dir)
|
|
|
|
compile
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
def scan(path)
|
|
|
|
@scanner =~ path
|
|
|
|
[ $&, $' ]
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def compile
|
|
|
|
k = @tab.keys
|
|
|
|
k.sort!
|
|
|
|
k.reverse!
|
|
|
|
k.collect!{|path| Regexp.escape(path) }
|
|
|
|
@scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)")
|
|
|
|
end
|
|
|
|
|
|
|
|
def normalize(dir)
|
|
|
|
ret = dir ? dir.dup : ""
|
|
|
|
ret.sub!(%r|/+$|, "")
|
|
|
|
ret
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|