1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
# encoding: BINARY
require 'rubygems'
require 'rspec'
require 'em-spec/rspec'
require 'em-http'
require 'em-websocket'
# Use embedded copy
$:.push 'debian/rubygems/em-websocket-client/lib'
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
|