Class: Capybara::Poltergeist::WebSocketServer

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/poltergeist/web_socket_server.rb

Overview

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).

Defined Under Namespace

Classes: FayeHandler

Constant Summary collapse

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)

1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port, timeout = nil) ⇒ WebSocketServer

Returns a new instance of WebSocketServer.



60
61
62
63
64
65
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 60

def initialize(port, timeout = nil)
  @port    = port
  @parser  = Http::Parser.new
  @server  = TCPServer.open(port)
  @timeout = timeout
end

Instance Attribute Details

#handlerObject (readonly)

Returns the value of attribute handler.



57
58
59
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 57

def handler
  @handler
end

#parserObject (readonly)

Returns the value of attribute parser.



57
58
59
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 57

def parser
  @parser
end

#portObject (readonly)

Returns the value of attribute port.



57
58
59
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 57

def port
  @port
end

#serverObject (readonly)

Returns the value of attribute server.



57
58
59
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 57

def server
  @server
end

#socketObject (readonly)

Returns the value of attribute socket.



57
58
59
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 57

def socket
  @socket
end

#timeoutObject

Returns the value of attribute timeout.



58
59
60
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 58

def timeout
  @timeout
end

Instance Method Details

#acceptObject

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



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 73

def accept
  @socket = server.accept

  while msg = socket.gets
    parser << msg
    break if msg == "\r\n"
  end

  @handler = FayeHandler.new(self, env)
  socket.write handler.handshake_response
end

#closeObject



128
129
130
131
132
133
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 128

def close
  [server, socket].each do |s|
    s.close_read
    s.close_write
  end
end

#connected?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 67

def connected?
  !socket.nil?
end

#envObject

Note that the socket.read(8) assumes we’re using the hixie-76 parser. This is fine for now as it corresponds to the version of Web Sockets that the version of WebKit in PhantomJS uses, but it might need to change in the future.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 88

def env
  @env ||= begin
    env = {
      'REQUEST_METHOD' => parser.http_method,
      'SCRIPT_NAME'    => '',
      'PATH_INFO'      => '',
      'QUERY_STRING'   => '',
      'SERVER_NAME'    => '127.0.0.1',
      'SERVER_PORT'    => port.to_s,
      'HTTP_ORIGIN'    => 'http://127.0.0.1:2000/',
      'rack.input'     => StringIO.new(socket.read(8))
    }
    parser.headers.each do |header, value|
      env['HTTP_' + header.upcase.gsub('-', '_')] = value
    end
    env
  end
end

#receiveObject

Block until the next message is available from the Web Socket



108
109
110
111
112
113
114
115
116
117
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 108

def receive
  until handler.message?
    IO.select([socket], [], [], timeout)
    data = socket.recv_nonblock(RECV_SIZE)
    break if data.empty?
    handler.parse(data)
  end

  handler.next_message
end

#send(message) ⇒ Object

Send a message and block until there is a response



120
121
122
123
124
125
126
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 120

def send(message)
  accept unless connected?
  socket.write handler.encode(message)
  receive
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
  raise TimeoutError.new(message)
end