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).
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
- 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
-
#handler ⇒ Object
readonly
Returns the value of attribute handler.
-
#parser ⇒ Object
readonly
Returns the value of attribute parser.
-
#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
- #connected? ⇒ Boolean
-
#env ⇒ Object
Note that the socket.read(8) assumes we’re using the hixie-76 parser.
-
#initialize(port = nil, timeout = nil) ⇒ WebSocketServer
constructor
A new instance of WebSocketServer.
-
#receive ⇒ Object
Block until the next message is available from the Web Socket.
-
#send(message) ⇒ Object
Send a message and block until there is a response.
- #start_server(port) ⇒ Object
Constructor Details
#initialize(port = nil, timeout = nil) ⇒ WebSocketServer
Returns a new instance of WebSocketServer.
65 66 67 68 69 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 65 def initialize(port = nil, timeout = nil) @timeout = timeout @parser = Http::Parser.new @server = start_server(port) end |
Instance Attribute Details
#handler ⇒ Object (readonly)
Returns the value of attribute handler.
62 63 64 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 62 def handler @handler end |
#parser ⇒ Object (readonly)
Returns the value of attribute parser.
62 63 64 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 62 def parser @parser end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
62 63 64 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 62 def port @port end |
#server ⇒ Object (readonly)
Returns the value of attribute server.
62 63 64 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 62 def server @server end |
#socket ⇒ Object (readonly)
Returns the value of attribute socket.
62 63 64 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 62 def socket @socket end |
#timeout ⇒ Object
Returns the value of attribute timeout.
63 64 65 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 63 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.
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 96 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 |
#close ⇒ Object
155 156 157 158 159 160 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 155 def close [server, socket].compact.each do |s| s.close_read s.close_write end end |
#connected? ⇒ Boolean
90 91 92 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 90 def connected? !socket.nil? end |
#env ⇒ Object
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.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 111 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 |
#receive ⇒ Object
Block until the next message is available from the Web Socket. Raises Errno::EWOULDBLOCK if timeout is reached.
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 132 def receive start = Time.now until handler. 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? handler.parse(data) end handler. end |
#send(message) ⇒ Object
Send a message and block until there is a response
147 148 149 150 151 152 153 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 147 def send() accept unless connected? socket.write handler.encode() receive rescue Errno::EWOULDBLOCK raise TimeoutError.new() end |
#start_server(port) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/capybara/poltergeist/web_socket_server.rb', line 75 def start_server(port) time = Time.now begin TCPServer.open(HOST, port || 0) rescue Errno::EADDRINUSE if (Time.now - time) < BIND_TIMEOUT sleep(0.01) retry else raise end end end |