1# 2# server.rb -- GenericServer Class 3# 4# Author: IPR -- Internet Programming with Ruby -- writers 5# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou 6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights 7# reserved. 8# 9# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $ 10 11require 'thread' 12require 'socket' 13require 'webrick/config' 14require 'webrick/log' 15 16module WEBrick 17 18 ## 19 # Server error exception 20 21 class ServerError < StandardError; end 22 23 ## 24 # Base server class 25 26 class SimpleServer 27 28 ## 29 # A SimpleServer only yields when you start it 30 31 def SimpleServer.start 32 yield 33 end 34 end 35 36 ## 37 # A generic module for daemonizing a process 38 39 class Daemon 40 41 ## 42 # Performs the standard operations for daemonizing a process. Runs a 43 # block, if given. 44 45 def Daemon.start 46 exit!(0) if fork 47 Process::setsid 48 exit!(0) if fork 49 Dir::chdir("/") 50 File::umask(0) 51 STDIN.reopen("/dev/null") 52 STDOUT.reopen("/dev/null", "w") 53 STDERR.reopen("/dev/null", "w") 54 yield if block_given? 55 end 56 end 57 58 ## 59 # Base TCP server class. You must subclass GenericServer and provide a #run 60 # method. 61 62 class GenericServer 63 64 ## 65 # The server status. One of :Stop, :Running or :Shutdown 66 67 attr_reader :status 68 69 ## 70 # The server configuration 71 72 attr_reader :config 73 74 ## 75 # The server logger. This is independent from the HTTP access log. 76 77 attr_reader :logger 78 79 ## 80 # Tokens control the number of outstanding clients. The 81 # <code>:MaxClients</code> configuration sets this. 82 83 attr_reader :tokens 84 85 ## 86 # Sockets listening for connections. 87 88 attr_reader :listeners 89 90 ## 91 # Creates a new generic server from +config+. The default configuration 92 # comes from +default+. 93 94 def initialize(config={}, default=Config::General) 95 @config = default.dup.update(config) 96 @status = :Stop 97 @config[:Logger] ||= Log::new 98 @logger = @config[:Logger] 99 100 @tokens = SizedQueue.new(@config[:MaxClients]) 101 @config[:MaxClients].times{ @tokens.push(nil) } 102 103 webrickv = WEBrick::VERSION 104 rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" 105 @logger.info("WEBrick #{webrickv}") 106 @logger.info("ruby #{rubyv}") 107 108 @listeners = [] 109 unless @config[:DoNotListen] 110 if @config[:Listen] 111 warn(":Listen option is deprecated; use GenericServer#listen") 112 end 113 listen(@config[:BindAddress], @config[:Port]) 114 if @config[:Port] == 0 115 @config[:Port] = @listeners[0].addr[1] 116 end 117 end 118 end 119 120 ## 121 # Retrieves +key+ from the configuration 122 123 def [](key) 124 @config[key] 125 end 126 127 ## 128 # Adds listeners from +address+ and +port+ to the server. See 129 # WEBrick::Utils::create_listeners for details. 130 131 def listen(address, port) 132 @listeners += Utils::create_listeners(address, port, @logger) 133 end 134 135 ## 136 # Starts the server and runs the +block+ for each connection. This method 137 # does not return until the server is stopped from a signal handler or 138 # another thread using #stop or #shutdown. 139 # 140 # If the block raises a subclass of StandardError the exception is logged 141 # and ignored. If an IOError or Errno::EBADF exception is raised the 142 # exception is ignored. If an Exception subclass is raised the exception 143 # is logged and re-raised which stops the server. 144 # 145 # To completely shut down a server call #shutdown from ensure: 146 # 147 # server = WEBrick::GenericServer.new 148 # # or WEBrick::HTTPServer.new 149 # 150 # begin 151 # server.start 152 # ensure 153 # server.shutdown 154 # end 155 156 def start(&block) 157 raise ServerError, "already started." if @status != :Stop 158 server_type = @config[:ServerType] || SimpleServer 159 160 server_type.start{ 161 @logger.info \ 162 "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}" 163 call_callback(:StartCallback) 164 165 thgroup = ThreadGroup.new 166 @status = :Running 167 begin 168 while @status == :Running 169 begin 170 if svrs = IO.select(@listeners, nil, nil, 2.0) 171 svrs[0].each{|svr| 172 @tokens.pop # blocks while no token is there. 173 if sock = accept_client(svr) 174 sock.do_not_reverse_lookup = config[:DoNotReverseLookup] 175 th = start_thread(sock, &block) 176 th[:WEBrickThread] = true 177 thgroup.add(th) 178 else 179 @tokens.push(nil) 180 end 181 } 182 end 183 rescue Errno::EBADF, IOError => ex 184 # if the listening socket was closed in GenericServer#shutdown, 185 # IO::select raise it. 186 rescue StandardError => ex 187 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" 188 @logger.error msg 189 rescue Exception => ex 190 @logger.fatal ex 191 raise 192 end 193 end 194 195 ensure 196 @status = :Shutdown 197 @logger.info "going to shutdown ..." 198 thgroup.list.each{|th| th.join if th[:WEBrickThread] } 199 call_callback(:StopCallback) 200 @logger.info "#{self.class}#start done." 201 @status = :Stop 202 end 203 } 204 end 205 206 ## 207 # Stops the server from accepting new connections. 208 209 def stop 210 if @status == :Running 211 @status = :Shutdown 212 end 213 end 214 215 ## 216 # Shuts down the server and all listening sockets. New listeners must be 217 # provided to restart the server. 218 219 def shutdown 220 stop 221 @listeners.each{|s| 222 if @logger.debug? 223 addr = s.addr 224 @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})") 225 end 226 begin 227 s.shutdown 228 rescue Errno::ENOTCONN 229 # when `Errno::ENOTCONN: Socket is not connected' on some platforms, 230 # call #close instead of #shutdown. 231 # (ignore @config[:ShutdownSocketWithoutClose]) 232 s.close 233 else 234 unless @config[:ShutdownSocketWithoutClose] 235 s.close 236 end 237 end 238 } 239 @listeners.clear 240 end 241 242 ## 243 # You must subclass GenericServer and implement \#run which accepts a TCP 244 # client socket 245 246 def run(sock) 247 @logger.fatal "run() must be provided by user." 248 end 249 250 private 251 252 # :stopdoc: 253 254 ## 255 # Accepts a TCP client socket from the TCP server socket +svr+ and returns 256 # the client socket. 257 258 def accept_client(svr) 259 sock = nil 260 begin 261 sock = svr.accept 262 sock.sync = true 263 Utils::set_non_blocking(sock) 264 Utils::set_close_on_exec(sock) 265 rescue Errno::ECONNRESET, Errno::ECONNABORTED, 266 Errno::EPROTO, Errno::EINVAL => ex 267 rescue StandardError => ex 268 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" 269 @logger.error msg 270 end 271 return sock 272 end 273 274 ## 275 # Starts a server thread for the client socket +sock+ that runs the given 276 # +block+. 277 # 278 # Sets the socket to the <code>:WEBrickSocket</code> thread local variable 279 # in the thread. 280 # 281 # If any errors occur in the block they are logged and handled. 282 283 def start_thread(sock, &block) 284 Thread.start{ 285 begin 286 Thread.current[:WEBrickSocket] = sock 287 begin 288 addr = sock.peeraddr 289 @logger.debug "accept: #{addr[3]}:#{addr[1]}" 290 rescue SocketError 291 @logger.debug "accept: <address unknown>" 292 raise 293 end 294 call_callback(:AcceptCallback, sock) 295 block ? block.call(sock) : run(sock) 296 rescue Errno::ENOTCONN 297 @logger.debug "Errno::ENOTCONN raised" 298 rescue ServerError => ex 299 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}" 300 @logger.error msg 301 rescue Exception => ex 302 @logger.error ex 303 ensure 304 @tokens.push(nil) 305 Thread.current[:WEBrickSocket] = nil 306 if addr 307 @logger.debug "close: #{addr[3]}:#{addr[1]}" 308 else 309 @logger.debug "close: <address unknown>" 310 end 311 sock.close unless sock.closed? 312 end 313 } 314 end 315 316 ## 317 # Calls the callback +callback_name+ from the configuration with +args+ 318 319 def call_callback(callback_name, *args) 320 if cb = @config[callback_name] 321 cb.call(*args) 322 end 323 end 324 end # end of GenericServer 325end 326