Class: Raptor::Binder
- Inherits:
-
Object
- Object
- Raptor::Binder
- Defined in:
- lib/raptor/binder.rb,
sig/generated/raptor/binder.rbs
Overview
Manages binding to network addresses and creating listening sockets.
Binder handles parsing URI bind specifications, creating TCP, Unix, and SSL server sockets, and managing socket options for optimal performance. It supports binding to multiple addresses simultaneously.
Defined Under Namespace
Classes: SslListener, UnknownBindSchemeError
Constant Summary collapse
- SOCKET_BACKLOG =
1024
Instance Attribute Summary collapse
-
#listeners ⇒ Array<TCPServer, UNIXServer, SslListener>
readonly
Array of listening sockets.
Instance Method Summary collapse
-
#addresses ⇒ Array<String>
Returns the bound addresses as strings.
-
#close ⇒ void
Closes all listening sockets.
-
#create_ssl_listeners(host, port, ssl_params) ⇒ Array<SslListener>
Creates SSL server sockets for the given host, port, and SSL parameters.
-
#create_tcp_listeners(host, port) ⇒ Array<TCPServer>
Creates TCP server sockets for the given host and port.
-
#create_unix_listeners(path) ⇒ Array<UNIXServer>
Creates a Unix domain server socket at the given path.
-
#initialize(bind_uris) ⇒ Binder
constructor
Creates a new Binder with the specified bind URIs.
-
#loopback_addresses ⇒ Array<String>
Returns all available loopback IP addresses.
-
#parse ⇒ void
Parses bind URIs and creates listening sockets.
-
#server_port ⇒ Integer
Returns the port number of the first TCP or SSL listener.
Constructor Details
#initialize(bind_uris) ⇒ Binder
Creates a new Binder with the specified bind URIs.
Parses the provided bind URIs and creates listening sockets for each one. Supports tcp://, unix://, and ssl:// schemes. Localhost is expanded to all available loopback addresses (both IPv4 and IPv6).
80 81 82 83 84 |
# File 'lib/raptor/binder.rb', line 80 def initialize(bind_uris) @bind_uris = bind_uris @listeners = nil parse end |
Instance Attribute Details
#listeners ⇒ Array<TCPServer, UNIXServer, SslListener> (readonly)
Array of listening sockets.
64 65 66 |
# File 'lib/raptor/binder.rb', line 64 def listeners @listeners end |
Instance Method Details
#addresses ⇒ Array<String>
Returns the bound addresses as strings.
TCP listeners are returned as "host:port", Unix listeners as the socket path, and SSL listeners as "ssl://host:port".
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/raptor/binder.rb', line 97 def addresses @listeners.map do |listener| case listener when UNIXServer listener.path when SslListener address = listener.local_address "ssl://#{address.ip_address}:#{address.ip_port}" else address = listener.local_address "#{address.ip_address}:#{address.ip_port}" end end end |
#close ⇒ void
This method returns an undefined value.
Closes all listening sockets.
132 133 134 |
# File 'lib/raptor/binder.rb', line 132 def close @listeners.each(&:close) end |
#create_ssl_listeners(host, port, ssl_params) ⇒ Array<SslListener>
Creates SSL server sockets for the given host, port, and SSL parameters.
Wraps each TCP listener with an SSL context to produce SslListener objects. The ssl_params hash must include "cert" and "key" entries pointing to the certificate and private key files respectively.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/raptor/binder.rb', line 221 def create_ssl_listeners(host, port, ssl_params) require "openssl" tcp_servers = create_tcp_listeners(host, port) ssl_context = OpenSSL::SSL::SSLContext.new ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_params["cert"])) ssl_context.key = OpenSSL::PKey.read(File.read(ssl_params["key"])) ssl_context.alpn_protocols = ["h2", "http/1.1"] ssl_context.alpn_select_cb = ->(protocols) { protocols.include?("h2") ? "h2" : "http/1.1" } ssl_context.freeze tcp_servers.map { |tcp_server| SslListener.new(tcp_server: tcp_server, ssl_context: ssl_context) } end |
#create_tcp_listeners(host, port) ⇒ Array<TCPServer>
Creates TCP server sockets for the given host and port.
167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/raptor/binder.rb', line 167 def create_tcp_listeners(host, port) if host == "localhost" return loopback_addresses.map { |address| create_tcp_listeners(address, port) }.flatten end host = host[1..-2] if host&.start_with?("[") tcp_server = TCPServer.new(host, port) tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) tcp_server.listen SOCKET_BACKLOG [tcp_server] end |
#create_unix_listeners(path) ⇒ Array<UNIXServer>
Creates a Unix domain server socket at the given path.
Removes stale socket files left by crashed processes (when the socket is not currently in use). Registers an at_exit hook to clean up the socket file on normal process exit.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/raptor/binder.rb', line 192 def create_unix_listeners(path) if File.exist?(path) begin UNIXSocket.new(path).close raise "Socket #{path.inspect} is already in use" rescue Errno::ECONNREFUSED File.delete(path) end end server = UNIXServer.new(path) master_pid = Process.pid at_exit { File.delete(path) rescue nil if Process.pid == master_pid } [server] end |
#loopback_addresses ⇒ Array<String>
Returns all available loopback IP addresses.
241 242 243 244 245 246 247 |
# File 'lib/raptor/binder.rb', line 241 def loopback_addresses Socket.ip_address_list.filter_map do |addrinfo| next unless addrinfo.ipv4_loopback? || addrinfo.ipv6_loopback? addrinfo.ip_address end.tap(&:uniq!) end |
#parse ⇒ void
This method returns an undefined value.
Parses bind URIs and creates listening sockets.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/raptor/binder.rb', line 144 def parse @listeners = @bind_uris.map do |bind_uri| uri = URI.parse(bind_uri) case uri.scheme when "tcp" create_tcp_listeners(uri.host, uri.port) when "unix" create_unix_listeners(uri.path) when "ssl" create_ssl_listeners(uri.host, uri.port, URI.decode_www_form(uri.query || "").to_h) else raise UnknownBindSchemeError.new(uri.scheme) end end.tap(&:flatten!) end |
#server_port ⇒ Integer
Returns the port number of the first TCP or SSL listener.
Used to populate SERVER_PORT in the Rack environment. Returns 0 if no TCP or SSL listener is configured (e.g., Unix socket only).
120 121 122 123 124 125 |
# File 'lib/raptor/binder.rb', line 120 def server_port tcp_listener = @listeners.find { |listener| listener.is_a?(TCPServer) || listener.is_a?(SslListener) } return 0 unless tcp_listener tcp_listener.local_address.ip_port end |