faye-websocket 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye-websocket might be problematic. Click here for more details.
- data/CHANGELOG.txt +22 -0
- data/README.rdoc +24 -2
- data/examples/autobahn_client.rb +1 -2
- data/examples/index.html +7 -2
- data/examples/server.rb +2 -2
- data/ext/faye_websocket_mask/extconf.rb +6 -0
- data/ext/faye_websocket_mask/faye_websocket_mask.c +35 -0
- data/lib/faye/websocket.rb +9 -4
- data/lib/faye/websocket/client.rb +7 -5
- data/lib/faye/websocket/draft75_parser.rb +53 -24
- data/lib/faye/websocket/draft76_parser.rb +15 -1
- data/lib/faye/websocket/{protocol8_parser.rb → hybi_parser.rb} +147 -168
- data/lib/faye/websocket/hybi_parser/handshake.rb +78 -0
- data/lib/faye/websocket/hybi_parser/stream_reader.rb +29 -0
- data/spec/faye/websocket/client_spec.rb +19 -7
- data/spec/faye/websocket/draft75_parser_spec.rb +43 -13
- data/spec/faye/websocket/draft76_parser_spec.rb +34 -0
- data/spec/faye/websocket/{protocol8_parser_spec.rb → hybi_parser_spec.rb} +39 -28
- data/spec/spec_helper.rb +1 -1
- metadata +79 -95
@@ -0,0 +1,78 @@
|
|
1
|
+
module Faye
|
2
|
+
class WebSocket
|
3
|
+
class HybiParser
|
4
|
+
|
5
|
+
class Handshake
|
6
|
+
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
7
|
+
|
8
|
+
attr_reader :protocol
|
9
|
+
|
10
|
+
def initialize(uri, protocols)
|
11
|
+
@uri = uri
|
12
|
+
@protocols = protocols
|
13
|
+
@key = Base64.encode64((1..16).map { rand(255).chr } * '').strip
|
14
|
+
@accept = Base64.encode64(Digest::SHA1.digest(@key + GUID)).strip
|
15
|
+
@buffer = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def request_data
|
19
|
+
hostname = @uri.host + (@uri.port ? ":#{@uri.port}" : '')
|
20
|
+
|
21
|
+
headers = [
|
22
|
+
"GET #{@uri.path}#{@uri.query ? '?' : ''}#{@uri.query} HTTP/1.1",
|
23
|
+
"Host: #{hostname}",
|
24
|
+
"Upgrade: websocket",
|
25
|
+
"Connection: Upgrade",
|
26
|
+
"Sec-WebSocket-Key: #{@key}",
|
27
|
+
"Sec-WebSocket-Version: 13"
|
28
|
+
]
|
29
|
+
|
30
|
+
if @protocols
|
31
|
+
headers << "Sec-WebSocket-Protocol: #{@protocols * ', '}"
|
32
|
+
end
|
33
|
+
|
34
|
+
(headers + ['','']).join("\r\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse(data)
|
38
|
+
message = []
|
39
|
+
complete = false
|
40
|
+
data.each_byte do |byte|
|
41
|
+
if complete
|
42
|
+
message << byte
|
43
|
+
else
|
44
|
+
@buffer << byte
|
45
|
+
complete ||= complete?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
message
|
49
|
+
end
|
50
|
+
|
51
|
+
def complete?
|
52
|
+
@buffer[-4..-1] == [0x0D, 0x0A, 0x0D, 0x0A]
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid?
|
56
|
+
data = WebSocket.encode(@buffer)
|
57
|
+
|
58
|
+
response = Net::HTTPResponse.read_new(Net::BufferedIO.new(StringIO.new(data)))
|
59
|
+
return false unless response.code.to_i == 101
|
60
|
+
|
61
|
+
upgrade = response['Upgrade']
|
62
|
+
connection = response['Connection']
|
63
|
+
protocol = response['Sec-WebSocket-Protocol']
|
64
|
+
|
65
|
+
@protocol = @protocols && @protocols.include?(protocol) ?
|
66
|
+
protocol :
|
67
|
+
nil
|
68
|
+
|
69
|
+
upgrade and upgrade =~ /^websocket$/i and
|
70
|
+
connection and connection.split(/\s*,\s*/).include?('Upgrade') and
|
71
|
+
((!@protocols and !protocol) or @protocol) and
|
72
|
+
response['Sec-WebSocket-Accept'] == @accept
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Faye
|
2
|
+
class WebSocket
|
3
|
+
class HybiParser
|
4
|
+
|
5
|
+
class StreamReader
|
6
|
+
def initialize
|
7
|
+
@queue = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def read(length)
|
11
|
+
read_bytes(length)
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(bytes)
|
15
|
+
return unless bytes and bytes.size > 0
|
16
|
+
@queue.concat(bytes)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def read_bytes(length)
|
22
|
+
return nil if length > @queue.size
|
23
|
+
@queue.shift(length)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -15,7 +15,7 @@ WebSocketSteps = EM::RSpec.async_steps do
|
|
15
15
|
EM.next_tick(&callback)
|
16
16
|
end
|
17
17
|
|
18
|
-
def open_socket(url, &callback)
|
18
|
+
def open_socket(url, protocols, &callback)
|
19
19
|
done = false
|
20
20
|
|
21
21
|
resume = lambda do |open|
|
@@ -26,7 +26,7 @@ WebSocketSteps = EM::RSpec.async_steps do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
@ws = Faye::WebSocket::Client.new(url)
|
29
|
+
@ws = Faye::WebSocket::Client.new(url, protocols)
|
30
30
|
|
31
31
|
@ws.onopen = lambda { |e| resume.call(true) }
|
32
32
|
@ws.onclose = lambda { |e| resume.call(false) }
|
@@ -50,6 +50,11 @@ WebSocketSteps = EM::RSpec.async_steps do
|
|
50
50
|
callback.call
|
51
51
|
end
|
52
52
|
|
53
|
+
def check_protocol(protocol, &callback)
|
54
|
+
@ws.protocol.should == protocol
|
55
|
+
callback.call
|
56
|
+
end
|
57
|
+
|
53
58
|
def listen_for_message(&callback)
|
54
59
|
@ws.add_event_listener('message', lambda { |e| @message = e.data })
|
55
60
|
callback.call
|
@@ -74,6 +79,7 @@ end
|
|
74
79
|
describe Faye::WebSocket::Client do
|
75
80
|
include WebSocketSteps
|
76
81
|
|
82
|
+
let(:protocols) { ["foo", "echo"] }
|
77
83
|
let(:plain_text_url) { "ws://0.0.0.0:8000/" }
|
78
84
|
let(:secure_url) { "wss://0.0.0.0:8000/" }
|
79
85
|
|
@@ -84,23 +90,29 @@ describe Faye::WebSocket::Client do
|
|
84
90
|
|
85
91
|
shared_examples_for "socket client" do
|
86
92
|
it "can open a connection" do
|
87
|
-
open_socket(socket_url)
|
93
|
+
open_socket(socket_url, protocols)
|
88
94
|
check_open
|
95
|
+
check_protocol("echo")
|
89
96
|
end
|
90
97
|
|
91
98
|
it "cannot open a connection to the wrong host" do
|
92
|
-
open_socket(blocked_url)
|
99
|
+
open_socket(blocked_url, protocols)
|
100
|
+
check_closed
|
101
|
+
end
|
102
|
+
|
103
|
+
it "cannot open a connection with unacceptable protocols" do
|
104
|
+
open_socket(socket_url, ["foo"])
|
93
105
|
check_closed
|
94
106
|
end
|
95
107
|
|
96
108
|
it "can close the connection" do
|
97
|
-
open_socket(socket_url)
|
109
|
+
open_socket(socket_url, protocols)
|
98
110
|
close_socket
|
99
111
|
check_closed
|
100
112
|
end
|
101
113
|
|
102
114
|
describe "in the OPEN state" do
|
103
|
-
before { open_socket(socket_url) }
|
115
|
+
before { open_socket(socket_url, protocols) }
|
104
116
|
|
105
117
|
it "can send and receive messages" do
|
106
118
|
listen_for_message
|
@@ -111,7 +123,7 @@ describe Faye::WebSocket::Client do
|
|
111
123
|
|
112
124
|
describe "in the CLOSED state" do
|
113
125
|
before do
|
114
|
-
open_socket(socket_url)
|
126
|
+
open_socket(socket_url, protocols)
|
115
127
|
close_socket
|
116
128
|
end
|
117
129
|
|
@@ -11,21 +11,51 @@ describe Faye::WebSocket::Draft75Parser do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
describe :parse do
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
shared_examples_for "draft-75 parser" do
|
15
|
+
it "parses text frames" do
|
16
|
+
@web_socket.should_receive(:receive).with("Hello")
|
17
|
+
parse [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "parses multiple frames from the same packet" do
|
21
|
+
@web_socket.should_receive(:receive).with("Hello").exactly(2)
|
22
|
+
parse [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "parses text frames beginning 0x00-0x7F" do
|
26
|
+
@web_socket.should_receive(:receive).with("Hello")
|
27
|
+
parse [0x66, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "ignores frames with a length header" do
|
31
|
+
@web_socket.should_not_receive(:receive)
|
32
|
+
parse [0x80, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "parses text following an ignored block" do
|
36
|
+
@web_socket.should_receive(:receive).with("Hello")
|
37
|
+
parse [0x80, 0x02, 0x48, 0x65, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "parses multibyte text frames" do
|
41
|
+
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
42
|
+
parse [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "parses frames received in several packets" do
|
46
|
+
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
47
|
+
parse [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65]
|
48
|
+
parse [0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "parses fragmented frames" do
|
52
|
+
@web_socket.should_receive(:receive).with("Hello")
|
53
|
+
parse [0x00, 0x48, 0x65, 0x6c]
|
54
|
+
parse [0x6c, 0x6f, 0xff]
|
55
|
+
end
|
17
56
|
end
|
18
57
|
|
19
|
-
|
20
|
-
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
21
|
-
parse [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
|
22
|
-
end
|
23
|
-
|
24
|
-
it "parses fragmented frames" do
|
25
|
-
@web_socket.should_receive(:receive).with("Hello")
|
26
|
-
parse [0x00, 0x48, 0x65, 0x6c]
|
27
|
-
parse [0x6c, 0x6f, 0xff]
|
28
|
-
end
|
58
|
+
it_should_behave_like "draft-75 parser"
|
29
59
|
end
|
30
60
|
|
31
61
|
describe :frame do
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding=utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Faye::WebSocket::Draft76Parser do
|
6
|
+
include EncodingHelper
|
7
|
+
|
8
|
+
before do
|
9
|
+
@web_socket = mock Faye::WebSocket
|
10
|
+
@parser = Faye::WebSocket::Draft76Parser.new(@web_socket)
|
11
|
+
@parser.instance_eval { @handshake_complete = true }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe :parse do
|
15
|
+
it_should_behave_like "draft-75 parser"
|
16
|
+
|
17
|
+
it "closes the socket if a close frame is received" do
|
18
|
+
@web_socket.should_receive(:close)
|
19
|
+
parse [0xFF, 0x00]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe :frame do
|
24
|
+
it "returns the given string formatted as a WebSocket frame" do
|
25
|
+
bytes(@parser.frame "Hello").should == [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "encodes multibyte characters correctly" do
|
29
|
+
message = encode "Apple = "
|
30
|
+
bytes(@parser.frame message).should == [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
describe Faye::WebSocket::
|
5
|
+
describe Faye::WebSocket::HybiParser do
|
6
6
|
include EncodingHelper
|
7
|
-
|
7
|
+
|
8
8
|
before do
|
9
9
|
@web_socket = mock Faye::WebSocket
|
10
|
-
@parser = Faye::WebSocket::
|
10
|
+
@parser = Faye::WebSocket::HybiParser.new(@web_socket)
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
describe :parse do
|
14
14
|
let(:mask) { (1..4).map { rand 255 } }
|
15
|
-
|
15
|
+
|
16
16
|
def mask_message(*bytes)
|
17
17
|
output = []
|
18
18
|
bytes.each_with_index do |byte, i|
|
@@ -20,124 +20,135 @@ describe Faye::WebSocket::Protocol8Parser do
|
|
20
20
|
end
|
21
21
|
output
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it "parses unmasked text frames" do
|
25
25
|
@web_socket.should_receive(:receive).with("Hello")
|
26
26
|
parse [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
it "parses multiple frames from the same packet" do
|
30
|
+
@web_socket.should_receive(:receive).with("Hello").exactly(2)
|
31
|
+
parse [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
32
|
+
end
|
33
|
+
|
29
34
|
it "parses empty text frames" do
|
30
35
|
@web_socket.should_receive(:receive).with("")
|
31
36
|
parse [0x81, 0x00]
|
32
37
|
end
|
33
|
-
|
38
|
+
|
34
39
|
it "parses fragmented text frames" do
|
35
40
|
@web_socket.should_receive(:receive).with("Hello")
|
36
41
|
parse [0x01, 0x03, 0x48, 0x65, 0x6c]
|
37
42
|
parse [0x80, 0x02, 0x6c, 0x6f]
|
38
43
|
end
|
39
|
-
|
44
|
+
|
40
45
|
it "parses masked text frames" do
|
41
46
|
@web_socket.should_receive(:receive).with("Hello")
|
42
47
|
parse [0x81, 0x85] + mask + mask_message(0x48, 0x65, 0x6c, 0x6c, 0x6f)
|
43
48
|
end
|
44
|
-
|
49
|
+
|
45
50
|
it "parses masked empty text frames" do
|
46
51
|
@web_socket.should_receive(:receive).with("")
|
47
52
|
parse [0x81, 0x80] + mask + mask_message()
|
48
53
|
end
|
49
|
-
|
54
|
+
|
50
55
|
it "parses masked fragmented text frames" do
|
51
56
|
@web_socket.should_receive(:receive).with("Hello")
|
52
57
|
parse [0x01, 0x81] + mask + mask_message(0x48)
|
53
58
|
parse [0x80, 0x84] + mask + mask_message(0x65, 0x6c, 0x6c, 0x6f)
|
54
59
|
end
|
55
|
-
|
60
|
+
|
56
61
|
it "closes the socket if the frame has an unrecognized opcode" do
|
57
62
|
@web_socket.should_receive(:close).with(1002, nil, false)
|
58
63
|
parse [0x83, 0x00]
|
59
64
|
end
|
60
|
-
|
65
|
+
|
61
66
|
it "closes the socket if a close frame is received" do
|
62
67
|
@web_socket.should_receive(:close).with(1000, "Hello", false)
|
63
68
|
parse [0x88, 0x07, 0x03, 0xe8, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
64
69
|
end
|
65
|
-
|
70
|
+
|
66
71
|
it "parses unmasked multibyte text frames" do
|
67
72
|
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
68
73
|
parse [0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf]
|
69
74
|
end
|
70
|
-
|
75
|
+
|
76
|
+
it "parses frames received in several packets" do
|
77
|
+
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
78
|
+
parse [0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c]
|
79
|
+
parse [0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf]
|
80
|
+
end
|
81
|
+
|
71
82
|
it "parses fragmented multibyte text frames" do
|
72
83
|
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
73
84
|
parse [0x01, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3]
|
74
85
|
parse [0x80, 0x01, 0xbf]
|
75
86
|
end
|
76
|
-
|
87
|
+
|
77
88
|
it "parses masked multibyte text frames" do
|
78
89
|
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
79
90
|
parse [0x81, 0x8b] + mask + mask_message(0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf)
|
80
91
|
end
|
81
|
-
|
92
|
+
|
82
93
|
it "parses masked fragmented multibyte text frames" do
|
83
94
|
@web_socket.should_receive(:receive).with(encode "Apple = ")
|
84
95
|
parse [0x01, 0x8a] + mask + mask_message(0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3)
|
85
96
|
parse [0x80, 0x81] + mask + mask_message(0xbf)
|
86
97
|
end
|
87
|
-
|
98
|
+
|
88
99
|
it "parses unmasked medium-length text frames" do
|
89
100
|
@web_socket.should_receive(:receive).with("Hello" * 40)
|
90
101
|
parse [0x81, 0x7e, 0x00, 0xc8] + [0x48, 0x65, 0x6c, 0x6c, 0x6f] * 40
|
91
102
|
end
|
92
|
-
|
103
|
+
|
93
104
|
it "parses masked medium-length text frames" do
|
94
105
|
@web_socket.should_receive(:receive).with("Hello" * 40)
|
95
106
|
parse [0x81, 0xfe, 0x00, 0xc8] + mask + mask_message(*([0x48, 0x65, 0x6c, 0x6c, 0x6f] * 40))
|
96
107
|
end
|
97
|
-
|
108
|
+
|
98
109
|
it "replies to pings with a pong" do
|
99
110
|
@web_socket.should_receive(:send).with([0x4f, 0x48, 0x41, 0x49], :pong)
|
100
111
|
parse [0x89, 0x04, 0x4f, 0x48, 0x41, 0x49]
|
101
112
|
end
|
102
113
|
end
|
103
|
-
|
114
|
+
|
104
115
|
describe :frame do
|
105
116
|
it "returns the given string formatted as a WebSocket frame" do
|
106
117
|
bytes(@parser.frame "Hello").should == [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
107
118
|
end
|
108
|
-
|
119
|
+
|
109
120
|
it "encodes multibyte characters correctly" do
|
110
121
|
message = encode "Apple = "
|
111
122
|
bytes(@parser.frame message).should == [0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf]
|
112
123
|
end
|
113
|
-
|
124
|
+
|
114
125
|
it "encodes medium-length strings using extra length bytes" do
|
115
126
|
message = "Hello" * 40
|
116
127
|
bytes(@parser.frame message).should == [0x81, 0x7e, 0x00, 0xc8] + [0x48, 0x65, 0x6c, 0x6c, 0x6f] * 40
|
117
128
|
end
|
118
|
-
|
129
|
+
|
119
130
|
it "encodes long strings using extra length bytes" do
|
120
131
|
message = "Hello" * 13108
|
121
132
|
bytes(@parser.frame message).should == [0x81, 0x7f] +
|
122
133
|
[0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04] +
|
123
134
|
[0x48, 0x65, 0x6c, 0x6c, 0x6f] * 13108
|
124
135
|
end
|
125
|
-
|
136
|
+
|
126
137
|
it "encodes close frames with an error code" do
|
127
138
|
frame = @parser.frame "Hello", :close, 1002
|
128
139
|
bytes(frame).should == [0x88, 0x07, 0x03, 0xea, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
129
140
|
end
|
130
|
-
|
141
|
+
|
131
142
|
it "encodes pong frames" do
|
132
143
|
bytes(@parser.frame '', :pong).should == [0x8a, 0x00]
|
133
144
|
end
|
134
145
|
end
|
135
|
-
|
146
|
+
|
136
147
|
describe :utf8 do
|
137
148
|
it "detects valid UTF-8" do
|
138
149
|
Faye::WebSocket.valid_utf8?( [72, 101, 108, 108, 111, 45, 194, 181, 64, 195, 159, 195, 182, 195, 164, 195, 188, 195, 160, 195, 161, 45, 85, 84, 70, 45, 56, 33, 33] ).should be_true
|
139
150
|
end
|
140
|
-
|
151
|
+
|
141
152
|
it "detects invalid UTF-8" do
|
142
153
|
Faye::WebSocket.valid_utf8?( [206, 186, 225, 189, 185, 207, 131, 206, 188, 206, 181, 237, 160, 128, 101, 100, 105, 116, 101, 100] ).should be_false
|
143
154
|
end
|