An HTTP Server
Creates a new HTTP server according to config
An HTTP server uses the following attributes:
An array of access logs. See WEBrick::AccessLog
Local address for the server to bind to
Root path to serve files from
Options for the default HTTPServlet::FileHandler
The HTTP version of this server
Port to listen on
Called with a request and response before each request is serviced.
Maximum time to wait between requests
Array of alternate names for this server for virtual hosting
Name for this server for virtual hosting
# File webrick/httpserver.rb, line 46
def initialize(config={}, default=Config::HTTP)
super(config, default)
@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] = [
[ $stderr, AccessLog::COMMON_LOG_FORMAT ],
[ $stderr, AccessLog::REFERER_LOG_FORMAT ]
]
end
@virtual_hosts = Array.new
end
Logs req and res in the access logs.
config is used for the server name.
# File webrick/httpserver.rb, line 220
def access_log(config, req, res)
param = AccessLog::setup_params(config, req, res)
@config[:AccessLog].each{|logger, fmt|
logger << AccessLog::format(fmt+"\n", param)
}
end
Creates the HTTPRequest used when handling the HTTP request. Can be overridden by subclasses.
# File webrick/httpserver.rb, line 230
def create_request(with_webrick_config)
HTTPRequest.new(with_webrick_config)
end
Creates the HTTPResponse used when handling the HTTP request. Can be overridden by subclasses.
# File webrick/httpserver.rb, line 237
def create_response(with_webrick_config)
HTTPResponse.new(with_webrick_config)
end
The default OPTIONS request handler says GET, HEAD, POST and OPTIONS requests are allowed.
# File webrick/httpserver.rb, line 147
def do_OPTIONS(req, res)
res["allow"] = "GET,HEAD,POST,OPTIONS"
end
Finds the appropriate virtual host to handle req
# File webrick/httpserver.rb, line 207
def lookup_server(req)
@virtual_hosts.find{|s|
(s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
(s[:Port].nil? || req.port == s[:Port]) &&
((s[:ServerName].nil? || req.host == s[:ServerName]) ||
(!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
}
end
Mounts servlet on dir passing
options to the servlet at creation time
# File webrick/httpserver.rb, line 155
def mount(dir, servlet, *options)
@logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
@mount_tab[dir] = [ servlet, options ]
end
Mounts proc or block on dir and
calls it with a WEBrick::HTTPRequest and WEBrick::HTTPResponse
# File webrick/httpserver.rb, line 164
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
Processes requests on sock
# File webrick/httpserver.rb, line 69
def run(sock)
while true
req = create_request(@config)
res = create_response(@config)
server = self
begin
timeout = @config[:RequestTimeout]
while timeout > 0
break if sock.to_io.wait_readable(0.5)
break if @status != :Running
timeout -= 0.5
end
raise HTTPStatus::EOFError if timeout <= 0 || @status != :Running
raise HTTPStatus::EOFError if sock.eof?
req.parse(sock)
res.request_method = req.request_method
res.request_uri = req.request_uri
res.request_http_version = req.http_version
res.keep_alive = req.keep_alive?
server = lookup_server(req) || self
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)
end
server.service(req, res)
rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
res.set_error(ex)
rescue HTTPStatus::Error => ex
@logger.error(ex.message)
res.set_error(ex)
rescue HTTPStatus::Status => ex
res.status = ex.code
rescue StandardError => ex
@logger.error(ex)
res.set_error(ex, true)
ensure
if req.request_line
if req.keep_alive? && res.keep_alive?
req.fixup()
end
res.send_response(sock)
server.access_log(@config, req, res)
end
end
break if @http_version < "1.1"
break unless req.keep_alive?
break unless res.keep_alive?
end
end
Finds a servlet for path
# File webrick/httpserver.rb, line 182
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
Services req and fills in res
# File webrick/httpserver.rb, line 125
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
ServerNameIndication callback
# File webrick/https.rb, line 129
def ssl_servername_callback(sslsocket, hostname = nil)
req = SNIRequest.new(sslsocket, hostname)
server = lookup_server(req)
server ? server.ssl_context : nil
end
Unmounts dir
# File webrick/httpserver.rb, line 173
def unmount(dir)
@logger.debug(sprintf("unmount %s.", dir))
@mount_tab.delete(dir)
end
Adds server as a virtual host.
# File webrick/httpserver.rb, line 193
def virtual_host(server)
@virtual_hosts << server
@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
}
end