/usr/lib/ruby/1.8/gserver.rb is in libruby1.8 1.8.7.352-2ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | #
# Copyright (C) 2001 John W. Small All Rights Reserved
#
# Author:: John W. Small
# Documentation:: Gavin Sinclair
# Licence:: Freeware.
#
# See the class GServer for documentation.
#
require "socket"
require "thread"
#
# GServer implements a generic server, featuring thread pool management,
# simple logging, and multi-server management. See HttpServer in
# <tt>xmlrpc/httpserver.rb</tt> in the Ruby standard library for an example of
# GServer in action.
#
# Any kind of application-level server can be implemented using this class.
# It accepts multiple simultaneous connections from clients, up to an optional
# maximum number. Several _services_ (i.e. one service per TCP port) can be
# run simultaneously, and stopped at any time through the class method
# <tt>GServer.stop(port)</tt>. All the threading issues are handled, saving
# you the effort. All events are optionally logged, but you can provide your
# own event handlers if you wish.
#
# === 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+:
#
# require 'gserver'
#
# #
# # A server that returns the time in seconds since 1970.
# #
# class TimeServer < GServer
# def initialize(port=10001, *args)
# super(port, *args)
# end
# def serve(io)
# io.puts(Time.now.to_i)
# end
# end
#
# # Run the server with logging enabled (it's a separate thread).
# server = TimeServer.new
# server.audit = true # Turn logging on.
# server.start
#
# # *** Now point your browser to http://localhost:10001 to see it working ***
#
# # See if it's still running.
# GServer.in_service?(10001) # -> true
# server.stopped? # -> false
#
# # Shut the server down gracefully.
# server.shutdown
#
# # Alternatively, stop it immediately.
# GServer.stop(10001)
# # or, of course, "server.stop".
#
# All the business of accepting connections and exception handling is taken
# care of. All we have to do is implement the method that actually serves the
# client.
#
# === Advanced
#
# As the example above shows, the way to use GServer is to subclass it to
# create a specific server, overriding the +serve+ method. You can override
# other methods as well if you wish, perhaps to collect statistics, or emit
# more detailed logging.
#
# connecting
# disconnecting
# starting
# stopping
#
# The above methods are only called if auditing is enabled.
#
# You can also override +log+ and +error+ if, for example, you wish to use a
# more sophisticated logging system.
#
class GServer
DEFAULT_HOST = "127.0.0.1"
def serve(io)
end
@@services = {} # Hash of opened ports, i.e. services
@@servicesMutex = Mutex.new
def GServer.stop(port, host = DEFAULT_HOST)
@@servicesMutex.synchronize {
@@services[host][port].stop
}
end
def GServer.in_service?(port, host = DEFAULT_HOST)
@@services.has_key?(host) and
@@services[host].has_key?(port)
end
def stop
@connectionsMutex.synchronize {
if @tcpServerThread
@tcpServerThread.raise "stop"
end
}
end
def stopped?
@tcpServerThread == nil
end
def shutdown
@shutdown = true
end
def connections
@connections.size
end
def join
@tcpServerThread.join if @tcpServerThread
end
attr_reader :port, :host, :maxConnections
attr_accessor :stdlog, :audit, :debug
def connecting(client)
addr = client.peeraddr
log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " +
"#{addr[2]}<#{addr[3]}> connect")
true
end
def disconnecting(clientPort)
log("#{self.class.to_s} #{@host}:#{@port} " +
"client:#{clientPort} disconnect")
end
protected :connecting, :disconnecting
def starting()
log("#{self.class.to_s} #{@host}:#{@port} start")
end
def stopping()
log("#{self.class.to_s} #{@host}:#{@port} stop")
end
protected :starting, :stopping
def error(detail)
log(detail.backtrace.join("\n"))
end
def log(msg)
if @stdlog
@stdlog.puts("[#{Time.new.ctime}] %s" % msg)
@stdlog.flush
end
end
protected :error, :log
def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
stdlog = $stderr, audit = false, debug = false)
@tcpServerThread = nil
@port = port
@host = host
@maxConnections = maxConnections
@connections = []
@connectionsMutex = Mutex.new
@connectionsCV = ConditionVariable.new
@stdlog = stdlog
@audit = audit
@debug = debug
end
def start(maxConnections = -1)
raise "running" if !stopped?
@shutdown = false
@maxConnections = maxConnections if maxConnections > 0
@@servicesMutex.synchronize {
if GServer.in_service?(@port,@host)
raise "Port already in use: #{host}:#{@port}!"
end
@tcpServer = TCPServer.new(@host,@port)
@port = @tcpServer.addr[1]
@@services[@host] = {} unless @@services.has_key?(@host)
@@services[@host][@port] = self;
}
@tcpServerThread = Thread.new {
begin
starting if @audit
while !@shutdown
@connectionsMutex.synchronize {
while @connections.size >= @maxConnections
@connectionsCV.wait(@connectionsMutex)
end
}
client = @tcpServer.accept
@connections << Thread.new(client) { |myClient|
begin
myPort = myClient.peeraddr[1]
serve(myClient) if !@audit or connecting(myClient)
rescue => detail
error(detail) if @debug
ensure
begin
myClient.close
rescue
end
@connectionsMutex.synchronize {
@connections.delete(Thread.current)
@connectionsCV.signal
}
disconnecting(myPort) if @audit
end
}
end
rescue => detail
error(detail) if @debug
ensure
begin
@tcpServer.close
rescue
end
if @shutdown
@connectionsMutex.synchronize {
while @connections.size > 0
@connectionsCV.wait(@connectionsMutex)
end
}
else
@connections.each { |c| c.raise "stop" }
end
@tcpServerThread = nil
@@servicesMutex.synchronize {
@@services[@host].delete(@port)
}
stopping if @audit
end
}
self
end
end
|