Bug #4705 ยป 0003-clean-up-and-add-docs-for-GServer.patch
lib/gserver.rb | ||
---|---|---|
# Documentation:: Gavin Sinclair
|
||
# Licence:: Freeware.
|
||
#
|
||
# See the class GServer for documentation.
|
||
# A generic server, featuring thread pool management,
|
||
# simple logging, and multi-server management.
|
||
# See GServer for more documentation.
|
||
#
|
||
require "socket"
|
||
... | ... | |
# you the effort. All events are optionally logged, but you can provide your
|
||
# own event handlers if you wish.
|
||
#
|
||
# === Example
|
||
# == Example
|
||
#
|
||
# Using GServer is simple. Below we implement a simple time server, run it,
|
||
# query it, and shut it down. Try this code in +irb+:
|
||
... | ... | |
# other methods as well if you wish, perhaps to collect statistics, or emit
|
||
# more detailed logging.
|
||
#
|
||
# connecting
|
||
# disconnecting
|
||
# starting
|
||
# stopping
|
||
# * #connecting
|
||
# * #disconnecting
|
||
# * #starting
|
||
# * #stopping
|
||
#
|
||
# The above methods are only called if auditing is enabled.
|
||
# The above methods are only called if auditing is enabled, via #audit=.
|
||
#
|
||
# You can also override +log+ and +error+ if, for example, you wish to use a
|
||
# You can also override #log and #error if, for example, you wish to use a
|
||
# more sophisticated logging system.
|
||
#
|
||
class GServer
|
||
... | ... | |
@@services = {} # Hash of opened ports, i.e. services
|
||
@@servicesMutex = Mutex.new
|
||
# Stop the server running on the given port, bound to the given host
|
||
#
|
||
# +port+:: port, as a FixNum, of the server to stop
|
||
# +host+:: host on which to find the server to stop
|
||
def GServer.stop(port, host = DEFAULT_HOST)
|
||
@@servicesMutex.synchronize {
|
||
@@services[host][port].stop
|
||
}
|
||
end
|
||
# Check if a server is running on the given port and host
|
||
#
|
||
# +port+:: port, as a FixNum, of the server to check
|
||
# +host+:: host on which to find the server to check
|
||
#
|
||
# Returns true if a server is running on that port and host.
|
||
def GServer.in_service?(port, host = DEFAULT_HOST)
|
||
@@services.has_key?(host) and
|
||
@@services[host].has_key?(port)
|
||
end
|
||
# Stop the server
|
||
def stop
|
||
@connectionsMutex.synchronize {
|
||
if @tcpServerThread
|
||
... | ... | |
}
|
||
end
|
||
# Returns true if the server has stopped.
|
||
def stopped?
|
||
@tcpServerThread == nil
|
||
end
|
||
# Schedule a shutdown for the server
|
||
def shutdown
|
||
@shutdown = true
|
||
end
|
||
# Return the current number of connected clients
|
||
def connections
|
||
@connections.size
|
||
end
|
||
# Join with the server thread
|
||
def join
|
||
@tcpServerThread.join if @tcpServerThread
|
||
end
|
||
attr_reader :port, :host, :maxConnections
|
||
attr_accessor :stdlog, :audit, :debug
|
||
# Port on which to listen, as a FixNum
|
||
attr_reader :port
|
||
# Host on which to bind, as a String
|
||
attr_reader :host
|
||
# Maximum number of connections to accept at at ime, as a FixNum
|
||
attr_reader :maxConnections
|
||
# IO Device on which log messages should be written
|
||
attr_accessor :stdlog
|
||
# Set to true to cause the callbacks #connecting, #disconnecting, #starting, and #stopping to
|
||
# be called during the server's lifecycle
|
||
attr_accessor :audit
|
||
# Set to true to show more detailed logging
|
||
attr_accessor :debug
|
||
# Called when a client connects, if auditing is enabled.
|
||
#
|
||
# +client+:: a TCPSocket instances representing the client that connected
|
||
#
|
||
# Return true to allow this client to connect, false to prevent it.
|
||
def connecting(client)
|
||
addr = client.peeraddr
|
||
log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " +
|
||
... | ... | |
true
|
||
end
|
||
# Called when a client disconnects, if audition is enabled.
|
||
#
|
||
# +clientPort+:: the port of the client that is connecting
|
||
def disconnecting(clientPort)
|
||
log("#{self.class.to_s} #{@host}:#{@port} " +
|
||
"client:#{clientPort} disconnect")
|
||
... | ... | |
protected :connecting, :disconnecting
|
||
# Called when the server is starting up, if auditing is enabled.
|
||
def starting()
|
||
log("#{self.class.to_s} #{@host}:#{@port} start")
|
||
end
|
||
# Called when the server is shutting down, if auditing is enabled.
|
||
def stopping()
|
||
log("#{self.class.to_s} #{@host}:#{@port} stop")
|
||
end
|
||
protected :starting, :stopping
|
||
# Called if #debug is true whenever an unhandled exception is raised.
|
||
# This implementation simply logs the backtrace.
|
||
#
|
||
# +detail+:: The Exception that was caught
|
||
def error(detail)
|
||
log(detail.backtrace.join("\n"))
|
||
end
|
||
# Log a message to #stdlog, if it's defined. This implementation
|
||
# outputs the timestamp and message to the log.
|
||
#
|
||
# +msg+:: the message to log
|
||
def log(msg)
|
||
if @stdlog
|
||
@stdlog.puts("[#{Time.new.ctime}] %s" % msg)
|
||
... | ... | |
protected :error, :log
|
||
# Create a new server
|
||
#
|
||
# +port+:: the port, as a FixNum, on which to listen.
|
||
# +host+:: the host to bind to
|
||
# +maxConnections+:: The maximum number of simultaneous
|
||
# connections to accept
|
||
# +stdlog+:: IO device on which to log messages
|
||
# +audit+:: if true, lifecycle callbacks will be called. See #audit
|
||
# +debug+:: if true, error messages are logged. See #debug
|
||
def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
|
||
stdlog = $stderr, audit = false, debug = false)
|
||
@tcpServerThread = nil
|
||
... | ... | |
@debug = debug
|
||
end
|
||
# Start the server if it isn't already running
|
||
#
|
||
# +maxConnections+:: override +maxConnections+ given to the constructor. A negative
|
||
# value indicates that the value from the constructor should be used.
|
||
def start(maxConnections = -1)
|
||
raise "running" if !stopped?
|
||
raise "server is already running" if !stopped?
|
||
@shutdown = false
|
||
@maxConnections = maxConnections if maxConnections > 0
|
||
@@servicesMutex.synchronize {
|