Class: Capybara::Poltergeist::WebSocketServer
- Inherits:
-
Object
- Object
- Capybara::Poltergeist::WebSocketServer
- 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).
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
- BIND_TIMEOUT =
How many seconds to try to bind to the port for before failing
5
- HOST =
'127.0.0.1'
Instance Attribute Summary collapse
-
#driver ⇒ Object
readonly
Returns the value of attribute driver.
-
#host ⇒ Object
readonly
Returns the value of attribute host.
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#server ⇒ Object
readonly
Returns the value of attribute server.
-
#socket ⇒ Object
readonly
Returns the value of attribute socket.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
Instance Method Summary collapse
-
#accept ⇒ Object
Accept a client on the TCP server socket, then receive its initial HTTP request and use that to initialize a Web Socket.
-
#close ⇒ Object
Closing sockets separately as ‘close_read`, `close_write` causes IO mistakes on JRuby, using just `close` fixes that.
- #connected? ⇒ Boolean
-
#initialize(port = nil, timeout = nil, custom_host = nil) ⇒ WebSocketServer
constructor
A new instance of WebSocketServer.
-
#receive(cmd_id, receive_timeout = nil) ⇒ Object
Block until the next message is available from the Web Socket.
-
#send(cmd_id, message, accept_timeout = nil) ⇒ Object
Send a message and block until there is a response.
- #start_server(port, custom_host) ⇒ Object
- #write(data) ⇒ Object
Constructor Details
#initialize(port = nil, timeout = nil, custom_host = nil) ⇒ WebSocketServer
Returns a new instance of WebSocketServer.
26 27 28 29 30 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 26 def initialize(port = nil, timeout = nil, custom_host = nil) @timeout = timeout @server = start_server(port, custom_host) @receive_mutex = Mutex.new end |
Instance Attribute Details
#driver ⇒ Object (readonly)
Returns the value of attribute driver.
23 24 25 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 23 def driver @driver end |
#host ⇒ Object (readonly)
Returns the value of attribute host.
23 24 25 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 23 def host @host end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
23 24 25 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 23 def port @port end |
#server ⇒ Object (readonly)
Returns the value of attribute server.
23 24 25 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 23 def server @server end |
#socket ⇒ Object (readonly)
Returns the value of attribute socket.
23 24 25 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 23 def socket @socket end |
#timeout ⇒ Object
Returns the value of attribute timeout.
24 25 26 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 24 def timeout @timeout end |
Instance Method Details
#accept ⇒ Object
Accept a client on the TCP server socket, then receive its initial HTTP request and use that to initialize a Web Socket.
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 56 def accept @socket = server.accept @messages = {} @driver = ::WebSocket::Driver.server(self) @driver.on(:connect) { |event| @driver.start } @driver.on(:message) do |event| command_id = JSON.load(event.data)['command_id'] @messages[command_id] = event.data end end |
#close ⇒ Object
Closing sockets separately as ‘close_read`, `close_write` causes IO mistakes on JRuby, using just `close` fixes that.
107 108 109 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 107 def close [server, socket].compact.each(&:close) end |
#connected? ⇒ Boolean
50 51 52 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 50 def connected? !socket.nil? end |
#receive(cmd_id, receive_timeout = nil) ⇒ Object
Block until the next message is available from the Web Socket. Raises Errno::EWOULDBLOCK if timeout is reached.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 74 def receive(cmd_id, receive_timeout=nil) receive_timeout ||= timeout start = Time.now until @messages.has_key?(cmd_id) raise Errno::EWOULDBLOCK if (Time.now - start) >= receive_timeout if @receive_mutex.try_lock begin IO.select([socket], [], [], receive_timeout) or raise Errno::EWOULDBLOCK data = socket.recv(RECV_SIZE) break if data.empty? driver.parse(data) ensure @receive_mutex.unlock end else sleep(0.05) end end @messages.delete(cmd_id) end |
#send(cmd_id, message, accept_timeout = nil) ⇒ Object
Send a message and block until there is a response
97 98 99 100 101 102 103 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 97 def send(cmd_id, , accept_timeout=nil) accept unless connected? driver.text() receive(cmd_id, accept_timeout) rescue Errno::EWOULDBLOCK raise TimeoutError.new() end |
#start_server(port, custom_host) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 32 def start_server(port, custom_host) time = Time.now begin TCPServer.open(custom_host || HOST, port || 0).tap do |server| @port = server.addr[1] @host = server.addr[2] end rescue Errno::EADDRINUSE if (Time.now - time) < BIND_TIMEOUT sleep(0.01) retry else raise end end end |
#write(data) ⇒ Object
68 69 70 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 68 def write(data) @socket.write(data) end |