# File lib/ftw/server.rb, line 27 def initialize(addresses) addresses = [addresses] if !addresses.is_a?(Array) dns = FTW::DNS.singleton @control_lock = Mutex.new @sockets = {} failures = [] # address format is assumed to be 'host:port' # TODO(sissel): The split on ":" breaks ipv6 addresses, yo. addresses.each do |address| m = ADDRESS_RE.match(address) if !m raise InvalidAddress.new("Invalid address #{address.inspect}, spected string with format 'host:port'") end host, port = m[1..2] # first capture is host, second capture is port # Permit address being simply ':PORT' host = "0.0.0.0" if host.nil? # resolve each hostname, use the first one that successfully binds. local_failures = [] dns.resolve(host).each do |ip| #family = ip.include?(":") ? Socket::AF_INET6 : Socket::AF_INET #socket = Socket.new(family, Socket::SOCK_STREAM, 0) #sockaddr = Socket.pack_sockaddr_in(port, ip) socket = TCPServer.new(ip, port) #begin #socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) #socket.bind(sockaddr) # If we get here, bind was successful #rescue Errno::EADDRNOTAVAIL => e # TODO(sissel): Record this failure. #local_failures << "Could not bind to #{ip}:#{port}, address not available on this system." #next #rescue Errno::EACCES # TODO(sissel): Record this failure. #local_failures << "No permission to bind to #{ip}:#{port}: #{e.inspect}" #next #end begin socket.listen(100) rescue Errno::EADDRINUSE local_failures << "Address in use, #{ip}:#{port}, cannot listen." next end # Break when successfully listened #p :accept? => socket.respond_to?(:accept) @sockets["#{host}(#{ip}):#{port}"] = socket local_failures.clear break end failures += local_failures end # This allows us to interrupt the #each_connection's select() later # when anyone calls stop() @stopper = IO.pipe # Abort if there were failures raise ServerSetupFailure.new(failures) if failures.any? end