File: webrick/server.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: WEBrick#17
  class: ServerError#19
inherits from
  StandardError ( Builtin-Module )
  class: SimpleServer#21
inherits from
  Object ( Builtin-Module )
has properties
class method: start #22
  class: Daemon#27
inherits from
  Object ( Builtin-Module )
has properties
class method: start #28
  class: GenericServer#41
inherits from
  Object ( Builtin-Module )
has properties
attribute: status [R] #42
attribute: config [R] #42
attribute: logger [R] #42
attribute: tokens [R] #42
attribute: listeners [R] #42
method: initialize / 2 #44
method: [] / 1 #70
method: listen (1/2) / 2 #74
method: start / 1 #78
method: stop #120
method: shutdown #126
method: run / 1 #138
method: accept_client / 1 #144
method: start_thread / 2 #161
method: call_callback / 2 #194

Class Hierarchy

Code

   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 
  11  require 'thread'
  12  require 'socket'
  13  require 'timeout'
  14  require 'webrick/config'
  15  require 'webrick/log'
  16 
  17  module WEBrick
  18 
  19    class ServerError < StandardError; end
  20 
  21    class SimpleServer
  22      def SimpleServer.start
  23        yield
  24      end
  25    end
  26 
  27    class Daemon
  28      def Daemon.start
  29        exit!(0) if fork
  30        Process::setsid
  31        exit!(0) if fork
  32        Dir::chdir("/")
  33        File::umask(0)
  34        STDIN.reopen("/dev/null")
  35        STDOUT.reopen("/dev/null", "w")
  36        STDERR.reopen("/dev/null", "w")
  37        yield if block_given?
  38      end
  39    end
  40 
  41    class GenericServer
  42      attr_reader :status, :config, :logger, :tokens, :listeners
  43 
  44      def initialize(config={}, default=Config::General)
  45        @config = default.dup.update(config)
  46        @status = :Stop
  47        @config[:Logger] ||= Log::new
  48        @logger = @config[:Logger]
  49 
  50        @tokens = SizedQueue.new(@config[:MaxClients])
  51        @config[:MaxClients].times{ @tokens.push(nil) }
  52 
  53        webrickv = WEBrick::VERSION
  54        rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
  55        @logger.info("WEBrick #{webrickv}")
  56        @logger.info("ruby #{rubyv}")
  57 
  58        @listeners = []
  59        unless @config[:DoNotListen]
  60          if @config[:Listen]
  61            warn(":Listen option is deprecated; use GenericServer#listen")
  62          end
  63          listen(@config[:BindAddress], @config[:Port])
  64          if @config[:Port] == 0
  65            @config[:Port] = @listeners[0].addr[1]
  66          end
  67        end
  68      end
  69 
  70      def [](key)
  71        @config[key]
  72      end
  73 
  74      def listen(address, port)
  75        @listeners += Utils::create_listeners(address, port, @logger)
  76      end
  77 
  78      def start(&block)
  79        raise ServerError, "already started." if @status != :Stop
  80        server_type = @config[:ServerType] || SimpleServer
  81 
  82        server_type.start{
  83          @logger.info \
  84            "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
  85          call_callback(:StartCallback)
  86 
  87          thgroup = ThreadGroup.new
  88          @status = :Running
  89          while @status == :Running
  90            begin
  91              if svrs = IO.select(@listeners, nil, nil, 2.0)
  92                svrs[0].each{|svr|
  93                  @tokens.pop          # blocks while no token is there.
  94                  if sock = accept_client(svr)
  95                    th = start_thread(sock, &block)
  96                    th[:WEBrickThread] = true
  97                    thgroup.add(th)
  98                  else
  99                    @tokens.push(nil)
 100                  end
 101                }
 102              end
 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]}"
 108              @logger.error msg
 109            end
 110          end
 111 
 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."
 116          @status = :Stop
 117        }
 118      end
 119 
 120      def stop
 121        if @status == :Running
 122          @status = :Shutdown
 123        end
 124      end
 125 
 126      def shutdown
 127        stop
 128        @listeners.each{|s|
 129          if @logger.debug?
 130            addr = s.addr
 131            @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
 132          end
 133          s.close
 134        }
 135        @listeners.clear
 136      end
 137 
 138      def run(sock)
 139        @logger.fatal "run() must be provided by user."
 140      end
 141 
 142      private
 143 
 144      def accept_client(svr)
 145        sock = nil
 146        begin
 147          sock = svr.accept
 148          sock.sync = true
 149          Utils::set_non_blocking(sock)
 150          Utils::set_close_on_exec(sock)
 151        rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO => ex
 152          # TCP connection was established but RST segment was sent
 153          # from peer before calling TCPServer#accept.
 154        rescue Exception => ex
 155          msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
 156          @logger.error msg
 157        end
 158        return sock
 159      end
 160 
 161      def start_thread(sock, &block)
 162        Thread.start{
 163          begin
 164            Thread.current[:WEBrickSocket] = sock
 165            begin
 166              addr = sock.peeraddr
 167              @logger.debug "accept: #{addr[3]}:#{addr[1]}"
 168            rescue SocketError
 169              @logger.debug "accept: <address unknown>"
 170              raise
 171            end
 172            call_callback(:AcceptCallback, sock)
 173            block ? block.call(sock) : run(sock)
 174          rescue Errno::ENOTCONN
 175            @logger.debug "Errno::ENOTCONN raised"
 176          rescue ServerError => ex
 177            msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
 178            @logger.error msg
 179          rescue Exception => ex
 180            @logger.error ex
 181          ensure
 182            @tokens.push(nil)
 183            Thread.current[:WEBrickSocket] = nil
 184            if addr
 185              @logger.debug "close: #{addr[3]}:#{addr[1]}"
 186            else
 187              @logger.debug "close: <address unknown>"
 188            end
 189            sock.close
 190          end
 191        }
 192      end
 193 
 194      def call_callback(callback_name, *args)
 195        if cb = @config[callback_name]
 196          cb.call(*args)
 197        end
 198      end
 199    end    # end of GenericServer
 200  end