This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# encoding: BINARY
|
||||
|
||||
require 'rubygems'
|
||||
require 'rspec'
|
||||
require 'em-spec/rspec'
|
||||
require 'em-http'
|
||||
|
||||
require 'em-websocket'
|
||||
require 'em-websocket-client'
|
||||
|
||||
require 'integration/shared_examples'
|
||||
require 'integration/gte_03_examples'
|
||||
|
||||
RSpec.configure do |c|
|
||||
c.mock_with :rspec
|
||||
end
|
||||
|
||||
class FakeWebSocketClient < EM::Connection
|
||||
attr_reader :handshake_response, :packets
|
||||
|
||||
def onopen(&blk); @onopen = blk; end
|
||||
def onclose(&blk); @onclose = blk; end
|
||||
def onerror(&blk); @onerror = blk; end
|
||||
def onmessage(&blk); @onmessage = blk; end
|
||||
|
||||
def initialize
|
||||
@state = :new
|
||||
@packets = []
|
||||
end
|
||||
|
||||
def receive_data(data)
|
||||
# puts "RECEIVE DATA #{data}"
|
||||
if @state == :new
|
||||
@handshake_response = data
|
||||
@onopen.call if defined? @onopen
|
||||
@state = :open
|
||||
else
|
||||
@onmessage.call(data) if defined? @onmessage
|
||||
@packets << data
|
||||
end
|
||||
end
|
||||
|
||||
def send(application_data)
|
||||
send_frame(:text, application_data)
|
||||
end
|
||||
|
||||
def send_frame(type, application_data)
|
||||
send_data construct_frame(type, application_data)
|
||||
end
|
||||
|
||||
def unbind
|
||||
@onclose.call if defined? @onclose
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def construct_frame(type, data)
|
||||
"\x00#{data}\xff"
|
||||
end
|
||||
end
|
||||
|
||||
class Draft03FakeWebSocketClient < FakeWebSocketClient
|
||||
private
|
||||
|
||||
def construct_frame(type, data)
|
||||
frame = ""
|
||||
frame << EM::WebSocket::Framing03::FRAME_TYPES[type]
|
||||
frame << encoded_length(data.size)
|
||||
frame << data
|
||||
end
|
||||
|
||||
def encoded_length(length)
|
||||
if length <= 125
|
||||
[length].pack('C') # since rsv4 is 0
|
||||
elsif length < 65536 # write 2 byte length
|
||||
"\126#{[length].pack('n')}"
|
||||
else # write 8 byte length
|
||||
"\127#{[length >> 32, length & 0xFFFFFFFF].pack("NN")}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Draft05FakeWebSocketClient < Draft03FakeWebSocketClient
|
||||
private
|
||||
|
||||
def construct_frame(type, data)
|
||||
frame = ""
|
||||
frame << "\x00\x00\x00\x00" # Mask with nothing for simplicity
|
||||
frame << (EM::WebSocket::Framing05::FRAME_TYPES[type] | 0b10000000)
|
||||
frame << encoded_length(data.size)
|
||||
frame << data
|
||||
end
|
||||
end
|
||||
|
||||
class Draft07FakeWebSocketClient < Draft05FakeWebSocketClient
|
||||
private
|
||||
|
||||
def construct_frame(type, data)
|
||||
frame = ""
|
||||
frame << (EM::WebSocket::Framing07::FRAME_TYPES[type] | 0b10000000)
|
||||
# Should probably mask the data, but I get away without bothering since
|
||||
# the server doesn't enforce that incoming frames are masked
|
||||
frame << encoded_length(data.size)
|
||||
frame << data
|
||||
end
|
||||
end
|
||||
|
||||
# Wrapper around em-websocket-client
|
||||
class Draft75WebSocketClient
|
||||
def onopen(&blk); @onopen = blk; end
|
||||
def onclose(&blk); @onclose = blk; end
|
||||
def onerror(&blk); @onerror = blk; end
|
||||
def onmessage(&blk); @onmessage = blk; end
|
||||
|
||||
def initialize
|
||||
@ws = EventMachine::WebSocketClient.connect('ws://127.0.0.1:12345/',
|
||||
:version => 75,
|
||||
:origin => 'http://example.com')
|
||||
@ws.errback { |err| @onerror.call if defined? @onerror }
|
||||
@ws.callback { @onopen.call if defined? @onopen }
|
||||
@ws.stream { |msg| @onmessage.call(msg) if defined? @onmessage }
|
||||
@ws.disconnect { @onclose.call if defined? @onclose }
|
||||
end
|
||||
|
||||
def send(message)
|
||||
@ws.send_msg(message)
|
||||
end
|
||||
|
||||
def close_connection
|
||||
@ws.close_connection
|
||||
end
|
||||
end
|
||||
|
||||
def start_server(opts = {})
|
||||
EM::WebSocket.run({:host => "0.0.0.0", :port => 12345}.merge(opts)) { |ws|
|
||||
yield ws if block_given?
|
||||
}
|
||||
end
|
||||
|
||||
def format_request(r)
|
||||
data = "#{r[:method]} #{r[:path]} HTTP/1.1\r\n"
|
||||
header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
|
||||
data << [header_lines, '', r[:body]].join("\r\n")
|
||||
data
|
||||
end
|
||||
|
||||
def format_response(r)
|
||||
data = r[:protocol] || "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
|
||||
header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
|
||||
data << [header_lines, '', r[:body]].join("\r\n")
|
||||
data
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :succeed_with_upgrade do |response|
|
||||
match do |actual|
|
||||
success = nil
|
||||
actual.callback { |upgrade_response, handler_klass|
|
||||
success = (upgrade_response.lines.sort == format_response(response).lines.sort)
|
||||
}
|
||||
success
|
||||
end
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :fail_with_error do |error_klass, error_message|
|
||||
match do |actual|
|
||||
success = nil
|
||||
actual.errback { |e|
|
||||
success = (e.class == error_klass)
|
||||
success &= (e.message == error_message) if error_message
|
||||
}
|
||||
success
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user