Class: FTW::Server
- Inherits:
-
Object
- Object
- FTW::Server
- Defined in:
- lib/ftw/server.rb
Overview
A web server.
Defined Under Namespace
Classes: InvalidAddress, ServerSetupFailure
Instance Method Summary collapse
-
#each_connection(&block) ⇒ Object
Yield FTW::Connection instances to the block as clients connect.
-
#initialize(addresses) ⇒ Server
constructor
Create a new server listening on the given addresses.
-
#stop ⇒ Object
Stop serving.
Constructor Details
#initialize(addresses) ⇒ Server
Create a new server listening on the given addresses
This method will create, bind, and listen, so any errors during that process be raised as ServerSetupFailure
The parameter ‘addresses’ can be a single string or an array of strings. These strings MUST have the form “address:port”. If the ‘address’ part is missing, it is assumed to be 0.0.0.0
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/ftw/server.rb', line 25 def initialize(addresses) addresses = [addresses] if !addresses.is_a?(Array) dns = FTW::DNS.singleton @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 # Abort if there were failures raise ServerSetupFailure.new(failures) if failures.any? end |
Instance Method Details
#each_connection(&block) ⇒ Object
Yield FTW::Connection instances to the block as clients connect.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/ftw/server.rb', line 93 def each_connection(&block) # TODO(sissel): Select on all sockets # TODO(sissel): Accept and yield to the block while true sockets = @sockets.values read, write, error = IO.select(sockets, nil, nil, nil) read.each do |serversocket| #p serversocket.methods.sort socket, addrinfo = serversocket.accept connection = FTW::Connection.from_io(socket) yield connection end end end |
#stop ⇒ Object
Stop serving.
86 87 88 89 90 |
# File 'lib/ftw/server.rb', line 86 def stop @sockets.each do |name, socket| socket.close end end |