sonixlabs-em-websocket 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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