Ticket #30: mongrel_changes.diff

File mongrel_changes.diff, 29.4 kB (added by cmdrclueless, 4 months ago)

Patch file for mongrel

  • lib/mongrel/configurator.rb

    old new  
    136136      opts[:throttle] ||= 0 
    137137      opts[:timeout] ||= 60 
    138138 
    139       @listener = Mongrel::HttpServer.new( 
    140       opts[:host], opts[:port].to_i, opts[:num_processors].to_i,  
    141       opts[:throttle].to_i, opts[:timeout].to_i,  
    142       opts[:log], opts[:log_level] 
    143       ) 
     139      if opts.has_key?(:unixmaster) and opts[:unixmaster] 
     140        @listener = Mongrel::UnixDispatchServer.new(opts[:host], 
     141                                                    opts[:port], 
     142                                                    opts[:min_children], 
     143                                                    opts[:max_children], 
     144                                                    opts[:log_file], 
     145                                                    opts[:log_level]) 
     146      else 
     147        @listener = Mongrel::HttpServer.new(opts[:host], 
     148                                            opts[:port].to_i, 
     149                                            opts[:num_processors].to_i, 
     150                                            opts[:throttle].to_i, 
     151                                            opts[:timeout].to_i, 
     152                                            opts[:log_file], 
     153                                            opts[:log_level]) 
     154      end 
    144155      @listener_name = "#{opts[:host]}:#{opts[:port]}" 
    145156      @listeners[@listener_name] = @listener 
    146157 
     
    372383 
    373384      unless RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ 
    374385        # graceful shutdown 
    375         trap("TERM") { Mongrel.log(:notice, "TERM signal received."); stop } 
     386        trap("TERM") { Mongrel.log(:notice, "pid #{$$}: TERM signal received."); stop } 
    376387        # debug mode 
    377         trap("USR1") { Mongrel.log(:notice, "USR1 received, toggling $mongrel_debug_client to #{!$mongrel_debug_client}"); $mongrel_debug_client = !$mongrel_debug_client } 
     388        trap("USR1") { Mongrel.log(:notice, "pid #{$$}: USR1 received, toggling $mongrel_debug_client to #{!$mongrel_debug_client}"); $mongrel_debug_client = !$mongrel_debug_client } 
    378389        # restart 
    379         trap("USR2") { Mongrel.log(:notice, "USR2 signal received."); stop(true) } 
     390        trap("USR2") { Mongrel.log(:notice, "pid #{$$}: USR2 signal received."); stop(true) } 
    380391 
    381392        Mongrel.log(:notice, "Signals ready.  TERM => stop.  USR2 => restart.  INT => stop (no restart).") 
    382393      else 
     
    387398  end 
    388399 
    389400end 
     401# vim:shiftwidth=2:tabstop=2:expandtab:ft=ruby 
  • lib/mongrel.rb

    old new  
     1# Copyright (c) 2005 Zed A. Shaw 
     2# Copyright (c) 2008 Raritan Computer, Inc (Brian Weaver <brian.weaver@raritan.com>) 
    13# Ruby 
    24require 'socket' 
    35require 'tempfile' 
     
    4749    attr_accessor :http_body 
    4850  end 
    4951 
    50   # This is the main driver of Mongrel, while the Mongrel::HttpParser and Mongrel::URIClassifier 
    51   # make up the majority of how the server functions.  It's a very simple class that just 
    52   # has a thread accepting connections and a simple HttpServer.process_client function 
    53   # to do the heavy lifting with the IO and Ruby.   
    54   # 
    55   # You use it by doing the following: 
    56   # 
    57   #   server = 2("0.0.0.0", 3000) 
    58   #   server.register("/stuff", MyNiftyHandler.new) 
    59   #   server.run.join 
    60   # 
    61   # The last line can be just server.run if you don't want to join the thread used. 
    62   # If you don't though Ruby will mysteriously just exit on you. 
    63   # 
    64   # Ruby's thread implementation is "interesting" to say the least.  Experiments with 
    65   # *many* different types of IO processing simply cannot make a dent in it.  Future 
    66   # releases of Mongrel will find other creative ways to make threads faster, but don't 
    67   # hold your breath until Ruby 1.9 is actually finally useful. 
    68   class HttpServer 
    69     attr_reader :acceptor 
    70     attr_reader :workers 
    71     attr_reader :classifier 
    72     attr_reader :host 
    73     attr_reader :port 
    74     attr_reader :throttle 
    75     attr_reader :timeout 
    76     attr_reader :num_processors 
    77  
     52  class Server 
    7853    attr_accessor :logger 
     54    attr_reader   :acceptor 
     55    attr_accessor :classifier 
     56    attr_reader   :domain 
     57    attr_reader   :host 
     58    attr_reader   :port 
    7959 
    80     # Creates a working server on host:port (strange things happen if port isn't a Number). 
    81     # Use HttpServer::run to start the server and HttpServer.acceptor.join to  
    82     # join the thread that's processing incoming requests on the socket. 
    83     # 
    84     # The num_processors optional argument is the maximum number of concurrent 
    85     # processors to accept, anything over this is closed immediately to maintain 
    86     # server processing performance.  This may seem mean but it is the most efficient 
    87     # way to deal with overload.  Other schemes involve still parsing the client's request 
    88     # which defeats the point of an overload handling system. 
    89     #  
    90     # The throttle parameter is a sleep timeout (in hundredths of a second) that is placed between  
    91     # socket.accept calls in order to give the server a cheap throttle time.  It defaults to 0 and 
    92     # actually if it is 0 then the sleep is not done at all. 
    93     def initialize(host, port, num_processors=950, throttle=0, timeout=60, log=nil, log_level=:debug) 
    94        
    95       tries = 0 
    96       @socket = TCPServer.new(host, port)  
    97        
     60    def initialize(log=nil, log_level=:debug) 
    9861      @classifier = URIClassifier.new 
    99       @host = host 
    100       @port = port 
    101       @workers = ThreadGroup.new 
    102       @throttle = throttle 
    103       @num_processors = num_processors 
    104       @timeout = timeout 
    105       @logger = Mongrel::Log.new(log || "log/mongrel-#{host}-#{port}.log", log_level) 
     62      @logger = Mongrel::Log.new(log || "log/mongrel-#{domain}-#{host}-#{port}.log", log_level) 
    10663    end 
    10764     
    10865    # Does the majority of the IO processing.  It has been written in Ruby using 
     
    11774        request = nil 
    11875        data = client.readpartial(Const::CHUNK_SIZE) 
    11976        nparsed = 0 
    120  
     77       
    12178        # Assumption: nparsed will always be less since data will get filled with more 
    12279        # after each parsing.  If it doesn't get more then there was a problem 
    12380        # with the read operation on the client socket.  Effect is to stop processing when the 
     
    185142            end 
    186143          end 
    187144        end 
    188       rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF 
     145      rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF => e 
     146        Mongrel.log(:debug, e.message) 
    189147        client.close rescue nil 
    190148      rescue HttpParserError => e 
    191         Mongrel.log(:error, "#{Time.now.httpdate}: HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}") 
    192         Mongrel.log(:error, "#{Time.now.httpdate}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n") 
     149        Mongrel.log(:error, "HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}") 
     150        Mongrel.log(:error, "REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n") 
    193151        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4 
    194152        client.write(Const::ERROR_400_RESPONSE) 
    195       rescue Errno::EMFILE 
     153      rescue Errno::EMFILE => e 
     154        Mongrel.log(:debug, e.message) 
    196155        reap_dead_workers('too many files') 
    197156      rescue Object => e 
    198         Mongrel.log(:error, "#{Time.now.httpdate}: Read error: #{e.inspect}") 
     157        Mongrel.log(:error, "Read error: #{e.inspect}") 
    199158        Mongrel.log(:error, e.backtrace.join("\n")) 
    200159      ensure 
    201160        begin 
    202161          client.close 
    203         rescue IOError 
     162        rescue IOError => e 
     163          Mongrel.log(:debug, e.message) 
    204164          # Already closed 
    205165        rescue Object => e 
    206           Mongrel.log(:error, "#{Time.now.httpdate}: Client error: #{e.inspect}") 
     166          Mongrel.log(:error, "Client error: #{e.inspect}") 
    207167          Mongrel.log(:error, e.backtrace.join("\n")) 
    208168        end 
    209169        request.body.delete if request and request.body.class == Tempfile 
    210170      end 
    211171    end 
    212172 
     173    def reap_dead_workers(reason=nil) 
     174      # override in subclass! 
     175    end 
     176 
     177    def configure_socket_options 
     178      case RUBY_PLATFORM 
     179      when /linux/ 
     180        # 9 is currently TCP_DEFER_ACCEPT 
     181        $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1] 
     182        $tcp_cork_opts = [Socket::SOL_TCP, 3, 1] 
     183      when /freebsd(([1-4]\..{1,2})|5\.[0-4])/ 
     184        # Do nothing, just closing a bug when freebsd <= 5.4 
     185      when /freebsd/ 
     186        # Use the HTTP accept filter if available. 
     187        # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg 
     188        unless `/sbin/sysctl -nq net.inet.accf.http`.empty? 
     189          $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')] 
     190        end 
     191      end 
     192    end 
     193 
     194    # Runs the thing.  It returns the thread used so you can "join" it.  You can also 
     195    # access the HttpServer::acceptor attribute to get the thread later. 
     196    def run 
     197      raise RuntimeError, "This method must be overridden by a subclass" 
     198    end 
     199 
     200    # Simply registers a handler with the internal URIClassifier.  When the URI is 
     201    # found in the prefix of a request then your handler's HttpHandler::process method 
     202    # is called.  See Mongrel::URIClassifier#register for more information. 
     203    # 
     204    # If you set in_front=true then the passed in handler will be put in the front of the list 
     205    # for that particular URI. Otherwise it's placed at the end of the list. 
     206    def register(uri, handler, in_front=false) 
     207      begin 
     208        @classifier.register(uri, [handler]) 
     209      rescue URIClassifier::RegistrationError 
     210        handlers = @classifier.resolve(uri)[2] 
     211        method_name = in_front ? 'unshift' : 'push' 
     212        handlers.send(method_name, handler) 
     213      end 
     214      handler.listener = self 
     215    end 
     216 
     217    # Removes any handlers registered at the given URI.  See Mongrel::URIClassifier#unregister 
     218    # for more information.  Remember this removes them *all* so the entire 
     219    # processing chain goes away. 
     220    def unregister(uri) 
     221      @classifier.unregister(uri) 
     222    end 
     223 
     224    def stop(synchronous=false) 
     225      #Mongrel.log(:debug, "stop invoked for #{self.inspect}") 
     226      # this appears to get invoked once for each signal, but after 
     227      # the first signal the 'acceptor' appear to be nil. WTF? 
     228      return if @acceptor.nil? 
     229      @acceptor.raise(StopServer.new) 
     230 
     231      if synchronous 
     232        sleep(0.5) while @acceptor.alive? 
     233      end 
     234    end 
     235 
     236  end 
     237 
     238  # This is the main driver of Mongrel, while the Mongrel::HttpParser and Mongrel::URIClassifier 
     239  # make up the majority of how the server functions.  It's a very simple class that just 
     240  # has a thread accepting connections and a simple HttpServer.process_client function 
     241  # to do the heavy lifting with the IO and Ruby.   
     242  # 
     243  # You use it by doing the following: 
     244  # 
     245  #   server = 2("0.0.0.0", 3000) 
     246  #   server.register("/stuff", MyNiftyHandler.new) 
     247  #   server.run.join 
     248  # 
     249  # The last line can be just server.run if you don't want to join the thread used. 
     250  # If you don't though Ruby will mysteriously just exit on you. 
     251  # 
     252  # Ruby's thread implementation is "interesting" to say the least.  Experiments with 
     253  # *many* different types of IO processing simply cannot make a dent in it.  Future 
     254  # releases of Mongrel will find other creative ways to make threads faster, but don't 
     255  # hold your breath until Ruby 1.9 is actually finally useful. 
     256  class HttpServer < Server 
     257    attr_reader :workers 
     258    attr_reader :throttle 
     259    attr_reader :timeout 
     260    attr_reader :num_processors 
     261 
     262    # Creates a working server on host:port (strange things happen if port isn't a Number). 
     263    # Use HttpServer::run to start the server and HttpServer.acceptor.join to  
     264    # join the thread that's processing incoming requests on the socket. 
     265    # 
     266    # The num_processors optional argument is the maximum number of concurrent 
     267    # processors to accept, anything over this is closed immediately to maintain 
     268    # server processing performance.  This may seem mean but it is the most efficient 
     269    # way to deal with overload.  Other schemes involve still parsing the client's request 
     270    # which defeats the point of an overload handling system. 
     271    #  
     272    # The throttle parameter is a sleep timeout (in hundredths of a second) that is placed between  
     273    # socket.accept calls in order to give the server a cheap throttle time.  It defaults to 0 and 
     274    # actually if it is 0 then the sleep is not done at all. 
     275    def initialize(host, port, num_processors=950, throttle=0, timeout=60, log=nil, log_level=:debug) 
     276       
     277      @host   = host 
     278      @port   = port 
     279      @domain = "tcp" 
     280 
     281      super(log, log_level) 
     282 
     283      @socket   = TCPServer.new(host, port)  
     284      @workers  = ThreadGroup.new 
     285      @throttle = throttle 
     286      @timeout  = timeout 
     287      @num_processors = num_processors 
     288 
     289    end 
     290     
    213291    # Used internally to kill off any worker threads that have taken too long 
    214292    # to complete processing.  Only called if there are too many processors 
    215293    # currently servicing.  It returns the count of workers still active 
    216294    # after the reap is done.  It only runs if there are workers to reap. 
    217295    def reap_dead_workers(reason='unknown') 
     296 
    218297      if @workers.list.length > 0 
    219         Mongrel.log(:error, "#{Time.now.httpdate}: Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'") 
    220         error_msg = "#{Time.now.httpdate}: Mongrel timed out this thread: #{reason}" 
     298 
     299        Mongrel.log(:error, "Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'") 
     300        error_msg = "Mongrel timed out this thread: #{reason}" 
    221301        mark = Time.now 
    222302        @workers.list.each do |worker| 
    223303          worker[:started_on] = Time.now if not worker[:started_on] 
    224304 
    225305          if mark - worker[:started_on] > @timeout + @throttle 
    226             Mongrel.log(:error, "#{Time.now.httpdate}: Thread #{worker.inspect} is too old, killing.") 
     306            Mongrel.log(:error, "Thread #{worker.inspect} is too old, killing.") 
    227307            worker.raise(TimeoutError.new(error_msg)) 
    228308          end 
    229309        end 
     310 
    230311      end 
    231312 
    232       return @workers.list.length 
     313      @workers.list.length 
    233314    end 
    234315 
    235316    # Performs a wait on all the currently running threads and kills any that take 
     
    238319    # that much longer. 
    239320    def graceful_shutdown 
    240321      while reap_dead_workers("shutdown") > 0 
    241         Mongrel.log(:error, "#{Time.now.httpdate}: Waiting for #{@workers.list.length} requests to finish, could take #{@timeout + @throttle} seconds.") 
     322        Mongrel.log(:error, "Waiting for #{@workers.list.length} requests to finish, could take #{@timeout + @throttle} seconds.") 
    242323        sleep @timeout / 10 
    243324      end 
    244325    end 
    245326 
    246     def configure_socket_options 
    247       case RUBY_PLATFORM 
    248       when /linux/ 
    249         # 9 is currently TCP_DEFER_ACCEPT 
    250         $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1] 
    251         $tcp_cork_opts = [Socket::SOL_TCP, 3, 1] 
    252       when /freebsd(([1-4]\..{1,2})|5\.[0-4])/ 
    253         # Do nothing, just closing a bug when freebsd <= 5.4 
    254       when /freebsd/ 
    255         # Use the HTTP accept filter if available. 
    256         # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg 
    257         unless `/sbin/sysctl -nq net.inet.accf.http`.empty? 
    258           $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')] 
    259         end 
    260       end 
    261     end 
    262      
    263327    # Runs the thing.  It returns the thread used so you can "join" it.  You can also 
    264328    # access the HttpServer::acceptor attribute to get the thread later. 
    265329    def run 
     
    279343 
    280344              num_workers = @workers.list.length 
    281345              if num_workers >= @num_processors 
    282                 Mongrel.log(:error, "#{Time.now.httpdate}: Server overloaded with #{num_workers} processors (#@num_processors max). Dropping connection.") 
     346                Mongrel.log(:error, "Server overloaded with #{num_workers} processors (#@num_processors max). Dropping connection.") 
    283347                client.close rescue nil 
    284348                reap_dead_workers("max processors") 
    285349              else 
     
    301365              # client closed the socket even before accept 
    302366              client.close rescue nil 
    303367            rescue Object => e 
    304               Mongrel.log(:error, "#{Time.now.httpdate}: Unhandled listen loop exception #{e.inspect}.") 
     368              Mongrel.log(:error, "Unhandled listen loop exception #{e.inspect}.") 
    305369              Mongrel.log(:error, e.backtrace.join("\n")) 
    306370            end 
    307371          end 
    308372          graceful_shutdown 
    309373        ensure 
    310374          @socket.close 
    311           # Mongrel.log(:error, "#{Time.now.httpdate}: Closed socket.") 
    312375        end 
    313376      end 
    314377 
    315       return @acceptor 
     378      @acceptor 
    316379    end 
     380  end 
    317381 
    318     # Simply registers a handler with the internal URIClassifier.  When the URI is 
    319     # found in the prefix of a request then your handler's HttpHandler::process method 
    320     # is called.  See Mongrel::URIClassifier#register for more information. 
    321     # 
    322     # If you set in_front=true then the passed in handler will be put in the front of the list 
    323     # for that particular URI. Otherwise it's placed at the end of the list. 
    324     def register(uri, handler, in_front=false) 
    325       begin 
    326         @classifier.register(uri, [handler]) 
    327       rescue URIClassifier::RegistrationError 
    328         handlers = @classifier.resolve(uri)[2] 
    329         method_name = in_front ? 'unshift' : 'push' 
    330         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     = '127.0.0.1' 
     389      @domain   = 'unix' 
     390      @port     = 'anonymous' 
     391      super(log, log_level) 
     392 
     393      @tcpsocket = 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 = [@tcpsocket] 
     403    end 
     404 
     405    def runchild(server) 
     406      BasicSocket.do_not_reverse_lookup = true 
     407      configure_socket_options 
     408 
     409      Mongrel.log(:notice, "New child server started, process #{$$}") 
     410      @acceptor = Thread.new do 
     411        begin 
     412          while not @terminate 
     413            begin 
     414              server.write("READY #{$$}\n") 
     415              server.flush 
     416 
     417              client = server.recv_io(TCPSocket) # read the client's file descriptor from the server! 
     418              unless client.is_a?(IO) 
     419                Mongrel.log(:error, "Child doesn't know how to handle object #{client.class}: #{client.inspect}") 
     420                next 
     421              end 
     422              if client.respond_to?(:setsockopt)  and defined?($tcp_cork_opts) and $tcp_cork_opts 
     423                client.setsockopt(*$tcp_cork_opts) rescue nil 
     424              end 
     425 
     426              process_client(client) 
     427            rescue StopServer 
     428              Mongrel.log(:debug, "server #{$$} received StopServer command") 
     429              @terminate = true 
     430            rescue Errno::ECONNABORTED 
     431              Mongrel.log(:debug, "server #{$$} connection aborted") 
     432              client.close rescue nil 
     433            rescue Errno::EPIPE 
     434              # server broke connection 
     435              Mongrel.log(:debug, "server #{$$} broken pipe") 
     436              @terminate = true 
     437            rescue Object => e 
     438              @terminte = true 
     439              Mongrel.log(:error, "Unhandled listen loop exception #{e.inspect}.") 
     440              Mongrel.log(:error, e.backtrace.join("\n")) 
     441            end 
     442          end 
     443        ensure 
     444          server.write("CLOSED #{$$}\n") rescue nil 
     445          server.close rescue nil 
     446        end 
     447        Mongrel.log(:notice, "child server stopped, process #{$$}") 
    331448      end 
    332       handler.listener = self 
     449 
     450      @acceptor 
    333451    end 
    334452 
    335     # Removes any handlers registered at the given URI.  See Mongrel::URIClassifier#unregister 
    336     # for more information.  Remember this removes them *all* so the entire 
    337     # processing chain goes away. 
    338     def unregister(uri) 
    339       @classifier.unregister(uri) 
     453    def start_child 
     454      cio,sio = UNIXSocket::socketpair 
     455      pid = fork 
     456      if pid.nil? # child 
     457        @close_on_fork << sio 
     458        begin 
     459          @close_on_fork.each { |io| io.close rescue nil } 
     460          runchild(cio).join 
     461          Kernel.exit! 0 
     462        rescue StopServer 
     463          Kernel.exit! 0 
     464        rescue Object => e 
     465          Mongrel.log(:error, "Unhandled listen loop exception #{e.inspect}.") 
     466          Mongrel.log(:error, e.backtrace.join("\n")) 
     467        end 
     468        Kernel.exit! 1 
     469      end 
     470      cio.close rescue nil 
     471      @close_on_fork << sio 
     472      [pid,sio] 
    340473    end 
    341474 
    342     # Stops the acceptor thread and then causes the worker threads to finish 
    343     # off the request queue before finally exiting. 
    344     def stop(synchronous=false) 
    345       @acceptor.raise(StopServer.new) 
     475    def evict_child(pid) 
     476      if @children.has_key?(pid) 
     477        io = @children.delete(pid) 
     478        if io.respond_to?(:close) 
     479          io.close rescue nil 
     480        end 
     481      end 
     482      if @busy.has_key?(pid) 
     483        io = @busy.delete(pid) 
     484        if io.respond_to?(:close) 
     485          io.close rescue nil 
     486        end 
     487      end 
     488      begin 
     489        Process.kill("TERM", pid)  
     490        Process.waitpid(pid) 
     491      rescue Errno::ESRCH,Errno::ECHILD 
     492        # do nothing 
     493      rescue Object => e 
     494        Mongrel.log(:error, "Unhandled exception #{e.inspect}.") 
     495        Mongrel.log(:error, e.backtrace.join("\n")) 
     496      end 
     497    end 
    346498 
    347       if synchronous 
    348         sleep(0.5) while @acceptor.alive? 
     499    def run 
     500      BasicSocket.do_not_reverse_lookup = true 
     501      configure_socket_options 
     502 
     503      # start up the necessary number of children 
     504      # 
     505      (0...@min_children).each do 
     506        pid, socket = start_child 
     507        @children[pid] = socket 
     508        @busy[pid]     = true 
    349509      end 
     510 
     511      @acceptor = Thread.new do 
     512        begin 
     513          while not @terminate 
     514            begin 
     515              @children.each_key do |pid| 
     516                begin 
     517                  do_evict = false 
     518                  do_evict = true if @children[pid].closed? or Process.waitpid(pid, Process::WNOHANG)  
     519                rescue Errno::ECHILD 
     520                  do_evice = true 
     521                rescue Object => e 
     522                  Mongrel.log(:error, "Unhandled exception #{e.inspect}.") 
     523                  Mongrel.log(:error, e.backtrace.join("\n")) 
     524                end 
     525 
     526                evict_child(pid) if do_evict 
     527              end 
     528 
     529              readfds = [ @tcpsocket ] + @children.values  
     530              r,w,e  = Kernel.select(readfds, nil, nil, 60) 
     531 
     532              # check to see if anyone wants to talk to us. If no one is talking 
     533              # and we have more than the necessary number of children then signal 
     534              # one to terminate. 
     535              # 
     536              evict_child(@children.keys.first) if (r.nil? or r.empty?) and @children.length > @min_children 
     537              next if r.nil? 
     538 
     539              # OK, someone is talking. Find out who and process the client. 
     540              # Delay the processing of the TCP server socket until all the children 
     541              # are finished. This allows a client to become available before processing 
     542              # the HTTP connection. 
     543              # 
     544              flag_http = false 
     545              r.each do |io| 
     546                if io == @tcpsocket # a new connection from apache or the outside world 
     547                  flag_http = true  # defer 
     548                else                # one of the children 
     549                  begin 
     550                    msg = io.readline 
     551                    msg.chomp! 
     552                    if msg =~ /^READY\s+(\d+)$/ 
     553                      pid = $1.to_i 
     554                      if @busy[pid].respond_to?(:close) 
     555                        @busy[pid].close rescue nil 
     556                      end 
     557                      @busy[pid] = false 
     558                    elsif msg =~ /CLOSED\s+(\d+)/ 
     559                      evict_child($1.to_i) 
     560                    end 
     561                  rescue EOFError => ex 
     562                    Mongrel.log(:error,ex.message) 
     563                    @children.each_key do |pid| 
     564                      evict_child(pid) if @children[pid] == io 
     565                    end 
     566                  end 
     567                end 
     568              end 
     569 
     570              next unless flag_http 
     571 
     572              client = @tcpsocket.accept 
     573              @busy.each_pair do |pid,busy| 
     574                unless busy 
     575                  io = @children[pid] 
     576                  io.send_io(client) 
     577                  io.flush 
     578 
     579                  @busy[pid] = client 
     580                  client = nil 
     581                  break 
     582                end 
     583              end 
     584 
     585              if client and not @max_children.nil? and @max_children == @children.length 
     586                Mongrel.log(:error, "Maximum number of thread exceeded, closing request") 
     587                client.close rescue nil 
     588                client = nil 
     589              end 
     590 
     591              next unless client 
     592 
     593              Mongrel.log(:debug, "No avaialble child found, spinning up a new child") 
     594 
     595              # spin up a new one 
     596              # 
     597              pid, socket = start_child 
     598              @children[pid] = socket 
     599              socket.readline 
     600              socket.send_io(client) 
     601              socket.flush 
     602              @busy[pid] = client 
     603 
     604            rescue StopServer 
     605              @terminate = true 
     606            rescue Object => e 
     607              Mongrel.log(:error, "Unhandled listen loop exception #{e.inspect}.") 
     608              Mongrel.log(:error, e.backtrace.join("\n")) 
     609            end 
     610          end 
     611        ensure 
     612          @tcpsocket.close  rescue nil 
     613          @children.each_key { |pid| evict_child(pid) } 
     614        end 
     615      end # end thread 
     616 
     617      @acceptor 
    350618    end 
    351  
    352619  end 
    353620end 
    354621 
     
    357624 
    358625$LOAD_PATH.unshift 'projects/mongrel_experimental/lib/' 
    359626Mongrel::Gems.require 'mongrel_experimental', ">=#{Mongrel::Const::MONGREL_VERSION}" 
     627 
     628# vim:expandtab:shiftwidth=2:tabstop=2 
  • bin/mongrel_rails

    old new  
    11# Copyright (c) 2005 Zed A. Shaw 
     2# Copyright (c) 2008 Raritan Computer, Inc (Brian Weaver <brian.weaver@raritan.com>) 
    23# You can redistribute it and/or modify it under the same terms as Ruby. 
    34# 
    45# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html 
     
    2728        ['-p', '--port PORT', "Which port to bind to", :@port, 3000], 
    2829        ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"], 
    2930        ['-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'], 
    3032        ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"], 
    3133        ['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_processors, 1024], 
    3234        ['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, 60], 
     
    3840        ['-C', '--config PATH', "Use a config file", :@config_file, nil], 
    3941        ['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil], 
    4042        ['-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], 
    4146        ['', '--user USER', "User to run as", :@user, nil], 
    4247        ['', '--group GROUP', "Group to run as", :@group, nil], 
    4348        ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil] 
     
    6974      valid_user? @user if @user 
    7075      valid_group? @group if @group 
    7176 
     77      @log_level = @log_level.to_sym unless @log_level.nil? or @log_level.is_a?(Symbol) 
     78 
    7279      return @valid 
    7380    end 
    7481 
     
    95102          Mongrel.log("Settings loaded from #{@config_file} (they override command line).") if @config_file 
    96103        end 
    97104 
    98         Mongrel.log("Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}, further information can be found in log/mongrel-#{defaults[:host]}-#{defaults[:port]}.log") 
     105        Mongrel.log("Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}, further information can be found in #{defaults[:log_file]}") 
    99106 
    100107        listener do 
    101108          mime = {} 
     
    180187 
    181188    def config_keys 
    182189      @config_keys ||= 
    183         %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors num_procs timeout throttle user group prefix
     190        %w(address host port cwd log_file log_level pid_file environment docroot mime_map daemon debug includes config_script num_processors num_procs timeout throttle user group prefix unixmaster min_children max_children
    184191    end 
    185192 
    186193    def settings 
     
    282289if not Mongrel::Command::Registry.instance.run ARGV 
    283290  exit 1 
    284291end 
     292 
     293# vim:shiftwidth=2:tabstop=2:expandtab:ft=ruby