Sha256: 53062573c8aecb71052b417ac072872eaa345bbf5f642c7cd86121231ca81b5f

Contents?: true

Size: 1.95 KB

Versions: 3

Compression:

Stored size: 1.95 KB

Contents

# frozen_string_literal: true

require 'socket'
require 'timeout'

module Sip2
  #
  # Sip2 Non-blocking socket
  # From https://spin.atomicobject.com/2013/09/30/socket-connection-timeout-ruby/
  #
  class NonBlockingSocket < Socket
    DEFAULT_TIMEOUT = 5

    attr_accessor :connection_timeout

    # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
    def self.connect(host:, port:, timeout: DEFAULT_TIMEOUT)
      # Convert the passed host into structures the non-blocking calls can deal with
      addr = Socket.getaddrinfo(host, nil)
      sockaddr = Socket.pack_sockaddr_in(port, addr[0][3])

      NonBlockingSocket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0).tap do |socket|
        socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

        socket.connection_timeout = timeout

        begin
          # Initiate the socket connection in the background. If it doesn't fail
          # immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
          # indicating the connection is in progress.
          socket.connect_nonblock(sockaddr)
        rescue IO::WaitWritable
          # IO.select will block until the socket is writable or the timeout
          # is exceeded - whichever comes first.
          if IO.select(nil, [socket], nil, timeout)
            begin
              # Verify there is now a good connection
              socket.connect_nonblock(sockaddr)
            rescue Errno::EISCONN
              # Good news everybody, the socket is connected!
            rescue StandardError
              # An unexpected exception was raised - the connection is no good.
              socket.close
              raise
            end
          else
            # IO.select returns nil when the socket is not ready before timeout
            # seconds have elapsed
            socket.close
            raise ConnectionTimeout
          end
        end
      end
    end
    # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
  end
end

Version data entries

3 entries across 3 versions & 1 rubygems

Version Path
sip2-0.2.3 lib/sip2/non_blocking_socket.rb
sip2-0.2.2 lib/sip2/non_blocking_socket.rb
sip2-0.2.1 lib/sip2/non_blocking_socket.rb