OSDN Git Service

ruby-1.9.1-rc1
[splhack/AndroidRuby.git] / lib / ruby-1.9.1-rc1 / lib / webrick / server.rb
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 'webrick/config'
14 require 'webrick/log'
15
16 module WEBrick
17
18   class ServerError < StandardError; end
19
20   class SimpleServer
21     def SimpleServer.start
22       yield
23     end
24   end
25
26   class Daemon
27     def Daemon.start
28       exit!(0) if fork
29       Process::setsid
30       exit!(0) if fork
31       Dir::chdir("/")
32       File::umask(0)
33       STDIN.reopen("/dev/null")
34       STDOUT.reopen("/dev/null", "w")
35       STDERR.reopen("/dev/null", "w")
36       yield if block_given?
37     end
38   end
39
40   class GenericServer
41     attr_reader :status, :config, :logger, :tokens, :listeners
42
43     def initialize(config={}, default=Config::General)
44       @config = default.dup.update(config)
45       @status = :Stop
46       @config[:Logger] ||= Log::new
47       @logger = @config[:Logger]
48
49       @tokens = SizedQueue.new(@config[:MaxClients])
50       @config[:MaxClients].times{ @tokens.push(nil) }
51
52       webrickv = WEBrick::VERSION
53       rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
54       @logger.info("WEBrick #{webrickv}")
55       @logger.info("ruby #{rubyv}")
56
57       @listeners = []
58       unless @config[:DoNotListen]
59         if @config[:Listen]
60           warn(":Listen option is deprecated; use GenericServer#listen")
61         end
62         listen(@config[:BindAddress], @config[:Port])
63         if @config[:Port] == 0
64           @config[:Port] = @listeners[0].addr[1]
65         end
66       end
67     end
68
69     def [](key)
70       @config[key]
71     end
72
73     def listen(address, port)
74       @listeners += Utils::create_listeners(address, port, @logger)
75     end
76
77     def start(&block)
78       raise ServerError, "already started." if @status != :Stop
79       server_type = @config[:ServerType] || SimpleServer
80
81       server_type.start{
82         @logger.info \
83           "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
84         call_callback(:StartCallback)
85
86         thgroup = ThreadGroup.new
87         @status = :Running
88         while @status == :Running
89           begin
90             if svrs = IO.select(@listeners, nil, nil, 2.0)
91               svrs[0].each{|svr|
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
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         begin
134           s.shutdown
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])
139           s.close
140         else
141           unless @config[:ShutdownSocketWithoutClose]
142             s.close
143           end
144         end
145       }
146       @listeners.clear
147     end
148
149     def run(sock)
150       @logger.fatal "run() must be provided by user."
151     end
152
153     private
154
155     def accept_client(svr)
156       sock = nil
157       begin
158         sock = svr.accept
159         sock.sync = true
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]}"
166         @logger.error msg
167       end
168       return sock
169     end
170
171     def start_thread(sock, &block)
172       Thread.start{
173         begin
174           Thread.current[:WEBrickSocket] = sock
175           begin
176             addr = sock.peeraddr
177             @logger.debug "accept: #{addr[3]}:#{addr[1]}"
178           rescue SocketError
179             @logger.debug "accept: <address unknown>"
180             raise
181           end
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]}"
188           @logger.error msg
189         rescue Exception => ex
190           @logger.error ex
191         ensure
192           @tokens.push(nil)
193           Thread.current[:WEBrickSocket] = nil
194           if addr
195             @logger.debug "close: #{addr[3]}:#{addr[1]}"
196           else
197             @logger.debug "close: <address unknown>"
198           end
199           sock.close
200         end
201       }
202     end
203
204     def call_callback(callback_name, *args)
205       if cb = @config[callback_name]
206         cb.call(*args)
207       end
208     end
209   end    # end of GenericServer
210 end