faye 0.6.4 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +9 -0
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +6 -1
- data/lib/faye/adapters/rack_adapter.rb +8 -2
- data/lib/{thin_extensions.rb → faye/thin_extensions.rb} +3 -21
- data/lib/faye/util/web_socket.rb +23 -10
- data/lib/faye/util/web_socket/draft75_parser.rb +19 -16
- data/lib/faye/util/web_socket/draft76_parser.rb +37 -36
- data/lib/faye/util/web_socket/protocol8_parser.rb +160 -85
- data/spec/javascript/engine_spec.js +6 -0
- data/spec/javascript/server/integration_spec.js +78 -0
- data/spec/javascript/web_socket/draft75parser_spec.js +39 -0
- data/spec/javascript/web_socket/protocol8parser_spec.js +130 -0
- data/spec/node.js +18 -0
- data/spec/ruby/engine_spec.rb +9 -0
- data/spec/ruby/server/integration_spec.rb +88 -0
- data/spec/ruby/web_socket/draft75_parser_spec.rb +41 -0
- data/spec/ruby/web_socket/protocol8_parser_spec.rb +120 -0
- data/spec/spec_helper.rb +19 -0
- metadata +9 -3
@@ -195,6 +195,12 @@ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this)
|
|
195
195
|
subscribe("alice", "/messages/foo")
|
196
196
|
}})
|
197
197
|
|
198
|
+
it("delivers multibyte messages correctly", function() { with(this) {
|
199
|
+
message.data = "Apple = "
|
200
|
+
publish(message)
|
201
|
+
expect_message("alice", [message])
|
202
|
+
}})
|
203
|
+
|
198
204
|
it("delivers messages to the subscribed client", function() { with(this) {
|
199
205
|
publish(message)
|
200
206
|
expect_message("alice", [message])
|
@@ -0,0 +1,78 @@
|
|
1
|
+
JS.ENV.IntegrationSteps = JS.Test.asyncSteps({
|
2
|
+
server: function(port, callback) {
|
3
|
+
this._adapter = new Faye.NodeAdapter({mount: "/bayeux", timeout: 25})
|
4
|
+
this._adapter.listen(port)
|
5
|
+
this._port = port
|
6
|
+
setTimeout(callback, 100)
|
7
|
+
},
|
8
|
+
|
9
|
+
stop: function(callback) {
|
10
|
+
this._adapter.stop()
|
11
|
+
setTimeout(callback, 100)
|
12
|
+
},
|
13
|
+
|
14
|
+
client: function(name, channels, callback) {
|
15
|
+
this._clients = this._clients || {}
|
16
|
+
this._inboxes = this._inboxes || {}
|
17
|
+
this._clients[name] = new Faye.Client("http://0.0.0.0:" + this._port + "/bayeux")
|
18
|
+
this._inboxes[name] = {}
|
19
|
+
|
20
|
+
var n = channels.length
|
21
|
+
if (n === 0) return callback()
|
22
|
+
|
23
|
+
Faye.each(channels, function(channel) {
|
24
|
+
var subscription = this._clients[name].subscribe(channel, function(message) {
|
25
|
+
this._inboxes[name][channel] = this._inboxes[name][channel] || []
|
26
|
+
this._inboxes[name][channel].push(message)
|
27
|
+
}, this)
|
28
|
+
subscription.callback(function() {
|
29
|
+
n -= 1
|
30
|
+
if (n === 0) callback()
|
31
|
+
})
|
32
|
+
}, this)
|
33
|
+
},
|
34
|
+
|
35
|
+
publish: function(name, channel, message, callback) {
|
36
|
+
this._clients[name].publish(channel, message)
|
37
|
+
setTimeout(callback, 100)
|
38
|
+
},
|
39
|
+
|
40
|
+
check_inbox: function(name, channel, messages, callback) {
|
41
|
+
var inbox = this._inboxes[name][channel] || []
|
42
|
+
this.assertEqual(messages, inbox)
|
43
|
+
callback()
|
44
|
+
}
|
45
|
+
})
|
46
|
+
|
47
|
+
JS.ENV.Server.IntegrationSpec = JS.Test.describe("Server integration", function() { with(this) {
|
48
|
+
include(IntegrationSteps)
|
49
|
+
|
50
|
+
before(function() { with(this) {
|
51
|
+
server(8000)
|
52
|
+
client("alice", [])
|
53
|
+
client("bob", ["/foo"])
|
54
|
+
}})
|
55
|
+
|
56
|
+
after(function() { this.stop() })
|
57
|
+
|
58
|
+
it("delivers a message between clients", function() { with(this) {
|
59
|
+
publish("alice", "/foo", {hello: "world"})
|
60
|
+
check_inbox("bob", "/foo", [{hello: "world"}])
|
61
|
+
}})
|
62
|
+
|
63
|
+
it("does not deliver messages for unsubscribed channels", function() { with(this) {
|
64
|
+
publish("alice", "/bar", {hello: "world"})
|
65
|
+
check_inbox("bob", "/foo", [])
|
66
|
+
}})
|
67
|
+
|
68
|
+
it("delivers multiple messages", function() { with(this) {
|
69
|
+
publish("alice", "/foo", {hello: "world"})
|
70
|
+
publish("alice", "/foo", {hello: "world"})
|
71
|
+
check_inbox("bob", "/foo", [{hello: "world"},{hello: "world"}])
|
72
|
+
}})
|
73
|
+
|
74
|
+
it("delivers multibyte strings", function() { with(this) {
|
75
|
+
publish("alice", "/foo", {hello: "Apple = "})
|
76
|
+
check_inbox("bob", "/foo", [{hello: "Apple = "}])
|
77
|
+
}})
|
78
|
+
}})
|
@@ -0,0 +1,39 @@
|
|
1
|
+
JS.ENV.WebSocket = JS.ENV.WebSocket || {}
|
2
|
+
|
3
|
+
JS.ENV.WebSocket.Draft75ParserSpec = JS.Test.describe("WebSocket.Draft75Parser", function() { with(this) {
|
4
|
+
before(function() { with(this) {
|
5
|
+
this.webSocket = {dispatchEvent: function() {}}
|
6
|
+
this.socket = new FakeSocket
|
7
|
+
this.parser = new Faye.WebSocket.Draft75Parser(webSocket, socket)
|
8
|
+
}})
|
9
|
+
|
10
|
+
describe("parse", function() { with(this) {
|
11
|
+
it("parses text frames", function() { with(this) {
|
12
|
+
expect(webSocket, "receive").given("Hello")
|
13
|
+
parser.parse([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff])
|
14
|
+
}})
|
15
|
+
|
16
|
+
it("parses multibyte text frames", function() { with(this) {
|
17
|
+
expect(webSocket, "receive").given("Apple = ")
|
18
|
+
parser.parse([0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff])
|
19
|
+
}})
|
20
|
+
|
21
|
+
it("parses fragmented frames", function() { with(this) {
|
22
|
+
expect(webSocket, "receive").given("Hello")
|
23
|
+
parser.parse([0x00, 0x48, 0x65, 0x6c])
|
24
|
+
parser.parse([0x6c, 0x6f, 0xff])
|
25
|
+
}})
|
26
|
+
}})
|
27
|
+
|
28
|
+
describe("frame", function() { with(this) {
|
29
|
+
it("returns the given string formatted as a WebSocket frame", function() { with(this) {
|
30
|
+
parser.frame("Hello")
|
31
|
+
assertEqual( [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], socket.read() )
|
32
|
+
}})
|
33
|
+
|
34
|
+
it("encodes multibyte characters correctly", function() { with(this) {
|
35
|
+
parser.frame("Apple = ")
|
36
|
+
assertEqual( [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff], socket.read() )
|
37
|
+
}})
|
38
|
+
}})
|
39
|
+
}})
|
@@ -0,0 +1,130 @@
|
|
1
|
+
JS.ENV.WebSocket = JS.ENV.WebSocket || {}
|
2
|
+
|
3
|
+
JS.ENV.WebSocket.Protocol8ParserSpec = JS.Test.describe("WebSocket.Protocol8Parser", function() { with(this) {
|
4
|
+
before(function() { with(this) {
|
5
|
+
this.webSocket = {dispatchEvent: function() {}}
|
6
|
+
this.socket = new FakeSocket
|
7
|
+
this.parser = new Faye.WebSocket.Protocol8Parser(webSocket, socket)
|
8
|
+
}})
|
9
|
+
|
10
|
+
define("parse", function() {
|
11
|
+
var bytes = [];
|
12
|
+
for (var i = 0, n = arguments.length; i < n; i++) bytes = bytes.concat(arguments[i])
|
13
|
+
this.parser.parse(new Buffer(bytes))
|
14
|
+
})
|
15
|
+
|
16
|
+
describe("parse", function() { with(this) {
|
17
|
+
define("mask", function() {
|
18
|
+
return this._mask = this._mask || Faye.map([1,2,3,4], function() { return Math.floor(Math.random() * 255) })
|
19
|
+
})
|
20
|
+
|
21
|
+
define("maskMessage", function(bytes) {
|
22
|
+
var output = []
|
23
|
+
Array.prototype.forEach.call(bytes, function(b, i) {
|
24
|
+
output[i] = bytes[i] ^ this.mask()[i % 4]
|
25
|
+
}, this)
|
26
|
+
return output
|
27
|
+
})
|
28
|
+
|
29
|
+
it("parses unmasked text frames", function() { with(this) {
|
30
|
+
expect(webSocket, "receive").given("Hello")
|
31
|
+
parse([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])
|
32
|
+
}})
|
33
|
+
|
34
|
+
it("parses fragmented text frames", function() { with(this) {
|
35
|
+
expect(webSocket, "receive").given("Hello")
|
36
|
+
parse([0x01, 0x03, 0x48, 0x65, 0x6c])
|
37
|
+
parse([0x80, 0x02, 0x6c, 0x6f])
|
38
|
+
}})
|
39
|
+
|
40
|
+
it("parses masked text frames", function() { with(this) {
|
41
|
+
expect(webSocket, "receive").given("Hello")
|
42
|
+
parse([0x81, 0x85], mask(), maskMessage([0x48, 0x65, 0x6c, 0x6c, 0x6f]))
|
43
|
+
}})
|
44
|
+
|
45
|
+
it("parses masked fragmented text frames", function() { with(this) {
|
46
|
+
expect(webSocket, "receive").given("Hello")
|
47
|
+
parse([0x01, 0x81], mask(), maskMessage([0x48]))
|
48
|
+
parse([0x80, 0x84], mask(), maskMessage([0x65, 0x6c, 0x6c, 0x6f]))
|
49
|
+
}})
|
50
|
+
|
51
|
+
it("closes the socket if the frame has an unrecognized opcode", function() { with(this) {
|
52
|
+
expect(webSocket, "close").given("protocol_error")
|
53
|
+
parse([0x83, 0x00])
|
54
|
+
}})
|
55
|
+
|
56
|
+
it("parses unmasked multibyte text frames", function() { with(this) {
|
57
|
+
expect(webSocket, "receive").given("Apple = ")
|
58
|
+
parse([0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf])
|
59
|
+
}})
|
60
|
+
|
61
|
+
it("parses fragmented multibyte text frames", function() { with(this) {
|
62
|
+
expect(webSocket, "receive").given("Apple = ")
|
63
|
+
parse([0x01, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3])
|
64
|
+
parse([0x80, 0x01, 0xbf])
|
65
|
+
}})
|
66
|
+
|
67
|
+
it("parses masked multibyte text frames", function() { with(this) {
|
68
|
+
expect(webSocket, "receive").given("Apple = ")
|
69
|
+
parse([0x81, 0x8b], mask(), maskMessage([0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf]))
|
70
|
+
}})
|
71
|
+
|
72
|
+
it("parses masked fragmented multibyte text frames", function() { with(this) {
|
73
|
+
expect(webSocket, "receive").given("Apple = ")
|
74
|
+
parse([0x01, 0x8a], mask(), maskMessage([0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3]))
|
75
|
+
parse([0x80, 0x81], mask(), maskMessage([0xbf]))
|
76
|
+
}})
|
77
|
+
|
78
|
+
it("parses unmasked medium-length text frames", function() { with(this) {
|
79
|
+
expect(webSocket, "receive").given("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
|
80
|
+
parse([129, 126, 0, 200, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111])
|
81
|
+
}})
|
82
|
+
|
83
|
+
it("parses masked medium-length text frames", function() { with(this) {
|
84
|
+
expect(webSocket, "receive").given("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
|
85
|
+
parse([129, 254, 0, 200], mask(), maskMessage([72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111]))
|
86
|
+
}})
|
87
|
+
|
88
|
+
it("replies to pings with a pong", function() { with(this) {
|
89
|
+
expect(webSocket, "send").given("OHAI", "pong")
|
90
|
+
parse([0x89, 0x04, 0x4f, 0x48, 0x41, 0x49])
|
91
|
+
}})
|
92
|
+
}})
|
93
|
+
|
94
|
+
describe("frame", function() { with(this) {
|
95
|
+
it("returns the given string formatted as a WebSocket frame", function() { with(this) {
|
96
|
+
parser.frame("Hello")
|
97
|
+
assertEqual( [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], socket.read() )
|
98
|
+
}})
|
99
|
+
|
100
|
+
it("encodes multibyte characters correctly", function() { with(this) {
|
101
|
+
parser.frame("Apple = ")
|
102
|
+
assertEqual( [0x81, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf], socket.read() )
|
103
|
+
}})
|
104
|
+
|
105
|
+
it("encodes medium-length strings using extra length bytes", function() { with(this) {
|
106
|
+
parser.frame("HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello")
|
107
|
+
assertEqual( [129, 126, 0, 200, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111, 72, 101, 108, 108, 111], socket.read() )
|
108
|
+
}})
|
109
|
+
|
110
|
+
it("encodes long strings using extra length bytes", function() { with(this) {
|
111
|
+
var reps = 13108, message = '', output = [0x81, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04]
|
112
|
+
while (reps--) {
|
113
|
+
message += "Hello"
|
114
|
+
output = output.concat([0x48, 0x65, 0x6c, 0x6c, 0x6f])
|
115
|
+
}
|
116
|
+
parser.frame(message)
|
117
|
+
assertEqual( output, socket.read() )
|
118
|
+
}})
|
119
|
+
|
120
|
+
it("encodes close frames with an error code", function() { with(this) {
|
121
|
+
parser.frame("Hello", "close", "protocol_error")
|
122
|
+
assertEqual( [0x88, 0x07, 0x03, 0xea, 0x48, 0x65, 0x6c, 0x6c, 0x6f], socket.read() )
|
123
|
+
}})
|
124
|
+
|
125
|
+
it("encodes pong frames", function() { with(this) {
|
126
|
+
parser.frame("", "pong")
|
127
|
+
assertEqual( [0x8a, 0x00], socket.read() )
|
128
|
+
}})
|
129
|
+
}})
|
130
|
+
}})
|
data/spec/node.js
CHANGED
@@ -6,6 +6,21 @@ JS.Packages(function() { with(this) {
|
|
6
6
|
autoload(/.*Spec/, {from: 'spec/javascript'})
|
7
7
|
}})
|
8
8
|
|
9
|
+
FakeSocket = function() {
|
10
|
+
this._fragments = []
|
11
|
+
}
|
12
|
+
FakeSocket.prototype.write = function(buffer, encoding) {
|
13
|
+
this._fragments.push([buffer, encoding])
|
14
|
+
}
|
15
|
+
FakeSocket.prototype.read = function() {
|
16
|
+
var output = []
|
17
|
+
this._fragments.forEach(function(buffer, i) {
|
18
|
+
for (var j = 0, n = buffer[0].length; j < n; j++)
|
19
|
+
output.push(buffer[0][j])
|
20
|
+
})
|
21
|
+
return output
|
22
|
+
}
|
23
|
+
|
9
24
|
JS.require('Faye', 'JS.Test', 'JS.Range', function() {
|
10
25
|
JS.Test.Unit.Assertions.include({
|
11
26
|
assertYield: function(expected) {
|
@@ -27,6 +42,9 @@ JS.require('Faye', 'JS.Test', 'JS.Range', function() {
|
|
27
42
|
'Server.SubscribeSpec',
|
28
43
|
'Server.UnsubscribeSpec',
|
29
44
|
'Server.ExtensionsSpec',
|
45
|
+
'Server.IntegrationSpec',
|
46
|
+
'WebSocket.Draft75ParserSpec',
|
47
|
+
'WebSocket.Protocol8ParserSpec',
|
30
48
|
'NodeAdapterSpec',
|
31
49
|
'ClientSpec',
|
32
50
|
'TransportSpec',
|
data/spec/ruby/engine_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding=utf-8
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
EngineSteps = EM::RSpec.async_steps do
|
@@ -94,6 +96,7 @@ describe "Pub/sub engines" do
|
|
94
96
|
end
|
95
97
|
|
96
98
|
shared_examples_for "faye engine" do
|
99
|
+
include EncodingHelper
|
97
100
|
include EngineSteps
|
98
101
|
|
99
102
|
let(:options) { {:timeout => 1} }
|
@@ -189,6 +192,12 @@ describe "Pub/sub engines" do
|
|
189
192
|
publish @message
|
190
193
|
expect_message :alice, [@message]
|
191
194
|
end
|
195
|
+
|
196
|
+
it "delivers multibyte messages correctly" do
|
197
|
+
@message["data"] = encode "Apple = "
|
198
|
+
publish @message
|
199
|
+
expect_message :alice, [@message]
|
200
|
+
end
|
192
201
|
end
|
193
202
|
|
194
203
|
describe "with a subscriber that is removed" do
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding=utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
require "thin"
|
6
|
+
Thin::Logging.silent = true
|
7
|
+
|
8
|
+
IntegrationSteps = EM::RSpec.async_steps do
|
9
|
+
def server(port, &callback)
|
10
|
+
@adapter = Faye::RackAdapter.new(:mount => "/bayeux", :timeout => 25)
|
11
|
+
@adapter.listen(port)
|
12
|
+
@port = port
|
13
|
+
EM.next_tick(&callback)
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop(&callback)
|
17
|
+
@adapter.stop
|
18
|
+
EM.next_tick(&callback)
|
19
|
+
end
|
20
|
+
|
21
|
+
def client(name, channels, &callback)
|
22
|
+
@clients ||= {}
|
23
|
+
@inboxes ||= {}
|
24
|
+
@clients[name] = Faye::Client.new("http://0.0.0.0:#{@port}/bayeux")
|
25
|
+
@inboxes[name] = {}
|
26
|
+
|
27
|
+
n = channels.size
|
28
|
+
return callback.call if n.zero?
|
29
|
+
|
30
|
+
channels.each do |channel|
|
31
|
+
subscription = @clients[name].subscribe(channel) do |message|
|
32
|
+
@inboxes[name][channel] ||= []
|
33
|
+
@inboxes[name][channel] << message
|
34
|
+
end
|
35
|
+
subscription.callback do
|
36
|
+
n -= 1
|
37
|
+
callback.call if n.zero?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def publish(name, channel, message, &callback)
|
43
|
+
@clients[name].publish(channel, message)
|
44
|
+
EM.add_timer(0.1, &callback)
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_inbox(name, channel, messages, &callback)
|
48
|
+
inbox = @inboxes[name][channel] || []
|
49
|
+
inbox.should == messages
|
50
|
+
callback.call
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "server integration" do
|
55
|
+
include IntegrationSteps
|
56
|
+
include EncodingHelper
|
57
|
+
|
58
|
+
before do
|
59
|
+
Faye.ensure_reactor_running!
|
60
|
+
server 8000
|
61
|
+
client :alice, []
|
62
|
+
client :bob, ["/foo"]
|
63
|
+
sync
|
64
|
+
end
|
65
|
+
|
66
|
+
after { stop }
|
67
|
+
|
68
|
+
it "delivers a message between clients" do
|
69
|
+
publish :alice, "/foo", {"hello" => "world"}
|
70
|
+
check_inbox :bob, "/foo", [{"hello" => "world"}]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does not deliver messages for unsubscribed channels" do
|
74
|
+
publish :alice, "/bar", {"hello" => "world"}
|
75
|
+
check_inbox :bob, "/foo", []
|
76
|
+
end
|
77
|
+
|
78
|
+
it "delivers multiple messages" do
|
79
|
+
publish :alice, "/foo", {"hello" => "world"}
|
80
|
+
publish :alice, "/foo", {"hello" => "world"}
|
81
|
+
check_inbox :bob, "/foo", [{"hello" => "world"}, {"hello" => "world"}]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "delivers multibyte strings" do
|
85
|
+
publish :alice, "/foo", {"hello" => encode("Apple = ")}
|
86
|
+
check_inbox :bob, "/foo", [{"hello" => encode("Apple = ")}]
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding=utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Faye::WebSocket::Draft75Parser do
|
6
|
+
include EncodingHelper
|
7
|
+
|
8
|
+
before do
|
9
|
+
@web_socket = mock Faye::WebSocket
|
10
|
+
@parser = Faye::WebSocket::Draft75Parser.new(@web_socket)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :parse do
|
14
|
+
it "parses text frames" do
|
15
|
+
@web_socket.should_receive(:receive).with("Hello")
|
16
|
+
parse [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "parses multibyte text frames" do
|
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
|
29
|
+
end
|
30
|
+
|
31
|
+
describe :frame do
|
32
|
+
it "returns the given string formatted as a WebSocket frame" do
|
33
|
+
bytes(@parser.frame "Hello").should == [0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "encodes multibyte characters correctly" do
|
37
|
+
message = encode "Apple = "
|
38
|
+
bytes(@parser.frame message).should == [0x00, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0xef, 0xa3, 0xbf, 0xff]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|