class Capybara::Poltergeist::WebSocketServer

This is a 'custom' Web Socket server that is designed to be synchronous. What this means is that it sends a message, and then waits for a response. It does not expect to receive a message at any other time than right after it has sent a message. So it is basically operating a request/response cycle (which is not how Web Sockets are usually used, but it's what we want here, as we want to send a message to PhantomJS and then wait for it to respond).

Constants

BIND_TIMEOUT

How many seconds to try to bind to the port for before failing

HOST
RECV_SIZE

How much to try to read from the socket at once (it's kinda arbitrary because we just keep reading until we've received a full frame)

Attributes

driver[R]
port[R]
server[R]
socket[R]
timeout[RW]

Public Class Methods

new(port = nil, timeout = nil) click to toggle source
# File lib/capybara/poltergeist/web_socket_server.rb, line 24
def initialize(port = nil, timeout = nil)
  @timeout = timeout
  @server  = start_server(port)
end

Public Instance Methods

accept() click to toggle source

Accept a client on the TCP server socket, then receive its initial HTTP request and use that to initialize a Web Socket.

# File lib/capybara/poltergeist/web_socket_server.rb, line 52
def accept
  @socket   = server.accept
  @messages = []

  @driver = ::WebSocket::Driver.server(self)
  @driver.on(:connect) { |event| @driver.start }
  @driver.on(:message) { |event| @messages << event.data }
end
close() click to toggle source

Closing sockets separately as `close_read`, `close_write` causes IO mistakes on JRuby, using just `close` fixes that.

# File lib/capybara/poltergeist/web_socket_server.rb, line 92
def close
  [server, socket].compact.each(&:close)
end
connected?() click to toggle source
# File lib/capybara/poltergeist/web_socket_server.rb, line 46
def connected?
  !socket.nil?
end
receive() click to toggle source

Block until the next message is available from the Web Socket. Raises Errno::EWOULDBLOCK if timeout is reached.

# File lib/capybara/poltergeist/web_socket_server.rb, line 67
def receive
  start = Time.now

  until @messages.any?
    raise Errno::EWOULDBLOCK if (Time.now - start) >= timeout
    IO.select([socket], [], [], timeout) or raise Errno::EWOULDBLOCK
    data = socket.recv(RECV_SIZE)
    break if data.empty?
    driver.parse(data)
  end

  @messages.shift
end
send(message) click to toggle source

Send a message and block until there is a response

# File lib/capybara/poltergeist/web_socket_server.rb, line 82
def send(message)
  accept unless connected?
  driver.text(message)
  receive
rescue Errno::EWOULDBLOCK
  raise TimeoutError.new(message)
end
start_server(port) click to toggle source
# File lib/capybara/poltergeist/web_socket_server.rb, line 29
def start_server(port)
  time = Time.now

  begin
    TCPServer.open(HOST, port || 0).tap do |server|
      @port = server.addr[1]
    end
  rescue Errno::EADDRINUSE
    if (Time.now - time) < BIND_TIMEOUT
      sleep(0.01)
      retry
    else
      raise
    end
  end
end
write(data) click to toggle source
# File lib/capybara/poltergeist/web_socket_server.rb, line 61
def write(data)
  @socket.write(data)
end