Re-enable spec/library for full CI runs.
[rbx.git] / lib / webrick / server.rb
blob16dbd46f981a546aa5c5fa61d52a8f4a07bbfc86
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
7 # reserved.
9 # $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
11 require 'thread'
12 require 'socket'
13 require 'timeout'
14 require 'webrick/config'
15 require 'webrick/log'
17 module WEBrick
19   class ServerError < StandardError; end
21   class SimpleServer
22     def SimpleServer.start
23       yield
24     end
25   end
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
41   class GenericServer
42     attr_reader :status, :config, :logger, :tokens, :listeners
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]
50       @tokens = SizedQueue.new(@config[:MaxClients])
51       @config[:MaxClients].times{ @tokens.push(nil) }
53       webrickv = WEBrick::VERSION
54       rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
55       @logger.info("WEBrick #{webrickv}")
56       @logger.info("ruby #{rubyv}")
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
70     def [](key)
71       @config[key]
72     end
74     def listen(address, port)
75       @listeners += Utils::create_listeners(address, port, @logger)
76     end
78     def start(&block)
79       raise ServerError, "already started." if @status != :Stop
80       server_type = @config[:ServerType] || SimpleServer
82       server_type.start{
83         @logger.info \
84           "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
85         call_callback(:StartCallback)
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
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
120     def stop
121       if @status == :Running
122         @status = :Shutdown
123       end
124     end
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
138     def run(sock)
139       @logger.fatal "run() must be provided by user."
140     end
142     private
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
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
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