sonixlabs-em-websocket 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.rdoc +80 -0
  3. data/Gemfile +3 -0
  4. data/README.md +98 -0
  5. data/Rakefile +11 -0
  6. data/em-websocket.gemspec +27 -0
  7. data/examples/echo.rb +8 -0
  8. data/examples/flash_policy_file_server.rb +21 -0
  9. data/examples/js/FABridge.js +604 -0
  10. data/examples/js/WebSocketMain.swf +0 -0
  11. data/examples/js/swfobject.js +4 -0
  12. data/examples/js/web_socket.js +312 -0
  13. data/examples/multicast.rb +47 -0
  14. data/examples/test.html +30 -0
  15. data/lib/em-websocket/client_connection.rb +19 -0
  16. data/lib/em-websocket/close03.rb +11 -0
  17. data/lib/em-websocket/close05.rb +11 -0
  18. data/lib/em-websocket/close06.rb +16 -0
  19. data/lib/em-websocket/close75.rb +10 -0
  20. data/lib/em-websocket/connection.rb +184 -0
  21. data/lib/em-websocket/debugger.rb +17 -0
  22. data/lib/em-websocket/framing03.rb +167 -0
  23. data/lib/em-websocket/framing04.rb +15 -0
  24. data/lib/em-websocket/framing05.rb +168 -0
  25. data/lib/em-websocket/framing07.rb +180 -0
  26. data/lib/em-websocket/framing76.rb +114 -0
  27. data/lib/em-websocket/handler.rb +56 -0
  28. data/lib/em-websocket/handler03.rb +10 -0
  29. data/lib/em-websocket/handler05.rb +10 -0
  30. data/lib/em-websocket/handler06.rb +10 -0
  31. data/lib/em-websocket/handler07.rb +10 -0
  32. data/lib/em-websocket/handler08.rb +10 -0
  33. data/lib/em-websocket/handler13.rb +10 -0
  34. data/lib/em-websocket/handler75.rb +9 -0
  35. data/lib/em-websocket/handler76.rb +12 -0
  36. data/lib/em-websocket/handler_factory.rb +107 -0
  37. data/lib/em-websocket/handshake04.rb +75 -0
  38. data/lib/em-websocket/handshake75.rb +21 -0
  39. data/lib/em-websocket/handshake76.rb +71 -0
  40. data/lib/em-websocket/masking04.rb +63 -0
  41. data/lib/em-websocket/message_processor_03.rb +38 -0
  42. data/lib/em-websocket/message_processor_06.rb +52 -0
  43. data/lib/em-websocket/version.rb +5 -0
  44. data/lib/em-websocket/websocket.rb +45 -0
  45. data/lib/em-websocket.rb +23 -0
  46. data/lib/sonixlabs-em-websocket.rb +1 -0
  47. data/spec/helper.rb +146 -0
  48. data/spec/integration/client_examples.rb +48 -0
  49. data/spec/integration/common_spec.rb +118 -0
  50. data/spec/integration/draft03_spec.rb +270 -0
  51. data/spec/integration/draft05_spec.rb +48 -0
  52. data/spec/integration/draft06_spec.rb +88 -0
  53. data/spec/integration/draft13_spec.rb +75 -0
  54. data/spec/integration/draft75_spec.rb +117 -0
  55. data/spec/integration/draft76_spec.rb +230 -0
  56. data/spec/integration/shared_examples.rb +91 -0
  57. data/spec/unit/framing_spec.rb +325 -0
  58. data/spec/unit/handler_spec.rb +147 -0
  59. data/spec/unit/masking_spec.rb +27 -0
  60. data/spec/unit/message_processor_spec.rb +36 -0
  61. metadata +198 -0
data/spec/helper.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'em-spec/rspec'
4
+ require 'pp'
5
+ require 'em-http'
6
+
7
+ require 'em-websocket'
8
+
9
+ RSpec.configure do |c|
10
+ c.mock_with :rspec
11
+ end
12
+
13
+ class FakeWebSocketClient < EM::Connection
14
+ attr_reader :handshake_response, :packets
15
+
16
+ def onopen(&blk); @onopen = blk; end
17
+ def onclose(&blk); @onclose = blk; end
18
+ def onerror(&blk); @onerror = blk; end
19
+ def onmessage(&blk); @onmessage = blk; end
20
+
21
+ def initialize
22
+ @state = :new
23
+ @packets = []
24
+ end
25
+
26
+ def receive_data(data)
27
+ # puts "RECEIVE DATA #{data}"
28
+ if @state == :new
29
+ @handshake_response = data
30
+ @onopen.call if @onopen
31
+ @state = :open
32
+ else
33
+ @onmessage.call(data) if @onmessage
34
+ @packets << data
35
+ end
36
+ end
37
+
38
+ def send(data)
39
+ send_data("\x00#{data}\xff")
40
+ end
41
+
42
+ def unbind
43
+ @onclose.call if @onclose
44
+ end
45
+ end
46
+
47
+ class Draft03FakeWebSocketClient < FakeWebSocketClient
48
+ def send(application_data)
49
+ frame = ''
50
+ opcode = 4 # fake only supports text frames
51
+ byte1 = opcode # since more, rsv1-3 are 0
52
+ frame << byte1
53
+
54
+ length = application_data.size
55
+ if length <= 125
56
+ byte2 = length # since rsv4 is 0
57
+ frame << byte2
58
+ elsif length < 65536 # write 2 byte length
59
+ frame << 126
60
+ frame << [length].pack('n')
61
+ else # write 8 byte length
62
+ frame << 127
63
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
64
+ end
65
+
66
+ frame << application_data
67
+
68
+ send_data(frame)
69
+ end
70
+ end
71
+
72
+ class Draft07FakeWebSocketClient < FakeWebSocketClient
73
+ def send(application_data)
74
+ frame = ''
75
+ opcode = 1 # fake only supports text frames
76
+ byte1 = opcode | 0b10000000 # since more, rsv1-3 are 0
77
+ frame << byte1
78
+
79
+ mask = 0b10000000
80
+
81
+ length = application_data.size
82
+ if length <= 125
83
+ byte2 = length # since rsv4 is 0
84
+ frame << (mask | byte2)
85
+ elsif length < 65536 # write 2 byte length
86
+ frame << (mask | 126)
87
+ frame << [length].pack('n')
88
+ else # write 8 byte length
89
+ frame << (mask | 127)
90
+ frame << [length >> 32, length & 0xFFFFFFFF].pack("NN")
91
+ end
92
+
93
+ frame << EventMachine::WebSocket::MaskedString.create_masked_string(application_data)
94
+
95
+ send_data(frame)
96
+ end
97
+ end
98
+
99
+
100
+ # Wrap EM:HttpRequest in a websocket like interface so that it can be used in the specs with the same interface as FakeWebSocketClient
101
+ class Draft75WebSocketClient
102
+ def onopen(&blk); @onopen = blk; end
103
+ def onclose(&blk); @onclose = blk; end
104
+ def onerror(&blk); @onerror = blk; end
105
+ def onmessage(&blk); @onmessage = blk; end
106
+
107
+ def initialize
108
+ @ws = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:timeout => 0)
109
+ @ws.errback { @onerror.call if @onerror }
110
+ @ws.callback { @onopen.call if @onopen }
111
+ @ws.stream { |msg| @onmessage.call(msg) if @onmessage }
112
+ end
113
+
114
+ def send(message)
115
+ @ws.send(message)
116
+ end
117
+
118
+ def close_connection
119
+ @ws.close_connection
120
+ end
121
+ end
122
+
123
+ def format_request(r)
124
+ data = "#{r[:method]} #{r[:path]} HTTP/1.1\r\n"
125
+ header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
126
+ data << [header_lines, '', r[:body]].join("\r\n")
127
+ data
128
+ end
129
+
130
+ def format_response(r)
131
+ data = r[:protocol] || "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
132
+ header_lines = r[:headers].map { |k,v| "#{k}: #{v}" }
133
+ data << [header_lines, '', r[:body]].join("\r\n")
134
+ data
135
+ end
136
+
137
+ def handler(request, secure = false)
138
+ connection = Object.new
139
+ EM::WebSocket::HandlerFactory.build(connection, format_request(request), secure)
140
+ end
141
+
142
+ RSpec::Matchers.define :send_handshake do |response|
143
+ match do |actual|
144
+ actual.handshake.lines.sort == format_response(response).lines.sort
145
+ end
146
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+
3
+ shared_examples_for "a websocket client" do
4
+ it "should accept a single-frame binary message" do
5
+ EM.run do
6
+ start_server { |server|
7
+ server.onmessage { |msg, type|
8
+ msg.should == '\xFF\xFF'
9
+ type.should == :binary
10
+ EM.stop
11
+ }
12
+ server.onerror {
13
+ failed
14
+ }
15
+ }
16
+
17
+ options = { :host => '0.0.0.0', :port => 12345, :debug => false }
18
+ client = EM.connect('0.0.0.0', 12345, EventMachine::WebSocket::ClientConnection, options) do |ws|
19
+ ws.onopen do
20
+ ws.send '\xFF\xFF', :binary
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ it "should accept a text message in the same frame as the server handshake response" do
27
+ EM.run do
28
+ start_server { |server|
29
+ server.onopen { server.send 'hello' }
30
+ server.onerror { failed }
31
+ }
32
+
33
+ options = { :host => '0.0.0.0', :port => 12345, :debug => false }
34
+ client = EM.connect( options[:host], options[:port], EventMachine::WebSocket::ClientConnection, options) do |ws|
35
+ ws.onmessage{ |msg, type|
36
+ msg.should == 'hello'
37
+ type.should == :text
38
+ EM.stop
39
+ }
40
+
41
+ EventMachine::add_timer 3 do
42
+ failed # ran out of time
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,118 @@
1
+ require 'helper'
2
+
3
+ # These tests are not specifi to any particular draft of the specification
4
+ #
5
+ describe "WebSocket server" do
6
+ include EM::SpecHelper
7
+ default_timeout 1
8
+
9
+ it "should fail on non WebSocket requests" do
10
+ em {
11
+ EventMachine.add_timer(0.1) do
12
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:12345/').get :timeout => 0
13
+ http.errback { done }
14
+ http.callback { fail }
15
+ end
16
+
17
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) {}
18
+ }
19
+ end
20
+
21
+ it "should populate ws.request with appropriate headers" do
22
+ em {
23
+ EventMachine.add_timer(0.1) do
24
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get :timeout => 0
25
+ http.errback { fail }
26
+ http.callback {
27
+ http.response_header.status.should == 101
28
+ http.close_connection
29
+ }
30
+ http.stream { |msg| }
31
+ end
32
+
33
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
34
+ ws.onopen {
35
+ ws.request["user-agent"].should == "EventMachine HttpClient"
36
+ ws.request["connection"].should == "Upgrade"
37
+ ws.request["upgrade"].should == "WebSocket"
38
+ ws.request["path"].should == "/"
39
+ ws.request["origin"].should == "127.0.0.1"
40
+ ws.request["host"].to_s.should == "ws://127.0.0.1:12345"
41
+ }
42
+ ws.onclose {
43
+ ws.state.should == :closed
44
+ EventMachine.stop
45
+ }
46
+ end
47
+ }
48
+ end
49
+
50
+ it "should allow sending and retrieving query string args passed in on the connection request." do
51
+ em {
52
+ EventMachine.add_timer(0.1) do
53
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:query => {'foo' => 'bar', 'baz' => 'qux'}, :timeout => 0)
54
+ http.errback { fail }
55
+ http.callback {
56
+ http.response_header.status.should == 101
57
+ http.close_connection
58
+ }
59
+ http.stream { |msg| }
60
+ end
61
+
62
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
63
+ ws.onopen {
64
+ path, query = ws.request["path"].split('?')
65
+ path.should == '/'
66
+ Hash[*query.split(/&|=/)].should == {"foo"=>"bar", "baz"=>"qux"}
67
+ ws.request["query"]["foo"].should == "bar"
68
+ ws.request["query"]["baz"].should == "qux"
69
+ }
70
+ ws.onclose {
71
+ ws.state.should == :closed
72
+ EventMachine.stop
73
+ }
74
+ end
75
+ }
76
+ end
77
+
78
+ it "should ws.response['Query'] to empty hash when no query string params passed in connection URI" do
79
+ em {
80
+ EventMachine.add_timer(0.1) do
81
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:12345/').get(:timeout => 0)
82
+ http.errback { fail }
83
+ http.callback {
84
+ http.response_header.status.should == 101
85
+ http.close_connection
86
+ }
87
+ http.stream { |msg| }
88
+ end
89
+
90
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) do |ws|
91
+ ws.onopen {
92
+ ws.request["path"].should == "/"
93
+ ws.request["query"].should == {}
94
+ }
95
+ ws.onclose {
96
+ ws.state.should == :closed
97
+ EventMachine.stop
98
+ }
99
+ end
100
+ }
101
+ end
102
+
103
+ it "should raise an exception if frame sent before handshake complete" do
104
+ em {
105
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |c|
106
+ # We're not using a real client so the handshake will not be sent
107
+ EM.add_timer(0.1) {
108
+ lambda {
109
+ c.send('early message')
110
+ }.should raise_error('Cannot send data before onopen callback')
111
+ done
112
+ }
113
+ }
114
+
115
+ client = EM.connect('0.0.0.0', 12345, EM::Connection)
116
+ }
117
+ end
118
+ end
@@ -0,0 +1,270 @@
1
+ require 'helper'
2
+ require 'integration/shared_examples'
3
+
4
+ describe "draft03" do
5
+ include EM::SpecHelper
6
+ default_timeout 1
7
+
8
+ before :each do
9
+ @request = {
10
+ :port => 80,
11
+ :method => "GET",
12
+ :path => "/demo",
13
+ :headers => {
14
+ 'Host' => 'example.com',
15
+ 'Connection' => 'Upgrade',
16
+ 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
17
+ 'Sec-WebSocket-Protocol' => 'sample',
18
+ 'Upgrade' => 'WebSocket',
19
+ 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
20
+ 'Origin' => 'http://example.com',
21
+ 'Sec-WebSocket-Draft' => '3'
22
+ },
23
+ :body => '^n:ds[4U'
24
+ }
25
+
26
+ @response = {
27
+ :headers => {
28
+ "Upgrade" => "WebSocket",
29
+ "Connection" => "Upgrade",
30
+ "Sec-WebSocket-Location" => "ws://example.com/demo",
31
+ "Sec-WebSocket-Origin" => "http://example.com",
32
+ "Sec-WebSocket-Protocol" => "sample"
33
+ },
34
+ :body => "8jKS\'y:G*Co,Wxa-"
35
+ }
36
+ end
37
+
38
+ it_behaves_like "a websocket server" do
39
+ def start_server
40
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
41
+ yield ws
42
+ }
43
+ end
44
+
45
+ def start_client
46
+ client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
47
+ client.send_data(format_request(@request))
48
+ yield client if block_given?
49
+ end
50
+ end
51
+
52
+ # These examples are straight from the spec
53
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
54
+ describe "examples from the spec" do
55
+ it "should accept a single-frame text message" do
56
+ em {
57
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
58
+ ws.onmessage { |msg|
59
+ msg.should == 'Hello'
60
+ done
61
+ }
62
+ }
63
+
64
+ # Create a fake client which sends draft 76 handshake
65
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
66
+ connection.send_data(format_request(@request))
67
+
68
+ # Send frame
69
+ connection.onopen {
70
+ connection.send_data("\x04\x05Hello")
71
+ }
72
+ }
73
+ end
74
+
75
+ it "should accept a fragmented text message" do
76
+ em {
77
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
78
+ ws.onmessage { |msg|
79
+ msg.should == 'Hello'
80
+ done
81
+ }
82
+ }
83
+
84
+ # Create a fake client which sends draft 76 handshake
85
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
86
+ connection.send_data(format_request(@request))
87
+
88
+ # Send frame
89
+ connection.onopen {
90
+ connection.send_data("\x84\x03Hel")
91
+ connection.send_data("\x00\x02lo")
92
+ }
93
+ }
94
+ end
95
+
96
+ it "should accept a ping request and respond with the same body" do
97
+ em {
98
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
99
+
100
+ # Create a fake client which sends draft 76 handshake
101
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
102
+ connection.send_data(format_request(@request))
103
+
104
+ # Send frame
105
+ connection.onopen {
106
+ connection.send_data("\x02\x05Hello")
107
+ }
108
+
109
+ connection.onmessage { |frame|
110
+ next if frame.nil?
111
+ frame.should == "\x03\x05Hello"
112
+ done
113
+ }
114
+ }
115
+ end
116
+
117
+ it "should accept a 256 bytes binary message in a single frame" do
118
+ em {
119
+ data = "a" * 256
120
+
121
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
122
+ ws.onmessage { |msg|
123
+ msg.should == data
124
+ done
125
+ }
126
+ }
127
+
128
+ # Create a fake client which sends draft 76 handshake
129
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
130
+ connection.send_data(format_request(@request))
131
+
132
+ # Send frame
133
+ connection.onopen {
134
+ connection.send_data("\x05\x7E\x01\x00" + data)
135
+ }
136
+ }
137
+ end
138
+
139
+ it "should accept a 64KiB binary message in a single frame" do
140
+ em {
141
+ data = "a" * 65536
142
+
143
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
144
+ ws.onmessage { |msg|
145
+ msg.should == data
146
+ done
147
+ }
148
+ }
149
+
150
+ # Create a fake client which sends draft 76 handshake
151
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
152
+ connection.send_data(format_request(@request))
153
+
154
+ # Send frame
155
+ connection.onopen {
156
+ connection.send_data("\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data)
157
+ }
158
+ }
159
+ end
160
+ end
161
+
162
+ describe "close handling" do
163
+ it "should respond to a new close frame with a close frame" do
164
+ em {
165
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }
166
+
167
+ # Create a fake client which sends draft 76 handshake
168
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
169
+ connection.send_data(format_request(@request))
170
+
171
+ # Send close frame
172
+ connection.onopen {
173
+ connection.send_data("\x01\x00")
174
+ }
175
+
176
+ # Check that close ack received
177
+ connection.onmessage { |frame|
178
+ frame.should == "\x01\x00"
179
+ done
180
+ }
181
+ }
182
+ end
183
+
184
+ it "should close the connection on receiving a close acknowlegement" do
185
+ em {
186
+ ack_received = false
187
+
188
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
189
+ ws.onopen {
190
+ # 2. Send a close frame
191
+ EM.next_tick {
192
+ ws.close_websocket
193
+ }
194
+ }
195
+ }
196
+
197
+ # 1. Create a fake client which sends draft 76 handshake
198
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
199
+ connection.send_data(format_request(@request))
200
+
201
+ # 3. Check that close frame recieved and acknowlege it
202
+ connection.onmessage { |frame|
203
+ frame.should == "\x01\x00"
204
+ ack_received = true
205
+ connection.send_data("\x01\x00")
206
+ }
207
+
208
+ # 4. Check that connection is closed _after_ the ack
209
+ connection.onclose {
210
+ ack_received.should == true
211
+ done
212
+ }
213
+ }
214
+ end
215
+
216
+ it "should not allow data frame to be sent after close frame sent" do
217
+ em {
218
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
219
+ ws.onopen {
220
+ # 2. Send a close frame
221
+ EM.next_tick {
222
+ ws.close_websocket
223
+ }
224
+
225
+ # 3. Check that exception raised if I attempt to send more data
226
+ EM.add_timer(0.1) {
227
+ lambda {
228
+ ws.send('hello world')
229
+ }.should raise_error(EM::WebSocket::WebSocketError, 'Cannot send data frame since connection is closing')
230
+ done
231
+ }
232
+ }
233
+ }
234
+
235
+ # 1. Create a fake client which sends draft 76 handshake
236
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
237
+ connection.send_data(format_request(@request))
238
+ }
239
+ end
240
+
241
+ it "should still respond to control frames after close frame sent" do
242
+ em {
243
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
244
+ ws.onopen {
245
+ # 2. Send a close frame
246
+ EM.next_tick {
247
+ ws.close_websocket
248
+ }
249
+ }
250
+ }
251
+
252
+ # 1. Create a fake client which sends draft 76 handshake
253
+ connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
254
+ connection.send_data(format_request(@request))
255
+
256
+ connection.onmessage { |frame|
257
+ if frame == "\x01\x00"
258
+ # 3. After the close frame is received send a ping frame, but
259
+ # don't respond with a close ack
260
+ connection.send_data("\x02\x05Hello")
261
+ else
262
+ # 4. Check that the pong is received
263
+ frame.should == "\x03\x05Hello"
264
+ done
265
+ end
266
+ }
267
+ }
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,48 @@
1
+ require 'helper'
2
+
3
+ describe "draft05" do
4
+ include EM::SpecHelper
5
+ default_timeout 1
6
+
7
+ before :each do
8
+ @request = {
9
+ :port => 80,
10
+ :method => "GET",
11
+ :path => "/demo",
12
+ :headers => {
13
+ 'Host' => 'example.com',
14
+ 'Upgrade' => 'websocket',
15
+ 'Connection' => 'Upgrade',
16
+ 'Sec-WebSocket-Key' => 'dGhlIHNhbXBsZSBub25jZQ==',
17
+ 'Sec-WebSocket-Protocol' => 'sample',
18
+ 'Sec-WebSocket-Origin' => 'http://example.com',
19
+ 'Sec-WebSocket-Version' => '5'
20
+ }
21
+ }
22
+ end
23
+
24
+ def start_server
25
+ EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
26
+ yield ws
27
+ }
28
+ end
29
+
30
+ def start_client
31
+ client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
32
+ client.send_data(format_request(@request))
33
+ yield client if block_given?
34
+ end
35
+
36
+ it "should open connection" do
37
+ em {
38
+ start_server { |server|
39
+ server.onopen {
40
+ server.instance_variable_get(:@handler).class.should == EventMachine::WebSocket::Handler05
41
+ done
42
+ }
43
+ }
44
+
45
+ start_client
46
+ }
47
+ end
48
+ end