* lib/webrick/server.rb (module WEBrick::GenericServer): A server
will now continue only when a StandardError subclass is raised. For other exception types the error will be logged at the fatal level and the server will safely stop. Based on a patch by Alex Young. [ruby-trunk - Feature #6236] * test/webrick/test_server.rb: Test for new exception handling behavior. Join the server thread instead of busy-waiting for it to shut down to remove race conditions. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35303 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
2fe9d4642e
commit
8c5c5a221f
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Thu Apr 12 05:27:01 2012 Eric Hodel <drbrain@segment7.net>
|
||||||
|
|
||||||
|
* lib/webrick/server.rb (module WEBrick::GenericServer): A server
|
||||||
|
will now continue only when a StandardError subclass is raised. For
|
||||||
|
other exception types the error will be logged at the fatal level and
|
||||||
|
the server will safely stop. Based on a patch by Alex Young.
|
||||||
|
[ruby-trunk - Feature #6236]
|
||||||
|
* test/webrick/test_server.rb: Test for new exception handling
|
||||||
|
behavior. Join the server thread instead of busy-waiting for it to
|
||||||
|
shut down to remove race conditions.
|
||||||
|
|
||||||
Thu Apr 12 03:50:44 2012 NARUSE, Yui <naruse@ruby-lang.org>
|
Thu Apr 12 03:50:44 2012 NARUSE, Yui <naruse@ruby-lang.org>
|
||||||
|
|
||||||
* lib/test/unit.rb (Test::Unit:Runner::Worker#_run_suites):
|
* lib/test/unit.rb (Test::Unit:Runner::Worker#_run_suites):
|
||||||
|
@ -82,6 +82,27 @@ module WEBrick
|
|||||||
@listeners += Utils::create_listeners(address, port, @logger)
|
@listeners += Utils::create_listeners(address, port, @logger)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Starts the server and runs the +block+ for each connection. This method
|
||||||
|
# does not return until the server is stopped from a signal handler or
|
||||||
|
# another thread using #stop or #shutdown.
|
||||||
|
#
|
||||||
|
# If the block raises a subclass of StandardError the exception is logged
|
||||||
|
# and ignored. If an IOError or Errno::EBADF exception is raised the
|
||||||
|
# exception is ignored. If an Exception subclass is raised the exception
|
||||||
|
# is logged and re-raised which stops the server.
|
||||||
|
#
|
||||||
|
# To completely shut down a server call #shutdown from ensure:
|
||||||
|
#
|
||||||
|
# server = WEBrick::GenericServer.new
|
||||||
|
# # or WEBrick::HTTPServer.new
|
||||||
|
#
|
||||||
|
# begin
|
||||||
|
# server.start
|
||||||
|
# ensure
|
||||||
|
# server.shutdown
|
||||||
|
# end
|
||||||
|
|
||||||
def start(&block)
|
def start(&block)
|
||||||
raise ServerError, "already started." if @status != :Stop
|
raise ServerError, "already started." if @status != :Stop
|
||||||
server_type = @config[:ServerType] || SimpleServer
|
server_type = @config[:ServerType] || SimpleServer
|
||||||
@ -93,44 +114,57 @@ module WEBrick
|
|||||||
|
|
||||||
thgroup = ThreadGroup.new
|
thgroup = ThreadGroup.new
|
||||||
@status = :Running
|
@status = :Running
|
||||||
while @status == :Running
|
begin
|
||||||
begin
|
while @status == :Running
|
||||||
if svrs = IO.select(@listeners, nil, nil, 2.0)
|
begin
|
||||||
svrs[0].each{|svr|
|
if svrs = IO.select(@listeners, nil, nil, 2.0)
|
||||||
@tokens.pop # blocks while no token is there.
|
svrs[0].each{|svr|
|
||||||
if sock = accept_client(svr)
|
@tokens.pop # blocks while no token is there.
|
||||||
sock.do_not_reverse_lookup = config[:DoNotReverseLookup]
|
if sock = accept_client(svr)
|
||||||
th = start_thread(sock, &block)
|
sock.do_not_reverse_lookup = config[:DoNotReverseLookup]
|
||||||
th[:WEBrickThread] = true
|
th = start_thread(sock, &block)
|
||||||
thgroup.add(th)
|
th[:WEBrickThread] = true
|
||||||
else
|
thgroup.add(th)
|
||||||
@tokens.push(nil)
|
else
|
||||||
end
|
@tokens.push(nil)
|
||||||
}
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
rescue Errno::EBADF, IOError => ex
|
||||||
|
# if the listening socket was closed in GenericServer#shutdown,
|
||||||
|
# IO::select raise it.
|
||||||
|
rescue StandardError => ex
|
||||||
|
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
||||||
|
@logger.error msg
|
||||||
|
rescue Exception => ex
|
||||||
|
@logger.fatal ex
|
||||||
|
raise
|
||||||
end
|
end
|
||||||
rescue Errno::EBADF, IOError => ex
|
|
||||||
# if the listening socket was closed in GenericServer#shutdown,
|
|
||||||
# IO::select raise it.
|
|
||||||
rescue Exception => ex
|
|
||||||
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
|
||||||
@logger.error msg
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
@logger.info "going to shutdown ..."
|
ensure
|
||||||
thgroup.list.each{|th| th.join if th[:WEBrickThread] }
|
@logger.info "going to shutdown ..."
|
||||||
call_callback(:StopCallback)
|
thgroup.list.each{|th| th.join if th[:WEBrickThread] }
|
||||||
@logger.info "#{self.class}#start done."
|
call_callback(:StopCallback)
|
||||||
@status = :Stop
|
@logger.info "#{self.class}#start done."
|
||||||
|
@status = :Stop
|
||||||
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Stops the server from accepting new connections.
|
||||||
|
|
||||||
def stop
|
def stop
|
||||||
if @status == :Running
|
if @status == :Running
|
||||||
@status = :Shutdown
|
@status = :Stop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Shuts down the server and all listening sockets. New listeners must be
|
||||||
|
# provided to restart the server.
|
||||||
|
|
||||||
def shutdown
|
def shutdown
|
||||||
stop
|
stop
|
||||||
@listeners.each{|s|
|
@listeners.each{|s|
|
||||||
@ -169,7 +203,7 @@ module WEBrick
|
|||||||
Utils::set_close_on_exec(sock)
|
Utils::set_close_on_exec(sock)
|
||||||
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
|
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
|
||||||
Errno::EPROTO, Errno::EINVAL => ex
|
Errno::EPROTO, Errno::EINVAL => ex
|
||||||
rescue Exception => ex
|
rescue StandardError => ex
|
||||||
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
||||||
@logger.error msg
|
@logger.error msg
|
||||||
end
|
end
|
||||||
|
@ -23,6 +23,32 @@ class TestWEBrickServer < Test::Unit::TestCase
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_start_exception
|
||||||
|
stopped = 0
|
||||||
|
config = {
|
||||||
|
:StopCallback => Proc.new{ stopped += 1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
e = assert_raises(Exception) do
|
||||||
|
TestWEBrick.start_server(Echo, config) { |server, addr, port, log|
|
||||||
|
listener = server.listeners.first
|
||||||
|
|
||||||
|
def listener.accept
|
||||||
|
raise Exception, 'fatal' # simulate ^C
|
||||||
|
end
|
||||||
|
|
||||||
|
true while server.status != :Running
|
||||||
|
|
||||||
|
TCPSocket.open(addr, port) { |sock| sock << "foo\n" }
|
||||||
|
|
||||||
|
sleep 0.1 until server.status == :Stop
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal('fatal', e.message)
|
||||||
|
assert_equal(stopped, 1)
|
||||||
|
end
|
||||||
|
|
||||||
def test_callbacks
|
def test_callbacks
|
||||||
accepted = started = stopped = 0
|
accepted = started = stopped = 0
|
||||||
config = {
|
config = {
|
||||||
|
@ -36,14 +36,13 @@ module TestWEBrick
|
|||||||
:AccessLog => [[logger, ""]]
|
:AccessLog => [[logger, ""]]
|
||||||
}.update(config))
|
}.update(config))
|
||||||
begin
|
begin
|
||||||
server.start
|
server_thread = server.start
|
||||||
addr = server.listeners[0].addr
|
addr = server.listeners[0].addr
|
||||||
block.yield([server, addr[3], addr[1], log])
|
block.yield([server, addr[3], addr[1], log])
|
||||||
ensure
|
ensure
|
||||||
server.shutdown
|
server.shutdown
|
||||||
until server.status == :Stop
|
|
||||||
sleep 0.1
|
server_thread.join
|
||||||
end
|
|
||||||
end
|
end
|
||||||
log_string
|
log_string
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user