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
@@ -0,0 +1,325 @@
1
+ require 'helper'
2
+
3
+ describe EM::WebSocket::Framing03 do
4
+ class FramingContainer
5
+ include EM::WebSocket::Framing03
6
+
7
+ def <<(data)
8
+ @data << data
9
+ process_data(data)
10
+ end
11
+
12
+ def debug(*args); end
13
+ end
14
+
15
+ before :each do
16
+ @f = FramingContainer.new
17
+ @f.initialize_framing
18
+ end
19
+
20
+ describe "basic examples" do
21
+ it "connection close" do
22
+ @f.should_receive(:message).with(:close, '', '')
23
+ @f << 0b00000001
24
+ @f << 0b00000000
25
+ end
26
+
27
+ it "ping" do
28
+ @f.should_receive(:message).with(:ping, '', '')
29
+ @f << 0b00000010
30
+ @f << 0b00000000
31
+ end
32
+
33
+ it "pong" do
34
+ @f.should_receive(:message).with(:pong, '', '')
35
+ @f << 0b00000011
36
+ @f << 0b00000000
37
+ end
38
+
39
+ it "text" do
40
+ @f.should_receive(:message).with(:text, '', 'foo')
41
+ @f << 0b00000100
42
+ @f << 0b00000011
43
+ @f << 'foo'
44
+ end
45
+
46
+ it "Text in two frames" do
47
+ @f.should_receive(:message).with(:text, '', 'hello world')
48
+ @f << 0b10000100
49
+ @f << 0b00000110
50
+ @f << "hello "
51
+ @f << 0b00000000
52
+ @f << 0b00000101
53
+ @f << "world"
54
+ end
55
+
56
+ it "2 byte extended payload length text frame" do
57
+ data = 'a' * 256
58
+ @f.should_receive(:message).with(:text, '', data)
59
+ @f << 0b00000100 # Single frame, text
60
+ @f << 0b01111110 # Length 126 (so read 2 bytes)
61
+ @f << 0b00000001 # Two bytes in network byte order (256)
62
+ @f << 0b00000000
63
+ @f << data
64
+ end
65
+ end
66
+
67
+ # These examples are straight from the spec
68
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
69
+ describe "examples from the spec" do
70
+ it "a single-frame text message" do
71
+ @f.should_receive(:message).with(:text, '', 'Hello')
72
+ @f << "\x04\x05Hello"
73
+ end
74
+
75
+ it "a fragmented text message" do
76
+ @f.should_receive(:message).with(:text, '', 'Hello')
77
+ @f << "\x84\x03Hel"
78
+ @f << "\x00\x02lo"
79
+ end
80
+
81
+ it "Ping request and response" do
82
+ @f.should_receive(:message).with(:ping, '', 'Hello')
83
+ @f << "\x02\x05Hello"
84
+ end
85
+
86
+ it "256 bytes binary message in a single frame" do
87
+ data = "a"*256
88
+ @f.should_receive(:message).with(:binary, '', data)
89
+ @f << "\x05\x7E\x01\x00" + data
90
+ end
91
+
92
+ it "64KiB binary message in a single frame" do
93
+ data = "a"*65536
94
+ @f.should_receive(:message).with(:binary, '', data)
95
+ @f << "\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
96
+ end
97
+ end
98
+
99
+ describe "other tests" do
100
+ it "should accept a fragmented unmasked text message in 3 frames" do
101
+ @f.should_receive(:message).with(:text, '', 'Hello world')
102
+ @f << "\x84\x03Hel"
103
+ @f << "\x80\x02lo"
104
+ @f << "\x00\x06 world"
105
+ end
106
+ end
107
+
108
+ describe "error cases" do
109
+ it "should raise an exception on continuation frame without preceeding more frame" do
110
+ lambda {
111
+ @f << 0b00000000 # Single frame, continuation
112
+ @f << 0b00000001 # Length 1
113
+ @f << 'f'
114
+ }.should raise_error(EM::WebSocket::WebSocketError, 'Continuation frame not expected')
115
+ end
116
+ end
117
+ end
118
+
119
+ # These examples are straight from the spec
120
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
121
+ describe EM::WebSocket::Framing04 do
122
+ class FramingContainer04
123
+ include EM::WebSocket::Framing04
124
+
125
+ def <<(data)
126
+ @data << data
127
+ process_data(data)
128
+ end
129
+
130
+ def debug(*args); end
131
+ end
132
+
133
+ before :each do
134
+ @f = FramingContainer04.new
135
+ @f.initialize_framing
136
+ end
137
+
138
+ describe "examples from the spec" do
139
+ it "a single-frame text message" do
140
+ @f.should_receive(:message).with(:text, '', 'Hello')
141
+ @f << "\x84\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
142
+ end
143
+
144
+ it "a fragmented text message" do
145
+ @f.should_receive(:message).with(:text, '', 'Hello')
146
+ @f << "\x04\x03Hel"
147
+ @f << "\x80\x02lo"
148
+ end
149
+
150
+ it "Ping request" do
151
+ @f.should_receive(:message).with(:ping, '', 'Hello')
152
+ @f << "\x82\x05Hello"
153
+ end
154
+
155
+ it "a pong response" do
156
+ @f.should_receive(:message).with(:pong, '', 'Hello')
157
+ @f << "\x83\x05Hello"
158
+ end
159
+
160
+ it "256 bytes binary message in a single frame" do
161
+ data = "a"*256
162
+ @f.should_receive(:message).with(:binary, '', data)
163
+ @f << "\x85\x7E\x01\x00" + data
164
+ end
165
+
166
+ it "64KiB binary message in a single frame" do
167
+ data = "a"*65536
168
+ @f.should_receive(:message).with(:binary, '', data)
169
+ @f << "\x85\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
170
+ end
171
+ end
172
+
173
+ describe "other tests" do
174
+ it "should accept a fragmented unmasked text message in 3 frames" do
175
+ @f.should_receive(:message).with(:text, '', 'Hello world')
176
+ @f << "\x04\x03Hel"
177
+ @f << "\x00\x02lo"
178
+ @f << "\x80\x06 world"
179
+ end
180
+ end
181
+ end
182
+
183
+ describe EM::WebSocket::Framing07 do
184
+ class FramingContainer07
185
+ include EM::WebSocket::Framing07
186
+
187
+ def <<(data)
188
+ @data << data
189
+ process_data(data)
190
+ end
191
+
192
+ def debug(*args); end
193
+ end
194
+
195
+ before :each do
196
+ @f = FramingContainer07.new
197
+ @f.initialize_framing
198
+ end
199
+
200
+ # These examples are straight from the spec
201
+ # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07#section-4.6
202
+ # NOTE I modified these to be compliant with the rule that client data must be masked
203
+ describe "server side" do
204
+ describe "examples from the spec" do
205
+ it "rejects a single-frame unmasked text message from the client" do
206
+ lambda {
207
+ @f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
208
+ }.should raise_error(EventMachine::WebSocket::WebSocketError, 'Data from client must be masked')
209
+ end
210
+
211
+ it "a single-frame masked text message" do
212
+ @f.should_receive(:message).with(:text, '', 'Hello')
213
+ @f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello"
214
+ end
215
+
216
+ it "a fragmented masked text message" do
217
+ @f.should_receive(:message).with(:text, '', 'Hello')
218
+ @f << "\x01\x83\x01\x01\x01\x01Idm" # with mask x01x01x01x01, Hello -> Idmmn
219
+ @f << "\x80\x82\x01\x01\x01\x01mn"
220
+ end
221
+
222
+ it "Ping request" do
223
+ @f.should_receive(:message).with(:ping, '', 'Hello')
224
+ @f << "\x89\x85\x01\x01\x01\x01Idmmn"
225
+ end
226
+
227
+ it "a pong response" do
228
+ @f.should_receive(:message).with(:pong, '', 'Hello')
229
+ @f << "\x8a\x85\x01\x01\x01\x01Idmmn"
230
+ end
231
+
232
+ it "256 bytes binary message in a single masked frame" do
233
+ data = "b"*256
234
+ masked_data = "c"*256
235
+ @f.should_receive(:message).with(:binary, '', data)
236
+ @f << "\x82\xFE\x01\x00\x01\x01\x01\x01" + masked_data
237
+ end
238
+
239
+ it "64KiB binary message in a single masked frame" do
240
+ data = "b"*65536
241
+ masked_data = "c"*65536
242
+ @f.should_receive(:message).with(:binary, '', data)
243
+ @f << "\x82\xFF\x00\x00\x00\x00\x00\x01\x00\x00\x01\x01\x01\x01" + masked_data
244
+ end
245
+ end
246
+
247
+ describe "other tests" do
248
+ it "should raise a DataError if an invalid frame type is requested" do
249
+ lambda {
250
+ # Opcode 3 is not supported by this draft
251
+ @f << "\x83\x85\x01\x01\x01\x01Idmmn"
252
+ }.should raise_error(EventMachine::WebSocket::DataError, "Unknown opcode")
253
+ end
254
+
255
+ it "should accept a fragmented masked text message in 3 frames" do
256
+ @f.should_receive(:message).with(:text, '', 'Hello world')
257
+ @f << "\x01\x83\x01\x01\x01\x01Idm"
258
+ @f << "\x00\x82\x01\x01\x01\x01mn"
259
+ @f << "\x80\x86\x01\x01\x01\x01\x21vnsme"
260
+ end
261
+ end
262
+ end
263
+
264
+ describe "client side" do
265
+ before do
266
+ @f.mask_outbound_messages = true
267
+ @f.require_masked_inbound_messages = false
268
+ end
269
+ describe "examples from the spec" do
270
+ it "accepts a single-frame unmasked text message from the client" do
271
+ @f.should_receive(:message).with(:text, '', 'Hello')
272
+ @f << "\x81\x05\x48\x65\x6c\x6c\x6f" # "\x84\x05Hello"
273
+ end
274
+
275
+ it "a single-frame masked text message" do
276
+ @f.should_receive(:message).with(:text, '', 'Hello')
277
+ @f << "\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58" # "\x84\x05Hello"
278
+ end
279
+
280
+ it "a fragmented unmasked text message" do
281
+ @f.should_receive(:message).with(:text, '', 'Hello')
282
+ @f << "\x01\x03\Hel"
283
+ @f << "\x80\x02\lo"
284
+ end
285
+
286
+ it "Ping request" do
287
+ @f.should_receive(:message).with(:ping, '', 'Hello')
288
+ @f << "\x89\x05Hello"
289
+ end
290
+
291
+ it "a pong response" do
292
+ @f.should_receive(:message).with(:pong, '', 'Hello')
293
+ @f << "\x8a\x05Hello"
294
+ end
295
+
296
+ it "256 bytes binary message in a single unmasked frame" do
297
+ data = "a"*256
298
+ @f.should_receive(:message).with(:binary, '', data)
299
+ @f << "\x82\x7E\x01\x00" + data
300
+ end
301
+
302
+ it "64KiB binary message in a single unmasked frame" do
303
+ data = "a"*65536
304
+ @f.should_receive(:message).with(:binary, '', data)
305
+ @f << "\x82\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data
306
+ end
307
+ end
308
+
309
+ describe "other tests" do
310
+ it "should raise a DataError if an invalid frame type is requested" do
311
+ lambda {
312
+ # Opcode 3 is not supported by this draft
313
+ @f << "\x83\x05Hello"
314
+ }.should raise_error(EventMachine::WebSocket::DataError, "Unknown opcode")
315
+ end
316
+
317
+ it "should accept a fragmented unmasked text message in 3 frames" do
318
+ @f.should_receive(:message).with(:text, '', 'Hello world')
319
+ @f << "\x01\x03Hel"
320
+ @f << "\x00\x02lo"
321
+ @f << "\x80\x06 world"
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,147 @@
1
+ require 'helper'
2
+
3
+ describe "EventMachine::WebSocket::Handler" do
4
+ before :each do
5
+ @request = {
6
+ :port => 80,
7
+ :method => "GET",
8
+ :path => "/demo",
9
+ :headers => {
10
+ 'Host' => 'example.com',
11
+ 'Connection' => 'Upgrade',
12
+ 'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
13
+ 'Sec-WebSocket-Protocol' => 'sample',
14
+ 'Upgrade' => 'WebSocket',
15
+ 'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
16
+ 'Origin' => 'http://example.com'
17
+ },
18
+ :body => '^n:ds[4U'
19
+ }
20
+ @secure_request = @request.merge(:port => 443)
21
+
22
+ @response = {
23
+ :headers => {
24
+ "Upgrade" => "WebSocket",
25
+ "Connection" => "Upgrade",
26
+ "Sec-WebSocket-Location" => "ws://example.com/demo",
27
+ "Sec-WebSocket-Origin" => "http://example.com",
28
+ "Sec-WebSocket-Protocol" => "sample"
29
+ },
30
+ :body => "8jKS\'y:G*Co,Wxa-"
31
+ }
32
+ @secure_response = @response.merge(:headers => @response[:headers].merge('Sec-WebSocket-Location' => "wss://example.com/demo"))
33
+ end
34
+
35
+ it "should handle good request" do
36
+ handler(@request).should send_handshake(@response)
37
+ end
38
+
39
+ it "should handle good request to secure default port if secure mode is enabled" do
40
+ handler(@secure_request, true).should send_handshake(@secure_response)
41
+ end
42
+
43
+ it "should not handle good request to secure default port if secure mode is disabled" do
44
+ handler(@secure_request, false).should_not send_handshake(@secure_response)
45
+ end
46
+
47
+ it "should handle good request on nondefault port" do
48
+ @request[:port] = 8081
49
+ @request[:headers]['Host'] = 'example.com:8081'
50
+ @response[:headers]['Sec-WebSocket-Location'] =
51
+ 'ws://example.com:8081/demo'
52
+
53
+ handler(@request).should send_handshake(@response)
54
+ end
55
+
56
+ it "should handle good request to secure nondefault port" do
57
+ @secure_request[:port] = 8081
58
+ @secure_request[:headers]['Host'] = 'example.com:8081'
59
+ @secure_response[:headers]['Sec-WebSocket-Location'] = 'wss://example.com:8081/demo'
60
+ handler(@secure_request, true).should send_handshake(@secure_response)
61
+ end
62
+
63
+ it "should handle good request with no protocol" do
64
+ @request[:headers].delete('Sec-WebSocket-Protocol')
65
+ @response[:headers].delete("Sec-WebSocket-Protocol")
66
+
67
+ handler(@request).should send_handshake(@response)
68
+ end
69
+
70
+ it "should handle extra headers by simply ignoring them" do
71
+ @request[:headers]['EmptyValue'] = ""
72
+ @request[:headers]['AKey'] = "AValue"
73
+
74
+ handler(@request).should send_handshake(@response)
75
+ end
76
+
77
+ it "should raise error on HTTP request" do
78
+ @request[:headers] = {
79
+ 'Host' => 'www.google.com',
80
+ 'User-Agent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 GTB6 GTBA',
81
+ 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
82
+ 'Accept-Language' => 'en-us,en;q=0.5',
83
+ 'Accept-Encoding' => 'gzip,deflate',
84
+ 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
85
+ 'Keep-Alive' => '300',
86
+ 'Connection' => 'keep-alive',
87
+ }
88
+
89
+ lambda {
90
+ handler(@request).handshake
91
+ }.should raise_error(EM::WebSocket::HandshakeError)
92
+ end
93
+
94
+ it "should raise error on wrong method" do
95
+ @request[:method] = 'POST'
96
+
97
+ lambda {
98
+ handler(@request).handshake
99
+ }.should raise_error(EM::WebSocket::HandshakeError)
100
+ end
101
+
102
+ it "should raise error if upgrade header incorrect" do
103
+ @request[:headers]['Upgrade'] = 'NonWebSocket'
104
+
105
+ lambda {
106
+ handler(@request).handshake
107
+ }.should raise_error(EM::WebSocket::HandshakeError)
108
+ end
109
+
110
+ it "should raise error if Sec-WebSocket-Protocol is empty" do
111
+ @request[:headers]['Sec-WebSocket-Protocol'] = ''
112
+
113
+ lambda {
114
+ handler(@request).handshake
115
+ }.should raise_error(EM::WebSocket::HandshakeError)
116
+ end
117
+
118
+ %w[Sec-WebSocket-Key1 Sec-WebSocket-Key2].each do |header|
119
+ it "should raise error if #{header} has zero spaces" do
120
+ @request[:headers][header] = 'nospaces'
121
+
122
+ lambda {
123
+ handler(@request).handshake
124
+ }.should raise_error(EM::WebSocket::HandshakeError, 'Websocket Key1 or Key2 does not contain spaces - this is a symptom of a cross-protocol attack')
125
+ end
126
+ end
127
+
128
+ it "should raise error if spaces do not divide numbers in Sec-WebSocket-Key* " do
129
+ @request[:headers]['Sec-WebSocket-Key2'] = '12998 5 Y3 1.P00'
130
+
131
+ lambda {
132
+ handler(@request).handshake
133
+ }.should raise_error(EM::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
134
+ end
135
+
136
+ it "should leave request with incomplete header" do
137
+ data = format_request(@request)
138
+ # Sends only half of the request
139
+ EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length / 2)]).should == nil
140
+ end
141
+
142
+ it "should leave request with incomplete third key" do
143
+ data = format_request(@request)
144
+ # Removes last two bytes of the third key
145
+ EM::WebSocket::HandlerFactory.build(mock(EM::WebSocket::Connection), data[0...(data.length - 2)]).should == nil
146
+ end
147
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: BINARY
2
+
3
+ require 'helper'
4
+
5
+ describe EM::WebSocket::MaskedString do
6
+ it "should allow reading 4 byte mask and unmasking byte / bytes" do
7
+ t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x01\x00\x01\x00\x01")
8
+ t.read_mask
9
+ t.getbyte(3).should == 0x00
10
+ t.getbytes(4, 4).should == "\x00\x01\x00\x00"
11
+ t.getbytes(5, 3).should == "\x01\x00\x00"
12
+ end
13
+
14
+ it "should return nil from getbyte if index requested is out of range" do
15
+ t = EM::WebSocket::MaskedString.new("\x00\x00\x00\x00\x53")
16
+ t.read_mask
17
+ t.getbyte(4).should == 0x53
18
+ t.getbyte(5).should == nil
19
+ end
20
+
21
+ it "should allow switching masking on and off" do
22
+ t = EM::WebSocket::MaskedString.new("\x02\x00\x00\x00\x03")
23
+ t.getbyte(4).should == 0x03
24
+ t.read_mask
25
+ t.getbyte(4).should == 0x01
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ describe EM::WebSocket::MessageProcessor06 do
4
+ class MessageProcessorContainer06
5
+ attr_accessor :connection
6
+ include EM::WebSocket::MessageProcessor06
7
+ def debug(*args); end
8
+ end
9
+
10
+ before :each do
11
+ @mp = MessageProcessorContainer06.new
12
+ @mp.connection = Object.new
13
+ end
14
+
15
+ describe "#message" do
16
+ it "accepts a close"
17
+ it "accepts a ping"
18
+
19
+ it "accepts a pong" do
20
+ @mp.connection.should_receive(:trigger_on_message).with(:rock, :pong)
21
+ @mp.message :pong, :fraggle, :rock
22
+ end
23
+
24
+ it "accepts a binary message" do
25
+ @mp.connection.should_receive(:trigger_on_message).with(:rock, :binary)
26
+ @mp.message :binary, :fraggle, :rock
27
+ end
28
+
29
+ it "accepts a non-UTF8 text message"
30
+
31
+ it "accepts a text message" do
32
+ @mp.connection.should_receive(:trigger_on_message).with(:rock, :text)
33
+ @mp.message :text, :fraggle, :rock
34
+ end
35
+ end
36
+ end