lib/io/endpoint/shared_endpoint.rb
# frozen_string_literal: true # Released under the MIT License. # Copyright, 2023, by Samuel Williams. require_relative 'generic' require_relative 'composite_endpoint' module IO::Endpoint # Pre-connect and pre-bind sockets so that it can be used between processes. class SharedEndpoint < Generic # Create a new `SharedEndpoint` by binding to the given endpoint. def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false) wrappers = endpoint.bound do |server| # This is somewhat optional. We want to have a generic interface as much as possible so that users of this interface can just call it without knowing a lot of internal details. Therefore, we ignore errors here if it's because the underlying socket does not support the operation. begin server.listen(backlog) rescue Errno::EOPNOTSUPP # Ignore. end server.close_on_exec = close_on_exec end return self.new(endpoint, wrappers) end # Create a new `SharedEndpoint` by connecting to the given endpoint. def self.connected(endpoint, close_on_exec: false) wrapper = endpoint.connect wrapper.close_on_exec = close_on_exec return self.new(endpoint, [wrapper]) end def initialize(endpoint, wrappers, **options) super(**options) @endpoint = endpoint @wrappers = wrappers end attr :endpoint attr :wrappers def local_address_endpoint(**options) endpoints = @wrappers.map do |wrapper| AddressEndpoint.new(wrapper.to_io.local_address) end return CompositeEndpoint.new(endpoints, **options) end def remote_address_endpoint(**options) endpoints = @wrappers.map do |wrapper| AddressEndpoint.new(wrapper.to_io.remote_address) end return CompositeEndpoint.new(endpoints, **options) end # Close all the internal wrappers. def close @wrappers.each(&:close) @wrappers.clear end def bind @wrappers.each do |server| server = server.dup begin yield server ensure server.close end end end def connect @wrappers.each do |peer| peer = peer.dup begin yield peer ensure peer.close end end end def accept(backlog = nil, &block) bind do |server| server.accept_each(&block) end end def to_s "\#<#{self.class} #{@wrappers.size} descriptors for #{@endpoint}>" end end end