#-- # Copyright (C)2007 Tony Arcieri # You can redistribute this under the terms of the Ruby license # See file LICENSE for details #++ require 'socket' module Rev # Listeners wait for incoming connections. When a listener receives a # connection it fires the on_connection event with the newly accepted # socket as a parameter. class Listener < IOWatcher def initialize(listen_socket) @listen_socket = listen_socket super(@listen_socket) end # Returns an integer representing the underlying numeric file descriptor def fileno @listen_socket.fileno end # Close the listener def close detach if attached? @listen_socket.close end # Called whenever the server receives a new connection def on_connection(socket); end event_callback :on_connection ######### protected ######### # Rev callback for handling new connections def on_readable begin on_connection @listen_socket.accept_nonblock rescue Errno::EAGAIN, Errno::ECONNABORTED # EAGAIN can be triggered here if the socket is shared between # multiple processes and a thundering herd is woken up to accept # one connection, only one process will get the connection and # the others will be awoken. # ECONNABORTED is documented in accept() manpages but modern TCP # stacks with syncookies and/or accept()-filtering for DoS # protection do not see it. In any case this error is harmless # and we should instead spend our time with clients that follow # through on connection attempts. end end end class TCPListener < Listener DEFAULT_BACKLOG = 1024 # Create a new Rev::TCPListener on the specified address and port. # Accepts the following options: # # :backlog - Max size of the pending connection queue (default 1024) # :reverse_lookup - Retain BasicSocket's reverse DNS functionality (default false) # # If the specified address is an TCPServer object, it will ignore # the port and :backlog option and create a new Rev::TCPListener out # of the existing TCPServer object. def initialize(addr, port = nil, options = {}) BasicSocket.do_not_reverse_lookup = true unless options[:reverse_lookup] options[:backlog] ||= DEFAULT_BACKLOG listen_socket = if ::TCPServer === addr addr else raise ArgumentError, "port must be an integer" if nil == port ::TCPServer.new(addr, port) end listen_socket.instance_eval { listen(options[:backlog]) } super(listen_socket) end end class UNIXListener < Listener # Create a new Rev::UNIXListener # # Accepts the same arguments as UNIXServer.new # Optionally, it can also take anyn existing UNIXServer object # and create a Rev::UNIXListener out of it. def initialize(*args) super(::UNIXServer === args.first ? args.first : ::UNIXServer.new(*args)) end end end