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