lib/ionian/socket.rb in ionian-0.5.0 vs lib/ionian/socket.rb in ionian-0.6.0

- old
+ new

@@ -1,12 +1,15 @@ require 'ionian/extension/socket' module Ionian - # A convenient wrapper for TCP, UDP, and Unix sockets. + # A convenient wrapper for TCP, UDP, and Unix client sockets. class Socket + attr_accessor :expression + # Creates a new socket or wraps an existing socket. + # # Args: # host: IP or hostname to connect to. # port: Connection's port number. Default is 23. Unused by :unix protocol. # protocol: Type of socket to create. :tcp, :udp, :unix. Default is :tcp. # :udp will be automatically selected for addresses in the multicast range. @@ -18,41 +21,74 @@ # reuse_addr: Set true to enable the SO_REUSEADDR flag. Allows local address reuse. # no_delay: Set true to enable the TCP_NODELAY flag. Disables Nagle algorithm. # cork: Set true to enable the TCP_CORK flag. Buffers multiple writes # into one segment. # expression: Overrides the #read_match regular expression for received data. - def initialize(**kwargs) - @socket = nil + def initialize(existing_socket = nil, **kwargs) + @socket = existing_socket - # TODO: Should be able to parse the port out of host. - # :port should override this parsed value. + @ionian_listeners = [] - @host = kwargs.fetch :host - @port = kwargs.fetch :port, 23 - @bind_port = kwargs.fetch :bind_port, @port + @expression = kwargs.fetch :expression, nil - # Automatically select UDP for the multicast range. Otherwise default to TCP. - default_protocol = :tcp - default_protocol = :udp if Ionian::Extension::Socket.multicast? @host - default_protocol = :unix if @host.start_with? '/' + if existing_socket + # Convert existing socket. + @socket.extend Ionian::Extension::IO + @socket.extend Ionian::Extension::Socket + + if existing_socket.is_a? UNIXSocket + @host = existing_socket.path + @port = nil + else + @host = existing_socket.remote_address.ip_address if existing_socket + @port = existing_socket.remote_address.ip_port if existing_socket + end - @protocol = kwargs.fetch :protocol, default_protocol - @persistent = kwargs.fetch :persistent, true - @expression = kwargs.fetch :expression, nil + if @socket.is_a? TCPSocket + @protocol = :tcp + elsif @socket.is_a? UDPSocket + @protocol = :udp + elsif @socket.is_a? UNIXSocket + @protocol = :unix + end + + @persistent = true # Existing sockets are always persistent. + + @socket.expression = @expression if @expression + + initialize_socket_methods + else + # Initialize new socket. + + # TODO: Should be able to parse the port out of host. + # :port should override this parsed value. + + @host = kwargs.fetch :host + @port = kwargs.fetch :port, 23 + @bind_port = kwargs.fetch :bind_port, @port - @reuse_addr = kwargs.fetch :reuse_addr, false - @no_delay = kwargs.fetch :no_delay, false - @cork = kwargs.fetch :cork, false + # Automatically select UDP for the multicast range. Otherwise default to TCP. + default_protocol = :tcp + default_protocol = :udp if Ionian::Extension::Socket.multicast? @host + default_protocol = :unix if @host.start_with? '/' + + @protocol = kwargs.fetch :protocol, default_protocol + @persistent = kwargs.fetch :persistent, true + + @reuse_addr = kwargs.fetch :reuse_addr, false + @no_delay = kwargs.fetch :no_delay, false + @cork = kwargs.fetch :cork, false - create_socket if @persistent + + create_socket if @persistent + end end # Returns a symbol of the type of protocol this socket uses: # :tcp, :udp, :unix - def protocol? - @protocol - end + attr_reader :protocol + alias_method :protocol?, :protocol # Returns true if the socket remains open after writing data. def persistent? @persistent == false || @persistent == nil ? false : true end @@ -76,11 +112,29 @@ @socket.close unless @persistent matches end + # Register a block to be called when #run_match receives matched data. + # Method callbacks can be registered with &object.method(:method). + # Returns a reference to the given block. + # block = ionian_socket.register_observer {...} + def register_observer &block + @ionian_listeners << block unless @ionian_listeners.include? block + @socket.register_observer &block if @socket + block + end + alias_method :on_match, :register_observer + + # Unregister a block from being called when matched data is received. + def unregister_observer(&block) + @ionian_listeners.delete_if {|o| o == block} + @socket.unregister_observer &block if @socket + block + end + ### Methods Forwarded To @socket ### # Returns true if there is data in the receive buffer. # Args: # Timeout: Number of seconds to wait for data until @@ -143,10 +197,11 @@ case @protocol when :tcp @socket = ::TCPSocket.new @host, @port @socket.extend Ionian::Extension::Socket + @socket.expression = @expression if @expression @socket.no_delay = true if @no_delay @socket.cork = true if @cork when :udp @socket = ::UDPSocket.new @@ -167,9 +222,12 @@ # TODO: Implement SO_LINGER flag for non-persistent sockets; # especially send-and-forget. @socket.expression = @expression if @expression + + # Register listeners. + @ionian_listeners.each { |proc| @socket.on_match &proc } initialize_socket_methods end # Expose the @socket methods that haven't been defined by this class. \ No newline at end of file