Ticket #30: mongrel_patch_stable_1-1.20080624-rev_1028.patch
| File mongrel_patch_stable_1-1.20080624-rev_1028.patch, 32.7 kB (added by cmdrclueless, 4 months ago) |
|---|
-
branches/stable_1-1/test/test_ws_unix.rb
old new 1 # Copyright (c) 2005 Zed A. Shaw 2 # You can redistribute it and/or modify it under the same terms as Ruby. 3 # 4 # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 # for more information. 6 7 require 'test/testhelp' 8 9 include Mongrel 10 11 class TestHandler < Mongrel::HttpHandler 12 attr_reader :ran_test 13 14 def process(request, response) 15 @ran_test = true 16 response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n") 17 end 18 end 19 20 21 class WebServerUnixTest < Test::Unit::TestCase 22 23 def setup 24 @port = 9998 25 @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n" 26 27 redirect_test_io do 28 # We set num_processors=1 so that we can test the reaping code 29 @server = UnixDispatchServer.new("127.0.0.1", @port, 1) 30 end 31 32 @tester = TestHandler.new 33 @server.register("/test", @tester) 34 redirect_test_io do 35 @server.run 36 end 37 end 38 39 def teardown 40 redirect_test_io do 41 @server.stop(true) 42 end 43 end 44 45 def do_test(string, chunk, close_after=nil, shutdown_delay=0) 46 # Do not use instance variables here, because it needs to be thread safe 47 socket = TCPSocket.new("127.0.0.1", @port); 48 request = StringIO.new(string) 49 chunks_out = 0 50 51 while data = request.read(chunk) 52 chunks_out += socket.write(data) 53 socket.flush 54 sleep 0.2 55 if close_after and chunks_out > close_after 56 socket.close 57 sleep 1 58 end 59 end 60 sleep(shutdown_delay) 61 socket.write(" ") # Some platforms only raise the exception on attempted write 62 socket.flush 63 end 64 65 def test_trickle_attack 66 do_test(@valid_request, 3) 67 end 68 69 def test_close_client 70 assert_raises IOError do 71 do_test(@valid_request, 10, 20) 72 end 73 end 74 75 def test_bad_client 76 redirect_test_io do 77 do_test("GET /test HTTP/BAD", 3) 78 end 79 end 80 81 def test_header_is_too_long 82 redirect_test_io do 83 long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n" 84 assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do 85 do_test(long, long.length/2, 10) 86 end 87 end 88 end 89 90 def test_file_streamed_request 91 body = "a" * (Mongrel::Const::MAX_BODY * 2) 92 long = "GET /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body 93 do_test(long, Mongrel::Const::CHUNK_SIZE * 2 -400) 94 end 95 96 end 97 -
branches/stable_1-1/test/test_redirect_handler_unix.rb
old new 1 # Copyright (c) 2005 Zed A. Shaw 2 # You can redistribute it and/or modify it under the same terms as Ruby. 3 # 4 # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 # for more information. 6 7 require 'test/testhelp' 8 9 class RedirectHandlerUnixTest < Test::Unit::TestCase 10 11 def setup 12 redirect_test_io do 13 @server = Mongrel::UnixDispatchServer.new('127.0.0.1', 9998, 1) 14 end 15 @server.run 16 @client = Net::HTTP.new('127.0.0.1', 9998) 17 end 18 19 def teardown 20 @server.stop(true) 21 end 22 23 def test_simple_redirect 24 tester = Mongrel::RedirectHandler.new('/yo') 25 @server.register("/test", tester) 26 27 sleep(1) 28 res = @client.request_get('/test') 29 assert res != nil, "Didn't get a response" 30 assert_equal ['/yo'], res.get_fields('Location') 31 end 32 33 def test_rewrite 34 tester = Mongrel::RedirectHandler.new(/(\w+)/, '+\1+') 35 @server.register("/test", tester) 36 37 sleep(1) 38 res = @client.request_get('/test/something') 39 assert_equal ['/+test+/+something+'], res.get_fields('Location') 40 end 41 42 end 43 44 -
branches/stable_1-1/test/test_conditional_unix.rb
old new 1 # Copyright (c) 2005 Zed A. Shaw 2 # You can redistribute it and/or modify it under the same terms as Ruby. 3 # 4 # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html 5 # for more information. 6 7 require 'test/testhelp' 8 9 include Mongrel 10 11 class ConditionalResponseUnixTest < Test::Unit::TestCase 12 def setup 13 @server = UnixDispatchServer.new('127.0.0.1', 3501, 1) 14 @server.register('/', Mongrel::DirHandler.new('.')) 15 @server.run 16 17 @http = Net::HTTP.new(@server.host, @server.port) 18 19 # get the ETag and Last-Modified headers 20 @path = '/README' 21 res = @http.start { |http| http.get(@path) } 22 assert_not_nil @etag = res['ETag'] 23 assert_not_nil @last_modified = res['Last-Modified'] 24 assert_not_nil @content_length = res['Content-Length'] 25 end 26 27 def teardown 28 @server.stop(true) 29 end 30 31 # status should be 304 Not Modified when If-None-Match is the matching ETag 32 def test_not_modified_via_if_none_match 33 assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag 34 end 35 36 # status should be 304 Not Modified when If-Modified-Since is the matching Last-Modified date 37 def test_not_modified_via_if_modified_since 38 assert_status_for_get_and_head Net::HTTPNotModified, 'If-Modified-Since' => @last_modified 39 end 40 41 # status should be 304 Not Modified when If-None-Match is the matching ETag 42 # and If-Modified-Since is the matching Last-Modified date 43 def test_not_modified_via_if_none_match_and_if_modified_since 44 assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => @last_modified 45 end 46 47 # status should be 200 OK when If-None-Match is invalid 48 def test_invalid_if_none_match 49 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid' 50 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid', 'If-Modified-Since' => @last_modified 51 end 52 53 # status should be 200 OK when If-Modified-Since is invalid 54 def test_invalid_if_modified_since 55 assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => 'invalid' 56 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => 'invalid' 57 end 58 59 # status should be 304 Not Modified when If-Modified-Since is greater than the Last-Modified header, but less than the system time 60 def test_if_modified_since_greater_than_last_modified 61 sleep 2 62 last_modified_plus_1 = (Time.httpdate(@last_modified) + 1).httpdate 63 assert_status_for_get_and_head Net::HTTPNotModified, 'If-Modified-Since' => last_modified_plus_1 64 assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_plus_1 65 end 66 67 # status should be 200 OK when If-Modified-Since is less than the Last-Modified header 68 def test_if_modified_since_less_than_last_modified 69 last_modified_minus_1 = (Time.httpdate(@last_modified) - 1).httpdate 70 assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => last_modified_minus_1 71 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_minus_1 72 end 73 74 # status should be 200 OK when If-Modified-Since is a date in the future 75 def test_future_if_modified_since 76 the_future = Time.at(2**31-1).httpdate 77 assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => the_future 78 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => the_future 79 end 80 81 # status should be 200 OK when If-None-Match is a wildcard 82 def test_wildcard_match 83 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*' 84 assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*', 'If-Modified-Since' => @last_modified 85 end 86 87 private 88 89 # assert the response status is correct for GET and HEAD 90 def assert_status_for_get_and_head(response_class, headers = {}) 91 %w{ get head }.each do |method| 92 res = @http.send(method, @path, headers) 93 assert_kind_of response_class, res 94 assert_equal @etag, res['ETag'] 95 case response_class.to_s 96 when 'Net::HTTPNotModified' then 97 assert_nil res['Last-Modified'] 98 assert_nil res['Content-Length'] 99 when 'Net::HTTPOK' then 100 assert_equal @last_modified, res['Last-Modified'] 101 assert_equal @content_length, res['Content-Length'] 102 else 103 fail "Incorrect response class: #{response_class}" 104 end 105 end 106 end 107 end -
branches/stable_1-1/lib/mongrel/configurator.rb
old new 136 136 ops[:throttle] ||= 0 137 137 ops[:timeout] ||= 60 138 138 139 @listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:throttle].to_i, ops[:timeout].to_i) 139 @listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:throttle].to_i, ops[:timeout].to_i) unless ops.has_key?(:unixmaster) and ops[:unixmaster] 140 @listener = Mongrel::UnixDispatchServer.new(ops[:host], ops[:port].to_i, ops[:min_children].to_i, ops[:max_childen].to_i) if ops.has_key?(:unixmaster) and ops[:unixmaster] 140 141 @listener_name = "#{ops[:host]}:#{ops[:port]}" 141 142 @listeners[@listener_name] = @listener 142 143 … … 386 387 387 388 end 388 389 end 390 # vim:shiftwidth=2:tabstop=2:expandtab:ft=ruby -
branches/stable_1-1/lib/mongrel.rb
old new 40 40 # Thrown at a thread when it is timed out. 41 41 class TimeoutError < Exception; end 42 42 43 class UriChangeEvent < Exception; end 44 43 45 # A Hash with one extra parameter for the HTTP body, used internally. 44 46 class HttpParams < Hash 45 47 attr_accessor :http_body 46 48 end 47 49 50 class Server 51 attr_accessor :classifier 52 attr_reader :acceptor 53 attr_reader :domain 54 attr_reader :host 55 attr_reader :port 48 56 49 # This is the main driver of Mongrel, while the Mongrel::HttpParser and Mongrel::URIClassifier50 # make up the majority of how the server functions. It's a very simple class that just51 # has a thread accepting connections and a simple HttpServer.process_client function52 # to do the heavy lifting with the IO and Ruby.53 #54 # You use it by doing the following:55 #56 # server = HttpServer.new("0.0.0.0", 3000)57 # server.register("/stuff", MyNiftyHandler.new)58 # server.run.join59 #60 # The last line can be just server.run if you don't want to join the thread used.61 # If you don't though Ruby will mysteriously just exit on you.62 #63 # Ruby's thread implementation is "interesting" to say the least. Experiments with64 # *many* different types of IO processing simply cannot make a dent in it. Future65 # releases of Mongrel will find other creative ways to make threads faster, but don't66 # hold your breath until Ruby 1.9 is actually finally useful.67 class HttpServer68 attr_reader :acceptor69 attr_reader :workers70 attr_reader :classifier71 attr_reader :host72 attr_reader :port73 attr_reader :throttle74 attr_reader :timeout75 attr_reader :num_processors76 77 57 # Creates a working server on host:port (strange things happen if port isn't a Number). 78 58 # Use HttpServer::run to start the server and HttpServer.acceptor.join to 79 59 # join the thread that's processing incoming requests on the socket. … … 87 67 # The throttle parameter is a sleep timeout (in hundredths of a second) that is placed between 88 68 # socket.accept calls in order to give the server a cheap throttle time. It defaults to 0 and 89 69 # actually if it is 0 then the sleep is not done at all. 90 def initialize(host, port, num_processors=950, throttle=0, timeout=60) 91 92 tries = 0 93 @socket = TCPServer.new(host, port) 94 70 def initialize 95 71 @classifier = URIClassifier.new 96 @host = host97 @port = port98 @workers = ThreadGroup.new99 @throttle = throttle / 100.0100 @num_processors = num_processors101 @timeout = timeout102 72 end 103 73 104 74 # Does the majority of the IO processing. It has been written in Ruby using … … 204 174 end 205 175 end 206 176 177 def read_dead_workers(reason=nil) 178 raise RuntimeError, "This method must be overridden in a derived class" 179 end 180 181 def configure_socket_options 182 case RUBY_PLATFORM 183 when /linux/ 184 # 9 is currently TCP_DEFER_ACCEPT 185 $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1] 186 $tcp_cork_opts = [Socket::SOL_TCP, 3, 1] 187 when /freebsd(([1-4]\..{1,2})|5\.[0-4])/ 188 # Do nothing, just closing a bug when freebsd <= 5.4 189 when /freebsd/ 190 # Use the HTTP accept filter if available. 191 # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg 192 unless `/sbin/sysctl -nq net.inet.accf.http`.empty? 193 $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')] 194 end 195 end 196 end 197 198 # Runs the thing. It returns the thread used so you can "join" it. You can also 199 # access the HttpServer::acceptor attribute to get the thread later. 200 def run 201 raise RuntimeError, "This method must be overridden in a derived class" 202 end 203 204 # Simply registers a handler with the internal URIClassifier. When the URI is 205 # found in the prefix of a request then your handler's HttpHandler::process method 206 # is called. See Mongrel::URIClassifier#register for more information. 207 # 208 # If you set in_front=true then the passed in handler will be put in the front of the list 209 # for that particular URI. Otherwise it's placed at the end of the list. 210 def register(uri, handler, in_front=false) 211 begin 212 @classifier.register(uri, [handler]) 213 rescue URIClassifier::RegistrationError 214 handlers = @classifier.resolve(uri)[2] 215 method_name = in_front ? 'unshift' : 'push' 216 handlers.send(method_name, handler) 217 end 218 handler.listener = self 219 end 220 221 # Removes any handlers registered at the given URI. See Mongrel::URIClassifier#unregister 222 # for more information. Remember this removes them *all* so the entire 223 # processing chain goes away. 224 def unregister(uri) 225 @classifier.unregister(uri) 226 end 227 228 # Stops the acceptor thread and then causes the worker threads to finish 229 # off the request queue before finally exiting. 230 def stop(synchronous=false) 231 unless @acceptor.nil? 232 @acceptor.raise(StopServer.new) 233 234 if synchronous 235 sleep(0.5) while @acceptor.alive? 236 end 237 end 238 end 239 end 240 241 # This is the main driver of Mongrel, while the Mongrel::HttpParser and Mongrel::URIClassifier 242 # make up the majority of how the server functions. It's a very simple class that just 243 # has a thread accepting connections and a simple HttpServer.process_client function 244 # to do the heavy lifting with the IO and Ruby. 245 # 246 # You use it by doing the following: 247 # 248 # server = HttpServer.new("0.0.0.0", 3000) 249 # server.register("/stuff", MyNiftyHandler.new) 250 # server.run.join 251 # 252 # The last line can be just server.run if you don't want to join the thread used. 253 # If you don't though Ruby will mysteriously just exit on you. 254 # 255 # Ruby's thread implementation is "interesting" to say the least. Experiments with 256 # *many* different types of IO processing simply cannot make a dent in it. Future 257 # releases of Mongrel will find other creative ways to make threads faster, but don't 258 # hold your breath until Ruby 1.9 is actually finally useful. 259 class HttpServer < Server 260 attr_reader :workers 261 attr_reader :throttle 262 attr_reader :timeout 263 attr_reader :num_processors 264 265 # Creates a working server on host:port (strange things happen if port isn't a Number). 266 # Use HttpServer::run to start the server and HttpServer.acceptor.join to 267 # join the thread that's processing incoming requests on the socket. 268 # 269 # The num_processors optional argument is the maximum number of concurrent 270 # processors to accept, anything over this is closed immediately to maintain 271 # server processing performance. This may seem mean but it is the most efficient 272 # way to deal with overload. Other schemes involve still parsing the client's request 273 # which defeats the point of an overload handling system. 274 # 275 # The throttle parameter is a sleep timeout (in hundredths of a second) that is placed between 276 # socket.accept calls in order to give the server a cheap throttle time. It defaults to 0 and 277 # actually if it is 0 then the sleep is not done at all. 278 def initialize(host, port, num_processors=950, throttle=0, timeout=60) 279 @host = host 280 @port = port 281 @domain = "tcp" 282 super() 283 284 @socket = TCPServer.new(host, port) 285 @workers = ThreadGroup.new 286 @throttle = throttle / 100.0 287 @num_processors = num_processors 288 @timeout = timeout 289 end 290 207 291 # Used internally to kill off any worker threads that have taken too long 208 292 # to complete processing. Only called if there are too many processors 209 293 # currently servicing. It returns the count of workers still active … … 236 320 sleep @timeout / 10 237 321 end 238 322 end 239 240 def configure_socket_options241 case RUBY_PLATFORM242 when /linux/243 # 9 is currently TCP_DEFER_ACCEPT244 $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1]245 $tcp_cork_opts = [Socket::SOL_TCP, 3, 1]246 when /freebsd(([1-4]\..{1,2})|5\.[0-4])/247 # Do nothing, just closing a bug when freebsd <= 5.4248 when /freebsd/249 # Use the HTTP accept filter if available.250 # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg251 unless `/sbin/sysctl -nq net.inet.accf.http`.empty?252 $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')]253 end254 end255 end256 323 257 324 # Runs the thing. It returns the thread used so you can "join" it. You can also 258 325 # access the HttpServer::acceptor attribute to get the thread later. … … 310 377 311 378 return @acceptor 312 379 end 380 end 313 381 314 # Simply registers a handler with the internal URIClassifier. When the URI is 315 # found in the prefix of a request then your handler's HttpHandler::process method 316 # is called. See Mongrel::URIClassifier#register for more information. 317 # 318 # If you set in_front=true then the passed in handler will be put in the front of the list 319 # for that particular URI. Otherwise it's placed at the end of the list. 320 def register(uri, handler, in_front=false) 321 begin 322 @classifier.register(uri, [handler]) 323 rescue URIClassifier::RegistrationError 324 handlers = @classifier.resolve(uri)[2] 325 method_name = in_front ? 'unshift' : 'push' 326 handlers.send(method_name, handler) 382 class UnixDispatchServer < Server 383 attr_reader :min_children 384 attr_reader :num_children 385 attr_reader :max_children 386 387 def initialize(host, port, min_children=5, max_children=nil, log=nil, log_level=:debug) 388 @host = host 389 @port = port 390 @domain = 'unix' 391 super() 392 393 @serversock= TCPServer.new(@host,@port) 394 395 @terminate = false 396 @children = Hash.new 397 @busy = Hash.new 398 399 @min_children = min_children 400 @max_children = max_children 401 402 @close_on_fork = [@serversock] 403 end 404 405 def reap_dead_workers(reason=nil) 406 end # do nothing, not useful for the unix dispatch server 407 408 def runchild(server) 409 BasicSocket.do_not_reverse_lookup = true 410 411 configure_socket_options 412 413 @acceptor = Thread.new do 414 begin 415 while not @terminate 416 begin 417 server.write("READY #{$$}\n") 418 server.flush 419 420 client = server.recv_io(TCPSocket) # read the client's file descriptor from the server! 421 unless client.is_a?(TCPSocket) 422 next 423 end 424 if client.respond_to?(:setsockopt) and defined?($tcp_cork_opts) and $tcp_cork_opts 425 client.setsockopt(*$tcp_cork_opts) rescue nil 426 end 427 428 process_client(client) 429 rescue StopServer 430 @terminate = true 431 rescue Errno::ECONNABORTED 432 client.close rescue nil 433 rescue Errno::EPIPE 434 # server broke connection 435 @terminate = true 436 rescue SignalException => e 437 # child signaled to terminate 438 @terminate = true 439 rescue Object => e 440 @terminte = true 441 STDERR.puts "Child #{$$} Unhandled listen loop exception #{e.inspect}." 442 STDERR.puts e.backtrace.join("\n") 443 end 444 end 445 ensure 446 server.write("CLOSED #{$$}\n") rescue nil 447 server.close rescue nil 448 end 327 449 end 328 handler.listener = self450 return @acceptor 329 451 end 330 452 331 # Removes any handlers registered at the given URI. See Mongrel::URIClassifier#unregister 332 # for more information. Remember this removes them *all* so the entire 333 # processing chain goes away. 334 def unregister(uri) 335 @classifier.unregister(uri) 453 def start_child 454 cio,sio = UNIXSocket::socketpair 455 @close_on_fork << sio 456 457 pid = fork 458 if pid.nil? # child 459 begin 460 @close_on_fork.each { |io| io.close rescue nil } 461 unless RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ 462 trap("INT") { @terminate = true; stop } 463 trap("HUP") { @terminate = true; stop } 464 trap("TERM") { @terminate = true; stop } 465 end 466 begin 467 runchild(cio).join 468 rescue StopServer 469 nil # no need to log this, it's just going to terminate the child. 470 end 471 Kernel.exit!(0) 472 rescue Object => e 473 STDERR.puts "Child #{$$} Unhandled listen loop exception #{e.inspect}." 474 STDERR.puts e.backtrace.join("\n") 475 end 476 Kernel.exit!(1) 477 end 478 479 cio.close rescue nil 480 [pid,sio] 336 481 end 337 482 338 # Stops the acceptor thread and then causes the worker threads to finish 339 # off the request queue before finally exiting. 340 def stop(synchronous=false) 341 @acceptor.raise(StopServer.new) 483 def evict_child(pid) 484 begin 485 Process.kill("TERM", pid) 486 Process.waitpid(pid) 487 rescue Errno::ESRCH,Errno::ECHILD => e 488 nil # ignore 489 rescue StopServer 490 # be sure to pass the stop up the call chain until it gets to the toplevel handler! 491 raise 492 rescue Object => e 493 STDERR.puts "Server #{$$} Unhandled exception #{e.inspect}." 494 STDERR.puts :error, e.backtrace.join("\n") 495 end 342 496 343 if synchronous 344 sleep(0.5) while @acceptor.alive? 497 if @children.has_key?(pid) 498 io = @children.delete(pid) 499 if io.respond_to?(:close) 500 io.close rescue nil 501 end 345 502 end 503 504 if @busy.has_key?(pid) 505 io = @busy.delete(pid) 506 if io.respond_to?(:close) 507 io.close rescue nil 508 end 509 end 346 510 end 511 512 def run 513 BasicSocket.do_not_reverse_lookup = true 514 configure_socket_options 347 515 516 if defined?($tcp_defer_accept_opts) and $tcp_defer_accept_opts 517 @serversock.setsockopt(*$tcp_defer_accept_opts) rescue nil 518 end 519 520 @acceptor = Thread.new do 521 522 begin 523 while not @terminate 524 begin 525 @children.each_key do |pid| 526 begin 527 do_evict = false 528 do_evict = true if (@children[pid].closed? or Process.waitpid(pid, Process::WNOHANG) == pid) 529 rescue Errno::ECHILD 530 do_evict = true 531 rescue StopServer 532 raise # pass it up the chain, don't ignore it. 533 rescue Object => e 534 STDERR.puts "Server #{$$} Unhandled exception #{e.inspect}." 535 STDERR.puts e.backtrace.join("\n") 536 end 537 538 evict_child(pid) if do_evict 539 end 540 541 if @children.length < @min_children 542 (@children.length .. @min_children).each do 543 pid, socket = start_child 544 @children[pid] = socket 545 @busy[pid] = true 546 end 547 end 548 549 readfds = [ @serversock ] + @children.values 550 r,w,e = Kernel.select(readfds, nil, nil, 60) 551 552 # check to see if anyone wants to talk to us. If no one is talking 553 # and we have more than the necessary number of children then signal 554 # one to terminate. 555 # 556 evict_child(@children.keys.first) if (r.nil? or r.empty?) and @children.length > @min_children 557 next if r.nil? 558 559 # OK, someone is talking. Find out who and process the client. 560 # Delay the processing of the TCP server socket until all the children 561 # are finished. This allows a client to become available before processing 562 # the HTTP connection. 563 # 564 flag_http = false 565 r.each do |io| 566 if io == @serversock # a new connection from apache or the outside world 567 flag_http = true # defer 568 else # one of the children 569 begin 570 msg = io.readline 571 msg.chomp! 572 if msg =~ /^READY\s+(\d+)$/ 573 pid = $1.to_i 574 if @busy[pid].respond_to?(:close) 575 @busy[pid].close rescue nil 576 end 577 @busy[pid] = false 578 elsif msg =~ /CLOSED\s+(\d+)/ 579 evict_child($1.to_i) 580 end 581 rescue EOFError 582 @children.each_key do |pid| 583 evict_child(pid) if @children[pid] == io 584 end 585 end 586 end 587 end 588 589 next unless flag_http 590 591 client = nil 592 begin 593 client = @serversock.accept 594 rescue StandardError => e 595 STDERR.puts "Server #{$$} An error occurred accepting a new client connection, a restart may be necessary." 596 STDERR.puts "Server #{$$} #{e.message}" 597 end 598 599 next if client.nil? 600 601 @busy.each_pair do |pid,busy| 602 unless busy 603 io = @children[pid] 604 io.send_io(client) 605 io.flush 606 607 @busy[pid] = client 608 client = nil 609 break 610 end 611 end 612 613 if client and not @max_children.nil? and @max_children == @children.length 614 STDERR.puts "Server #{$$} Maximum number of child processes exceeded, request aborted" 615 client.close rescue nil 616 client = nil 617 end 618 619 next unless client 620 621 # spin up a new one 622 # 623 pid, socket = start_child 624 @children[pid] = socket 625 socket.readline 626 socket.send_io(client) 627 socket.flush 628 @busy[pid] = client 629 630 rescue StopServer 631 @terminate = true 632 rescue UriChangeEvent 633 @children.keys.each { |pid| evict_child(pid) } 634 rescue Object => e 635 STDERR.puts "Server #{$$} Unhandled listen loop exception #{e.inspect}." 636 STDERR.puts e.backtrace.join("\n") 637 end 638 end 639 ensure 640 @serversock.close rescue nil 641 @children.each_key { |pid| evict_child(pid) } 642 end 643 end # end thread 644 645 return @acceptor 646 end 647 648 def register(uri, handler, in_front=false) 649 super(uri,handler,in_front) 650 @acceptor.raise(UriChangeEvent.new) if not @acceptor.nil? and @acceptor.alive? 651 end 652 653 def unregister(uri) 654 super(uri) 655 @acceptor.raise(UriChangeEvent.new) if not @acceptor.nil? and @acceptor.alive? 656 end 348 657 end 349 658 end 350 659 … … 353 662 354 663 $LOAD_PATH.unshift 'projects/mongrel_experimental/lib/' 355 664 Mongrel::Gems.require 'mongrel_experimental', ">=#{Mongrel::Const::MONGREL_VERSION}" 665 666 # vim:expandtab:shiftwidth=2:tabstop=2 -
branches/stable_1-1/bin/mongrel_rails
old new 1 1 # Copyright (c) 2005 Zed A. Shaw 2 # Copyright (c) 2008 Raritan Computer, Inc (Brian Weaver <brian.weaver@raritan.com>) 2 3 # You can redistribute it and/or modify it under the same terms as Ruby. 3 4 # 4 5 # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html … … 27 28 ['-p', '--port PORT', "Which port to bind to", :@port, 3000], 28 29 ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"], 29 30 ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"], 31 ['-L', '--log-level (debug|notice|warn|info|error)', "The logging level", :@log_level, 'debug'], 30 32 ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"], 31 33 ['-n', '--num-processors INT', "Number of processors active before clients denied", :@num_processors, 1024], 32 34 ['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, 60], … … 38 40 ['-C', '--config PATH', "Use a config file", :@config_file, nil], 39 41 ['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil], 40 42 ['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil], 43 ['-U', '--unixmaster', "Run the rails as a master-slave server", :@unixmaster, false], 44 ['-N', '--children COUNT', "The default number of unix slaves to start", :@min_children, 5], 45 ['-M', '--max-children COUNT', "The maximum number of unix slaves to start", :@max_children, nil], 41 46 ['', '--user USER', "User to run as", :@user, nil], 42 47 ['', '--group GROUP', "Group to run as", :@group, nil], 43 48 ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil] … … 69 74 valid_user? @user if @user 70 75 valid_group? @group if @group 71 76 77 @log_level = @log_level.to_sym unless @log_level.nil? or @log_level.is_a?(Symbol) 78 72 79 return @valid 73 80 end 74 81 … … 92 99 daemonize 93 100 log "Daemonized, any open files are closed. Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info." 94 101 log "Settings loaded from #{@config_file} (they override command line)." if @config_file 102 103 write_pid_file 95 104 end 96 105 97 106 log "Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}" … … 128 137 config.run 129 138 config.log "Mongrel #{Mongrel::Const::MONGREL_VERSION} available at #{@address}:#{@port}" 130 139 131 if config.defaults[:daemon] 132 config.write_pid_file 133 else 140 unless config.defaults[:daemon] 134 141 config.log "Use CTRL-C to stop." 135 142 end 136 143 … … 180 187 181 188 def config_keys 182 189 @config_keys ||= 183 %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix )190 %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix unixmaster min_children max_children) 184 191 end 185 192 186 193 def settings … … 281 288 if not Mongrel::Command::Registry.instance.run ARGV 282 289 exit 1 283 290 end 291 292 # vim:shiftwidth=2:tabstop=2:expandtab:ft=ruby
