bushido-faye 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +247 -0
- data/README.rdoc +92 -0
- data/lib/faye-browser-min.js +1 -0
- data/lib/faye.rb +121 -0
- data/lib/faye/adapters/rack_adapter.rb +209 -0
- data/lib/faye/engines/connection.rb +60 -0
- data/lib/faye/engines/memory.rb +112 -0
- data/lib/faye/engines/proxy.rb +111 -0
- data/lib/faye/error.rb +49 -0
- data/lib/faye/mixins/logging.rb +47 -0
- data/lib/faye/mixins/publisher.rb +30 -0
- data/lib/faye/mixins/timeouts.rb +22 -0
- data/lib/faye/protocol/channel.rb +124 -0
- data/lib/faye/protocol/client.rb +378 -0
- data/lib/faye/protocol/extensible.rb +43 -0
- data/lib/faye/protocol/grammar.rb +58 -0
- data/lib/faye/protocol/publication.rb +5 -0
- data/lib/faye/protocol/server.rb +282 -0
- data/lib/faye/protocol/subscription.rb +24 -0
- data/lib/faye/transport/http.rb +76 -0
- data/lib/faye/transport/local.rb +22 -0
- data/lib/faye/transport/transport.rb +115 -0
- data/lib/faye/transport/web_socket.rb +99 -0
- data/lib/faye/util/namespace.rb +20 -0
- data/spec/browser.html +45 -0
- data/spec/encoding_helper.rb +7 -0
- data/spec/install.sh +78 -0
- data/spec/javascript/channel_spec.js +15 -0
- data/spec/javascript/client_spec.js +714 -0
- data/spec/javascript/engine/memory_spec.js +7 -0
- data/spec/javascript/engine_spec.js +417 -0
- data/spec/javascript/faye_spec.js +15 -0
- data/spec/javascript/grammar_spec.js +66 -0
- data/spec/javascript/node_adapter_spec.js +307 -0
- data/spec/javascript/publisher_spec.js +27 -0
- data/spec/javascript/server/connect_spec.js +168 -0
- data/spec/javascript/server/disconnect_spec.js +121 -0
- data/spec/javascript/server/extensions_spec.js +60 -0
- data/spec/javascript/server/handshake_spec.js +145 -0
- data/spec/javascript/server/integration_spec.js +124 -0
- data/spec/javascript/server/publish_spec.js +85 -0
- data/spec/javascript/server/subscribe_spec.js +247 -0
- data/spec/javascript/server/unsubscribe_spec.js +245 -0
- data/spec/javascript/server_spec.js +110 -0
- data/spec/javascript/transport_spec.js +130 -0
- data/spec/node.js +55 -0
- data/spec/phantom.js +17 -0
- data/spec/ruby/channel_spec.rb +17 -0
- data/spec/ruby/client_spec.rb +724 -0
- data/spec/ruby/engine/memory_spec.rb +7 -0
- data/spec/ruby/engine_examples.rb +427 -0
- data/spec/ruby/faye_spec.rb +14 -0
- data/spec/ruby/grammar_spec.rb +68 -0
- data/spec/ruby/publisher_spec.rb +27 -0
- data/spec/ruby/rack_adapter_spec.rb +236 -0
- data/spec/ruby/server/connect_spec.rb +170 -0
- data/spec/ruby/server/disconnect_spec.rb +120 -0
- data/spec/ruby/server/extensions_spec.rb +68 -0
- data/spec/ruby/server/handshake_spec.rb +143 -0
- data/spec/ruby/server/integration_spec.rb +126 -0
- data/spec/ruby/server/publish_spec.rb +81 -0
- data/spec/ruby/server/subscribe_spec.rb +247 -0
- data/spec/ruby/server/unsubscribe_spec.rb +247 -0
- data/spec/ruby/server_spec.rb +110 -0
- data/spec/ruby/transport_spec.rb +134 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/testswarm +29 -0
- data/spec/thin_proxy.rb +37 -0
- metadata +302 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
JS.ENV.Server.DisconnectSpec = JS.Test.describe("Server disconnect", function() { with(this) {
|
2
|
+
before(function() { with(this) {
|
3
|
+
this.engine = {}
|
4
|
+
stub(Faye.Engine, "get").returns(engine)
|
5
|
+
this.server = new Faye.Server()
|
6
|
+
}})
|
7
|
+
|
8
|
+
describe("#disconnect", function() { with(this) {
|
9
|
+
before(function() { with(this) {
|
10
|
+
this.clientId = "fakeclientid"
|
11
|
+
this.message = {channel: "/meta/disconnect",
|
12
|
+
clientId: "fakeclientid"}
|
13
|
+
}})
|
14
|
+
|
15
|
+
describe("with valid parameters", function() { with(this) {
|
16
|
+
before(function() { with(this) {
|
17
|
+
expect(engine, "clientExists").given(clientId).yielding([true])
|
18
|
+
}})
|
19
|
+
|
20
|
+
it("destroys the client", function() { with(this) {
|
21
|
+
expect(engine, "destroyClient").given(clientId)
|
22
|
+
server.disconnect(message, false, function() {})
|
23
|
+
}})
|
24
|
+
|
25
|
+
it("returns a successful response", function() { with(this) {
|
26
|
+
stub(engine, "destroyClient")
|
27
|
+
server.disconnect(message, false, function(response) {
|
28
|
+
assertEqual({
|
29
|
+
channel: "/meta/disconnect",
|
30
|
+
successful: true,
|
31
|
+
clientId: clientId
|
32
|
+
}, response)
|
33
|
+
})
|
34
|
+
}})
|
35
|
+
|
36
|
+
describe("with a message id", function() { with(this) {
|
37
|
+
before(function() { this.message.id = "foo" })
|
38
|
+
|
39
|
+
it("returns the same id", function() { with(this) {
|
40
|
+
stub(engine, "destroyClient")
|
41
|
+
server.disconnect(message, false, function(response) {
|
42
|
+
assertEqual({
|
43
|
+
channel: "/meta/disconnect",
|
44
|
+
successful: true,
|
45
|
+
clientId: clientId,
|
46
|
+
id: "foo"
|
47
|
+
}, response)
|
48
|
+
})
|
49
|
+
}})
|
50
|
+
}})
|
51
|
+
}})
|
52
|
+
|
53
|
+
describe("with an unknown client", function() { with(this) {
|
54
|
+
before(function() { with(this) {
|
55
|
+
expect(engine, "clientExists").given(clientId).yielding([false])
|
56
|
+
}})
|
57
|
+
|
58
|
+
it("does not destroy the client", function() { with(this) {
|
59
|
+
expect(engine, "destroyClient").exactly(0)
|
60
|
+
server.disconnect(message, false, function() {})
|
61
|
+
}})
|
62
|
+
|
63
|
+
it("returns an unsuccessful response", function() { with(this) {
|
64
|
+
stub(engine, "destroyClient")
|
65
|
+
server.disconnect(message, false, function(response) {
|
66
|
+
assertEqual({
|
67
|
+
channel: "/meta/disconnect",
|
68
|
+
successful: false,
|
69
|
+
error: "401:fakeclientid:Unknown client"
|
70
|
+
}, response)
|
71
|
+
})
|
72
|
+
}})
|
73
|
+
}})
|
74
|
+
|
75
|
+
describe("missing clientId", function() { with(this) {
|
76
|
+
before(function() { with(this) {
|
77
|
+
delete message.clientId
|
78
|
+
expect(engine, "clientExists").given(undefined).yielding([false])
|
79
|
+
}})
|
80
|
+
|
81
|
+
it("does not destroy the client", function() { with(this) {
|
82
|
+
expect(engine, "destroyClient").exactly(0)
|
83
|
+
server.disconnect(message, false, function() {})
|
84
|
+
}})
|
85
|
+
|
86
|
+
it("returns an unsuccessful response", function() { with(this) {
|
87
|
+
stub(engine, "destroyClient")
|
88
|
+
server.disconnect(message, false, function(response) {
|
89
|
+
assertEqual({
|
90
|
+
channel: "/meta/disconnect",
|
91
|
+
successful: false,
|
92
|
+
error: "402:clientId:Missing required parameter"
|
93
|
+
}, response)
|
94
|
+
})
|
95
|
+
}})
|
96
|
+
}})
|
97
|
+
|
98
|
+
describe("with an error", function() { with(this) {
|
99
|
+
before(function() { with(this) {
|
100
|
+
message.error = "invalid"
|
101
|
+
expect(engine, "clientExists").given(clientId).yielding([true])
|
102
|
+
}})
|
103
|
+
|
104
|
+
it("does not destroy the client", function() { with(this) {
|
105
|
+
expect(engine, "destroyClient").exactly(0)
|
106
|
+
server.disconnect(message, false, function() {})
|
107
|
+
}})
|
108
|
+
|
109
|
+
it("returns an unsuccessful response", function() { with(this) {
|
110
|
+
stub(engine, "destroyClient")
|
111
|
+
server.disconnect(message, false, function(response) {
|
112
|
+
assertEqual({
|
113
|
+
channel: "/meta/disconnect",
|
114
|
+
successful: false,
|
115
|
+
error: "invalid"
|
116
|
+
}, response)
|
117
|
+
})
|
118
|
+
}})
|
119
|
+
}})
|
120
|
+
}})
|
121
|
+
}})
|
@@ -0,0 +1,60 @@
|
|
1
|
+
JS.ENV.Server.ExtensionsSpec = JS.Test.describe("Server extensions", function() { with(this) {
|
2
|
+
before(function() { with(this) {
|
3
|
+
this.engine = {}
|
4
|
+
stub(Faye.Engine, "get").returns(engine)
|
5
|
+
this.server = new Faye.Server()
|
6
|
+
}})
|
7
|
+
|
8
|
+
describe("with an incoming extension installed", function() { with(this) {
|
9
|
+
before(function() { with(this) {
|
10
|
+
var extension = {
|
11
|
+
incoming: function(message, callback) {
|
12
|
+
message.ext = {auth: "password"}
|
13
|
+
callback(message)
|
14
|
+
}
|
15
|
+
}
|
16
|
+
server.addExtension(extension)
|
17
|
+
this.message = {channel: "/foo", data: "hello"}
|
18
|
+
}})
|
19
|
+
|
20
|
+
it("passes incoming messages through the extension", function() { with(this) {
|
21
|
+
expect(engine, "publish").given({channel: "/foo", data: "hello", ext: {auth: "password"}})
|
22
|
+
server.process(message, false, function() {})
|
23
|
+
}})
|
24
|
+
|
25
|
+
it("does not pass outgoing messages through the extension", function() { with(this) {
|
26
|
+
stub(server, "handshake").yields([message])
|
27
|
+
stub(engine, "publish")
|
28
|
+
var response = null
|
29
|
+
server.process({channel: "/meta/handshake"}, false, function(r) { response = r })
|
30
|
+
assertEqual( [{channel: "/foo", data: "hello"}], response )
|
31
|
+
}})
|
32
|
+
}})
|
33
|
+
|
34
|
+
describe("with an outgoing extension installed", function() { with(this) {
|
35
|
+
before(function() { with(this) {
|
36
|
+
var extension = {
|
37
|
+
outgoing: function(message, callback) {
|
38
|
+
message.ext = {auth: "password"}
|
39
|
+
callback(message)
|
40
|
+
}
|
41
|
+
}
|
42
|
+
server.addExtension(extension)
|
43
|
+
this.message = {channel: "/foo", data: "hello"}
|
44
|
+
}})
|
45
|
+
|
46
|
+
it("does not pass incoming messages through the extension", function() { with(this) {
|
47
|
+
expect(engine, "publish").given({channel: "/foo", data: "hello"})
|
48
|
+
server.process(message, false, function() {})
|
49
|
+
}})
|
50
|
+
|
51
|
+
it("passes outgoing messages through the extension", function() { with(this) {
|
52
|
+
stub(server, "handshake").yields([message])
|
53
|
+
stub(engine, "publish")
|
54
|
+
var response = null
|
55
|
+
server.process({channel: "/meta/handshake"}, false, function(r) { response = r })
|
56
|
+
assertEqual( [{channel: "/foo", data: "hello", ext: {auth: "password"}}], response )
|
57
|
+
}})
|
58
|
+
}})
|
59
|
+
}})
|
60
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() { with(this) {
|
2
|
+
before(function() { with(this) {
|
3
|
+
this.engine = {}
|
4
|
+
stub(Faye.Engine, "get").returns(engine)
|
5
|
+
this.server = new Faye.Server()
|
6
|
+
|
7
|
+
this.connectionTypes = ["long-polling", "cross-origin-long-polling",
|
8
|
+
"callback-polling","websocket",
|
9
|
+
"eventsource","in-process"]
|
10
|
+
}})
|
11
|
+
|
12
|
+
describe("#handshake", function() { with(this) {
|
13
|
+
before(function() { with(this) {
|
14
|
+
this.message = {channel: "/meta/handshake",
|
15
|
+
version: "1.0",
|
16
|
+
supportedConnectionTypes: ["long-polling"]}
|
17
|
+
}})
|
18
|
+
|
19
|
+
describe("with valid parameters", function() { with(this) {
|
20
|
+
it("creates a client", function() { with(this) {
|
21
|
+
expect(engine, "createClient")
|
22
|
+
server.handshake(message, false, function() {})
|
23
|
+
}})
|
24
|
+
|
25
|
+
it("returns a successful response", function() { with(this) {
|
26
|
+
stub(engine, "createClient").yields(["clientid"])
|
27
|
+
server.handshake(message, false, function(response) {
|
28
|
+
assertEqual({
|
29
|
+
channel: "/meta/handshake",
|
30
|
+
successful: true,
|
31
|
+
version: "1.0",
|
32
|
+
supportedConnectionTypes: connectionTypes,
|
33
|
+
clientId: "clientid"
|
34
|
+
}, response)
|
35
|
+
})
|
36
|
+
}})
|
37
|
+
|
38
|
+
describe("with a message id", function() { with(this) {
|
39
|
+
before(function() { this.message.id = "foo" })
|
40
|
+
|
41
|
+
it("returns the same id", function() { with(this) {
|
42
|
+
stub(engine, "createClient").yields(["clientid"])
|
43
|
+
server.handshake(message, false, function(response) {
|
44
|
+
assertEqual({
|
45
|
+
channel: "/meta/handshake",
|
46
|
+
successful: true,
|
47
|
+
version: "1.0",
|
48
|
+
supportedConnectionTypes: connectionTypes,
|
49
|
+
clientId: "clientid",
|
50
|
+
id: "foo"
|
51
|
+
}, response)
|
52
|
+
})
|
53
|
+
}})
|
54
|
+
}})
|
55
|
+
}})
|
56
|
+
|
57
|
+
describe("missing version", function() { with(this) {
|
58
|
+
before(function() { delete this.message.version })
|
59
|
+
|
60
|
+
it("does not create a client", function() { with(this) {
|
61
|
+
expect(engine, "createClient").exactly(0)
|
62
|
+
server.handshake(message, false, function() {})
|
63
|
+
}})
|
64
|
+
|
65
|
+
it("returns an unsuccessful response", function() { with(this) {
|
66
|
+
server.handshake(message, false, function(response) {
|
67
|
+
assertEqual({
|
68
|
+
channel: "/meta/handshake",
|
69
|
+
successful: false,
|
70
|
+
error: "402:version:Missing required parameter",
|
71
|
+
version: "1.0",
|
72
|
+
supportedConnectionTypes: connectionTypes
|
73
|
+
}, response)
|
74
|
+
})
|
75
|
+
}})
|
76
|
+
}})
|
77
|
+
|
78
|
+
describe("missing supportedConnectionTypes", function() { with(this) {
|
79
|
+
before(function() { delete this.message.supportedConnectionTypes })
|
80
|
+
|
81
|
+
it("does not create a client", function() { with(this) {
|
82
|
+
expect(engine, "createClient").exactly(0)
|
83
|
+
server.handshake(message, false, function() {})
|
84
|
+
}})
|
85
|
+
|
86
|
+
it("returns an unsuccessful response", function() { with(this) {
|
87
|
+
server.handshake(message, false, function(response) {
|
88
|
+
assertEqual({
|
89
|
+
channel: "/meta/handshake",
|
90
|
+
successful: false,
|
91
|
+
error: "402:supportedConnectionTypes:Missing required parameter",
|
92
|
+
version: "1.0",
|
93
|
+
supportedConnectionTypes: connectionTypes
|
94
|
+
}, response)
|
95
|
+
})
|
96
|
+
}})
|
97
|
+
}})
|
98
|
+
|
99
|
+
describe("with no matching supportedConnectionTypes", function() { with(this) {
|
100
|
+
before(function() { with(this) {
|
101
|
+
message.supportedConnectionTypes = ["iframe", "flash"]
|
102
|
+
}})
|
103
|
+
|
104
|
+
it("does not create a client", function() { with(this) {
|
105
|
+
expect(engine, "createClient").exactly(0)
|
106
|
+
server.handshake(message, false, function() {})
|
107
|
+
}})
|
108
|
+
|
109
|
+
it("returns an unsuccessful response", function() { with(this) {
|
110
|
+
server.handshake(message, false, function(response) {
|
111
|
+
assertEqual({
|
112
|
+
channel: "/meta/handshake",
|
113
|
+
successful: false,
|
114
|
+
error: "301:iframe,flash:Connection types not supported",
|
115
|
+
version: "1.0",
|
116
|
+
supportedConnectionTypes: connectionTypes
|
117
|
+
}, response)
|
118
|
+
})
|
119
|
+
}})
|
120
|
+
}})
|
121
|
+
|
122
|
+
describe("with an error", function() { with(this) {
|
123
|
+
before(function() { with(this) {
|
124
|
+
message.error = "invalid"
|
125
|
+
}})
|
126
|
+
|
127
|
+
it("does not create a client", function() { with(this) {
|
128
|
+
expect(engine, "createClient").exactly(0)
|
129
|
+
server.handshake(message, false, function() {})
|
130
|
+
}})
|
131
|
+
|
132
|
+
it("returns an unsuccessful response", function() { with(this) {
|
133
|
+
server.handshake(message, false, function(response) {
|
134
|
+
assertEqual({
|
135
|
+
channel: "/meta/handshake",
|
136
|
+
successful: false,
|
137
|
+
error: "invalid",
|
138
|
+
version: "1.0",
|
139
|
+
supportedConnectionTypes: connectionTypes
|
140
|
+
}, response)
|
141
|
+
})
|
142
|
+
}})
|
143
|
+
}})
|
144
|
+
}})
|
145
|
+
}})
|
@@ -0,0 +1,124 @@
|
|
1
|
+
JS.ENV.IntegrationSteps = JS.Test.asyncSteps({
|
2
|
+
server: function(port, ssl, callback) {
|
3
|
+
var shared = __dirname + '/../../../examples/shared',
|
4
|
+
|
5
|
+
options = ssl
|
6
|
+
? { key: shared + '/server.key', cert: shared + '/server.crt' }
|
7
|
+
: null
|
8
|
+
|
9
|
+
this._adapter = new Faye.NodeAdapter({mount: "/bayeux", timeout: 2})
|
10
|
+
this._port = port
|
11
|
+
this._secure = ssl
|
12
|
+
this._adapter.listen(port, options, callback)
|
13
|
+
},
|
14
|
+
|
15
|
+
stop: function(callback) {
|
16
|
+
for (var id in this._clients) this._clients[id].disconnect()
|
17
|
+
var self = this
|
18
|
+
setTimeout(function() { self._adapter.stop(callback) }, 100)
|
19
|
+
},
|
20
|
+
|
21
|
+
client: function(name, channels, callback) {
|
22
|
+
var scheme = this._secure ? "https" : "http"
|
23
|
+
this._clients = this._clients || {}
|
24
|
+
this._inboxes = this._inboxes || {}
|
25
|
+
this._clients[name] = new Faye.Client(scheme + "://0.0.0.0:" + this._port + "/bayeux")
|
26
|
+
this._inboxes[name] = {}
|
27
|
+
|
28
|
+
var n = channels.length
|
29
|
+
if (n === 0) return this._clients[name].connect(callback)
|
30
|
+
|
31
|
+
for (var i = 0; i < n; i++)
|
32
|
+
(function(channel) {
|
33
|
+
var subscription = this._clients[name].subscribe(channel, function(message) {
|
34
|
+
this._inboxes[name][channel] = this._inboxes[name][channel] || []
|
35
|
+
this._inboxes[name][channel].push(message)
|
36
|
+
}, this)
|
37
|
+
subscription.callback(function() {
|
38
|
+
n -= 1
|
39
|
+
if (n === 0) callback()
|
40
|
+
})
|
41
|
+
}).call(this, channels[i]);
|
42
|
+
},
|
43
|
+
|
44
|
+
publish: function(name, channel, message, callback) {
|
45
|
+
this._clients[name].publish(channel, message)
|
46
|
+
setTimeout(callback, 100)
|
47
|
+
},
|
48
|
+
|
49
|
+
check_inbox: function(name, channel, messages, callback) {
|
50
|
+
var inbox = this._inboxes[name][channel] || []
|
51
|
+
this.assertEqual(messages, inbox)
|
52
|
+
callback()
|
53
|
+
}
|
54
|
+
})
|
55
|
+
|
56
|
+
JS.ENV.Server.IntegrationSpec = JS.Test.describe("Server integration", function() { with(this) {
|
57
|
+
include(IntegrationSteps)
|
58
|
+
|
59
|
+
sharedExamplesFor("message bus", function() { with(this) {
|
60
|
+
before(function() { with(this) {
|
61
|
+
server(8000, serverOptions.ssl)
|
62
|
+
client("alice", [])
|
63
|
+
client("bob", ["/foo"])
|
64
|
+
}})
|
65
|
+
|
66
|
+
after(function() { this.stop() })
|
67
|
+
|
68
|
+
it("delivers a message between clients", function() { with(this) {
|
69
|
+
publish("alice", "/foo", {hello: "world"})
|
70
|
+
check_inbox("bob", "/foo", [{hello: "world"}])
|
71
|
+
}})
|
72
|
+
|
73
|
+
it("does not deliver messages for unsubscribed channels", function() { with(this) {
|
74
|
+
publish("alice", "/bar", {hello: "world"})
|
75
|
+
check_inbox("bob", "/foo", [])
|
76
|
+
}})
|
77
|
+
|
78
|
+
it("delivers multiple messages", function() { with(this) {
|
79
|
+
publish("alice", "/foo", {hello: "world"})
|
80
|
+
publish("alice", "/foo", {hello: "world"})
|
81
|
+
check_inbox("bob", "/foo", [{hello: "world"},{hello: "world"}])
|
82
|
+
}})
|
83
|
+
|
84
|
+
it("delivers multibyte strings", function() { with(this) {
|
85
|
+
publish("alice", "/foo", {hello: "Apple = "})
|
86
|
+
check_inbox("bob", "/foo", [{hello: "Apple = "}])
|
87
|
+
}})
|
88
|
+
}})
|
89
|
+
|
90
|
+
sharedExamplesFor("network transports", function() { with(this) {
|
91
|
+
describe("with HTTP transport", function() { with(this) {
|
92
|
+
before(function() { with(this) {
|
93
|
+
stub(Faye.Transport.WebSocket, "isUsable").yields([false])
|
94
|
+
}})
|
95
|
+
|
96
|
+
itShouldBehaveLike("message bus")
|
97
|
+
}})
|
98
|
+
|
99
|
+
describe("with WebSocket transport", function() { with(this) {
|
100
|
+
before(function() { with(this) {
|
101
|
+
stub(Faye.Transport.WebSocket, "isUsable").yields([true])
|
102
|
+
}})
|
103
|
+
|
104
|
+
itShouldBehaveLike("message bus")
|
105
|
+
}})
|
106
|
+
}})
|
107
|
+
|
108
|
+
describe("with HTTP server", function() { with(this) {
|
109
|
+
before(function() { with(this) {
|
110
|
+
this.serverOptions = {ssl: false}
|
111
|
+
}})
|
112
|
+
|
113
|
+
itShouldBehaveLike("network transports")
|
114
|
+
}})
|
115
|
+
|
116
|
+
describe("with HTTPS server", function() { with(this) {
|
117
|
+
before(function() { with(this) {
|
118
|
+
this.serverOptions = {ssl: true}
|
119
|
+
}})
|
120
|
+
|
121
|
+
itShouldBehaveLike("network transports")
|
122
|
+
}})
|
123
|
+
}})
|
124
|
+
|