2 # server.rb -- GenericServer Class
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
9 # $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
13 require 'webrick/config'
18 class ServerError < StandardError; end
21 def SimpleServer.start
33 STDIN.reopen("/dev/null")
34 STDOUT.reopen("/dev/null", "w")
35 STDERR.reopen("/dev/null", "w")
41 attr_reader :status, :config, :logger, :tokens, :listeners
43 def initialize(config={}, default=Config::General)
44 @config = default.dup.update(config)
46 @config[:Logger] ||= Log::new
47 @logger = @config[:Logger]
49 @tokens = SizedQueue.new(@config[:MaxClients])
50 @config[:MaxClients].times{ @tokens.push(nil) }
52 webrickv = WEBrick::VERSION
53 rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
54 @logger.info("WEBrick #{webrickv}")
55 @logger.info("ruby #{rubyv}")
58 unless @config[:DoNotListen]
60 warn(":Listen option is deprecated; use GenericServer#listen")
62 listen(@config[:BindAddress], @config[:Port])
63 if @config[:Port] == 0
64 @config[:Port] = @listeners[0].addr[1]
73 def listen(address, port)
74 @listeners += Utils::create_listeners(address, port, @logger)
78 raise ServerError, "already started." if @status != :Stop
79 server_type = @config[:ServerType] || SimpleServer
83 "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
84 call_callback(:StartCallback)
86 thgroup = ThreadGroup.new
88 while @status == :Running
90 if svrs = IO.select(@listeners, nil, nil, 2.0)
92 @tokens.pop # blocks while no token is there.
93 if sock = accept_client(svr)
94 sock.do_not_reverse_lookup = config[:DoNotReverseLookup]
95 th = start_thread(sock, &block)
96 th[:WEBrickThread] = true
103 rescue Errno::EBADF, IOError => ex
104 # if the listening socket was closed in GenericServer#shutdown,
105 # IO::select raise it.
106 rescue Exception => ex
107 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
112 @logger.info "going to shutdown ..."
113 thgroup.list.each{|th| th.join if th[:WEBrickThread] }
114 call_callback(:StopCallback)
115 @logger.info "#{self.class}#start done."
121 if @status == :Running
131 @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
135 rescue Errno::ENOTCONN
136 # when `Errno::ENOTCONN: Socket is not connected' on some platforms,
137 # call #close instead of #shutdown.
138 # (ignore @config[:ShutdownSocketWithoutClose])
141 unless @config[:ShutdownSocketWithoutClose]
150 @logger.fatal "run() must be provided by user."
155 def accept_client(svr)
160 Utils::set_non_blocking(sock)
161 Utils::set_close_on_exec(sock)
162 rescue Errno::ECONNRESET, Errno::ECONNABORTED,
163 Errno::EPROTO, Errno::EINVAL => ex
164 rescue Exception => ex
165 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
171 def start_thread(sock, &block)
174 Thread.current[:WEBrickSocket] = sock
177 @logger.debug "accept: #{addr[3]}:#{addr[1]}"
179 @logger.debug "accept: <address unknown>"
182 call_callback(:AcceptCallback, sock)
183 block ? block.call(sock) : run(sock)
184 rescue Errno::ENOTCONN
185 @logger.debug "Errno::ENOTCONN raised"
186 rescue ServerError => ex
187 msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
189 rescue Exception => ex
193 Thread.current[:WEBrickSocket] = nil
195 @logger.debug "close: #{addr[3]}:#{addr[1]}"
197 @logger.debug "close: <address unknown>"
204 def call_callback(callback_name, *args)
205 if cb = @config[callback_name]
209 end # end of GenericServer