faster_path 0.0.2

Alternative to Pathname
Documentation
require 'socket'

module SocketSpecs
  # helper to get the hostname associated to 127.0.0.1
  def self.hostname
    # Calculate each time, without caching, since the result might
    # depend on things like do_not_reverse_lookup mode, which is
    # changing from test to test
    Socket.getaddrinfo("127.0.0.1", nil)[0][2]
  end

  def self.hostnamev6
    Socket.getaddrinfo("::1", nil)[0][2]
  end

  def self.addr(which=:ipv4)
    case which
    when :ipv4
      host = "127.0.0.1"
    when :ipv6
      host = "::1"
    end
    Socket.getaddrinfo(host, nil)[0][3]
  end

  def self.find_available_port
    begin
      s = TCPServer.open(0)
      port = s.addr[1]
      s.close
      port
    rescue
      43191
    end
  end

  def self.port
    @@_port ||= find_available_port
  end

  def self.str_port
    port.to_s
  end

  def self.local_port
    @base ||= $$
    @base += 1
    local_port = (@base % (0xffff-1024)) + 1024
    local_port += 1 if local_port == port
    local_port
  end

  def self.sockaddr_in(port, host)
    Socket::SockAddr_In.new(Socket.sockaddr_in(port, host))
  end

  def self.socket_path
    tmp("unix_server_spec.socket", false)
  end

  # TCPServer echo server accepting one connection
  class SpecTCPServer
    def self.start(host=nil, port=nil, logger=nil)
      new(host, port, logger).start
    end

    attr_accessor :hostname, :port, :logger

    def initialize(host=nil, port=nil, logger=nil)
      @hostname = host || SocketSpecs.hostname
      @port = port || SocketSpecs.port
      @logger = logger

      start
    end

    def start
      log "SpecTCPServer starting on #{@hostname}:#{@port}"
      @server = TCPServer.new @hostname, @port

      @thread = Thread.new do
        socket = @server.accept
        log "SpecTCPServer accepted connection: #{socket}"
        service socket
      end
      self
    end

    def service(socket)
      begin
        data = socket.recv(1024)

        return if data.empty?
        log "SpecTCPServer received: #{data.inspect}"

        return if data == "QUIT"

        socket.send data, 0
      ensure
        socket.close
      end
    end

    def shutdown
      log "SpecTCPServer shutting down"
      @thread.join
      @server.close
    end

    def log(message)
      @logger.puts message if @logger
    end
  end
end