faye 0.7.2 → 0.8.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 might be problematic. Click here for more details.

Files changed (54) hide show
  1. data/History.txt +15 -3
  2. data/README.rdoc +2 -6
  3. data/lib/faye-browser-min.js +1 -1
  4. data/lib/faye.rb +25 -36
  5. data/lib/faye/adapters/rack_adapter.rb +43 -22
  6. data/lib/faye/engines/connection.rb +7 -10
  7. data/lib/faye/engines/memory.rb +28 -28
  8. data/lib/faye/engines/proxy.rb +109 -0
  9. data/lib/faye/mixins/logging.rb +1 -8
  10. data/lib/faye/mixins/timeouts.rb +1 -1
  11. data/lib/faye/protocol/channel.rb +3 -3
  12. data/lib/faye/protocol/client.rb +50 -45
  13. data/lib/faye/protocol/extensible.rb +11 -18
  14. data/lib/faye/protocol/server.rb +53 -38
  15. data/lib/faye/transport/http.rb +49 -27
  16. data/lib/faye/transport/transport.rb +3 -1
  17. data/lib/faye/transport/web_socket.rb +6 -10
  18. data/lib/faye/util/namespace.rb +2 -2
  19. data/spec/browser.html +3 -1
  20. data/spec/encoding_helper.rb +7 -0
  21. data/spec/javascript/client_spec.js +0 -5
  22. data/spec/javascript/engine/memory_spec.js +7 -0
  23. data/spec/javascript/engine_spec.js +22 -57
  24. data/spec/javascript/server/handshake_spec.js +10 -6
  25. data/spec/javascript/server/integration_spec.js +11 -10
  26. data/spec/javascript/server/publish_spec.js +85 -0
  27. data/spec/javascript/server_spec.js +5 -51
  28. data/spec/node.js +6 -6
  29. data/spec/ruby/client_spec.rb +1 -1
  30. data/spec/ruby/engine/memory_spec.rb +7 -0
  31. data/spec/ruby/{engine_spec.rb → engine_examples.rb} +28 -34
  32. data/spec/ruby/rack_adapter_spec.rb +1 -1
  33. data/spec/ruby/server/handshake_spec.rb +10 -6
  34. data/spec/ruby/server/publish_spec.rb +81 -0
  35. data/spec/ruby/server_spec.rb +6 -44
  36. data/spec/spec_helper.rb +5 -18
  37. data/spec/testswarm +1 -5
  38. metadata +105 -180
  39. data/lib/faye/engines/base.rb +0 -66
  40. data/lib/faye/engines/redis.rb +0 -225
  41. data/lib/faye/thin_extensions.rb +0 -75
  42. data/lib/faye/util/web_socket.rb +0 -89
  43. data/lib/faye/util/web_socket/api.rb +0 -103
  44. data/lib/faye/util/web_socket/client.rb +0 -82
  45. data/lib/faye/util/web_socket/draft75_parser.rb +0 -53
  46. data/lib/faye/util/web_socket/draft76_parser.rb +0 -53
  47. data/lib/faye/util/web_socket/protocol8_parser.rb +0 -324
  48. data/spec/javascript/web_socket/client_spec.js +0 -121
  49. data/spec/javascript/web_socket/draft75parser_spec.js +0 -39
  50. data/spec/javascript/web_socket/protocol8parser_spec.js +0 -153
  51. data/spec/redis.conf +0 -42
  52. data/spec/ruby/web_socket/client_spec.rb +0 -126
  53. data/spec/ruby/web_socket/draft75_parser_spec.rb +0 -41
  54. data/spec/ruby/web_socket/protocol8_parser_spec.rb +0 -145
@@ -8,45 +8,67 @@ module Faye
8
8
  def request(message, timeout)
9
9
  retry_block = retry_block(message, timeout)
10
10
 
11
- @client.cookies ||= CookieJar::Jar.new
12
- cookies = @client.cookies.get_cookies(@endpoint)
11
+ content = Faye.to_json(message)
12
+ cookies = @cookies.get_cookies(@endpoint)
13
+ params = build_params(URI.parse(@endpoint), content, cookies)
14
+ request = create_request(params)
13
15
 
14
- content = JSON.unparse(message)
15
- params = {
16
+ request.callback do
17
+ handle_response(request.response, retry_block)
18
+ store_cookies([*request.response_header['SET_COOKIE']].compact)
19
+ end
20
+ request.errback do
21
+ retry_block.call
22
+ trigger(:down)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def build_params(uri, content, cookies)
29
+ {
16
30
  :head => {
17
- 'Content-Length' => content.length,
31
+ 'Content-Length' => content.bytesize,
18
32
  'Content-Type' => 'application/json',
19
33
  'Cookie' => cookies * '; ',
20
- 'Host' => URI.parse(@endpoint).host
21
- },
34
+ 'Host' => uri.host
35
+ }.merge(@headers),
36
+
22
37
  :body => content,
23
38
  :timeout => -1 # for em-http-request < 1.0
24
39
  }
25
- if EventMachine::HttpRequest::VERSION.split('.')[0].to_i >= 1
26
- options = { # for em-http-request >= 1.0
27
- :inactivity_timeout => 0, # connection inactivity (post-setup) timeout (0 = disable timeout)
28
- }
29
- request = EventMachine::HttpRequest.new(@endpoint, options).post(params)
40
+ end
41
+
42
+ def create_request(params)
43
+ version = EventMachine::HttpRequest::VERSION.split('.')[0].to_i
44
+ client = if version >= 1
45
+ options = { # for em-http-request >= 1.0
46
+ :inactivity_timeout => 0 # connection inactivity (post-setup) timeout (0 = disable timeout)
47
+ }
48
+ EventMachine::HttpRequest.new(@endpoint, options)
49
+ else
50
+ EventMachine::HttpRequest.new(@endpoint)
51
+ end
52
+
53
+ client.post(params)
54
+ end
55
+
56
+ def handle_response(response, retry_block)
57
+ message = Yajl::Parser.parse(response) rescue nil
58
+ if message
59
+ receive(message)
60
+ trigger(:up)
30
61
  else
31
- request = EventMachine::HttpRequest.new(@endpoint).post(params)
32
- end
33
- request.callback do
34
- begin
35
- cookies = [*request.response_header['SET_COOKIE']]
36
- cookies.each do |cookie|
37
- @client.cookies.set_cookie(@endpoint, cookie)
38
- end
39
- receive(JSON.parse(request.response))
40
- trigger(:up)
41
- rescue
42
- retry_block.call
43
- end
44
- end
45
- request.errback do
46
62
  retry_block.call
47
63
  trigger(:down)
48
64
  end
49
65
  end
66
+
67
+ def store_cookies(cookies)
68
+ cookies.each do |cookie|
69
+ @cookies.set_cookie(@endpoint, cookie)
70
+ end
71
+ end
50
72
  end
51
73
 
52
74
  Transport.register 'long-polling', Transport::Http
@@ -5,6 +5,8 @@ module Faye
5
5
  include Publisher
6
6
  include Timeouts
7
7
 
8
+ attr_accessor :cookies, :headers
9
+
8
10
  def initialize(client, endpoint)
9
11
  debug('Created new ? transport for ?', connection_type, endpoint)
10
12
  @client = client
@@ -60,7 +62,7 @@ module Faye
60
62
 
61
63
  def retry_block(message, timeout)
62
64
  lambda do
63
- EventMachine.add_timer(timeout) { request(message, 2 * timeout) }
65
+ EventMachine.add_timer(@client.retry) { request(message, timeout) }
64
66
  end
65
67
  end
66
68
 
@@ -37,10 +37,9 @@ module Faye
37
37
  end
38
38
 
39
39
  def request(messages, timeout = nil)
40
- @timeout = timeout || @timeout
41
40
  @messages ||= {}
42
41
  messages.each { |message| @messages[message['id']] = message }
43
- with_socket { |socket| socket.send(JSON.unparse(messages)) }
42
+ with_socket { |socket| socket.send(Faye.to_json(messages)) }
44
43
  end
45
44
 
46
45
  def with_socket(&resume)
@@ -71,7 +70,7 @@ module Faye
71
70
  end
72
71
 
73
72
  @socket.onmessage = lambda do |event|
74
- messages = [JSON.parse(event.data)].flatten
73
+ messages = [Yajl::Parser.parse(event.data)].flatten
75
74
  messages.each { |message| @messages.delete(message['id']) }
76
75
  receive(messages)
77
76
  end
@@ -82,13 +81,10 @@ module Faye
82
81
  @state = UNCONNECTED
83
82
  @socket = nil
84
83
 
85
- if was_connected
86
- resend
87
- else
88
- EventMachine.add_timer(@timeout) { connect }
89
- @timeout = @timeout * 2
90
- trigger(:down)
91
- end
84
+ next resend if was_connected
85
+
86
+ EventMachine.add_timer(@client.retry) { connect }
87
+ trigger(:down)
92
88
  end
93
89
  end
94
90
 
@@ -10,8 +10,8 @@ module Faye
10
10
  end
11
11
 
12
12
  def generate
13
- name = Faye.random
14
- name = Faye.random while @used.has_key?(name)
13
+ name = Engine.random
14
+ name = Engine.random while @used.has_key?(name)
15
15
  @used[name] = name
16
16
  end
17
17
 
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
5
5
  <title>Faye test suite</title>
6
- <script type="text/javascript" src="../vendor/js.class/build/min/loader.js"></script>
6
+ <script type="text/javascript" src="../node_modules/jsclass/min/loader.js"></script>
7
7
  </head>
8
8
  <body>
9
9
  <script type="text/javascript">
@@ -16,6 +16,8 @@
16
16
  heartbeat: function() {}
17
17
  }
18
18
 
19
+ JS.cacheBust = true
20
+
19
21
  JS.Packages(function() { with(this) {
20
22
  file('../build/faye-browser-min.js').provides('Faye')
21
23
  autoload(/.*Spec/, {from: './javascript'})
@@ -0,0 +1,7 @@
1
+ module EncodingHelper
2
+ def encode(string)
3
+ return string unless string.respond_to?(:force_encoding)
4
+ string.force_encoding("UTF-8")
5
+ end
6
+ end
7
+
@@ -583,11 +583,6 @@ JS.ENV.ClientSpec = JS.Test.describe("Client", function() { with(this) {
583
583
  client.publish("/messages/foo", {hello: "world"})
584
584
  }})
585
585
 
586
- it("throws an error when publishing to an invalid channel", function() { with(this) {
587
- expect(transport, "send").given(objectIncluding({channel: "/messages/*"}), 60).exactly(0)
588
- assertThrows(Error, function() { client.publish("/messages/*", {hello: "world"}) })
589
- }})
590
-
591
586
  describe("on publish failure", function() { with(this) {
592
587
  before(function() { with(this) {
593
588
  stubResponse({channel: "/messages/foo",
@@ -0,0 +1,7 @@
1
+ JS.ENV.Engine.MemorySpec = JS.Test.describe("Memory engine", function() { with(this) {
2
+ before(function() {
3
+ this.engineOpts = {type: Faye.Engine.Memory}
4
+ })
5
+
6
+ itShouldBehaveLike("faye engine")
7
+ }})
@@ -12,11 +12,11 @@ JS.ENV.EngineSteps = JS.Test.asyncSteps({
12
12
  connect: function(name, engine, resume) {
13
13
  var clientId = this._clients[name]
14
14
  var inboxes = this._inboxes
15
- engine.connect(clientId, {}, function(m) {
16
- Faye.each(m, function(message) {
17
- delete message.id
18
- inboxes[name].push(message)
19
- })
15
+ engine.connect(clientId, {}, function(messages) {
16
+ for (var i = 0, n = messages.length; i < n; i++) {
17
+ delete messages[i].id
18
+ inboxes[name].push(messages[i])
19
+ }
20
20
  })
21
21
  setTimeout(resume, 10)
22
22
  },
@@ -32,7 +32,7 @@ JS.ENV.EngineSteps = JS.Test.asyncSteps({
32
32
 
33
33
  check_num_clients: function(n, resume) {
34
34
  var ids = new JS.Set()
35
- Faye.each(this._clients, function(name, id) { ids.add(id) })
35
+ for (var key in this._clients) ids.add(this._clients[key])
36
36
  this.assertEqual(n, ids.count())
37
37
  resume()
38
38
  },
@@ -55,10 +55,10 @@ JS.ENV.EngineSteps = JS.Test.asyncSteps({
55
55
 
56
56
  publish: function(messages, resume) {
57
57
  messages = [].concat(messages)
58
- Faye.each(messages, function(message) {
59
- message = Faye.extend({id: Faye.random()}, message)
58
+ for (var i = 0, n = messages.length; i < n; i++) {
59
+ var message = Faye.extend({id: Faye.random()}, messages[i])
60
60
  this.engine.publish(message)
61
- }, this)
61
+ }
62
62
  setTimeout(resume, 20)
63
63
  },
64
64
 
@@ -108,29 +108,18 @@ JS.ENV.EngineSteps = JS.Test.asyncSteps({
108
108
  check_different_messages: function(a, b, resume) {
109
109
  this.assertNotSame(this._inboxes[a][0], this._inboxes[b][0])
110
110
  resume()
111
- },
112
-
113
- clean_redis_db: function(resume) {
114
- this.engine.disconnect()
115
- var redis = require('redis').createClient(6379, 'localhost', {no_ready_check: true})
116
- redis.auth(this.engineOpts.password)
117
- redis.flushall(function() {
118
- redis.end()
119
- resume()
120
- })
121
111
  }
122
112
  })
123
113
 
124
114
  JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this) {
125
- include(JS.Test.Helpers)
126
-
127
- define("create_engine", function() { with(this) {
128
- var opts = Faye.extend(options(), engineOpts)
129
- return new engineKlass(opts)
130
- }})
131
-
132
115
  sharedExamplesFor("faye engine", function() { with(this) {
116
+ include(JS.Test.Helpers)
133
117
  include(EngineSteps)
118
+
119
+ define("create_engine", function() { with(this) {
120
+ var opts = Faye.extend(options(), engineOpts)
121
+ return new Faye.Engine.Proxy(opts)
122
+ }})
134
123
 
135
124
  define("options", function() { return {timeout: 1} })
136
125
 
@@ -390,7 +379,14 @@ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this)
390
379
  }})
391
380
 
392
381
  sharedBehavior("distributed engine", function() { with(this) {
382
+ include(JS.Test.Helpers)
393
383
  include(EngineSteps)
384
+
385
+ define("create_engine", function() { with(this) {
386
+ var opts = Faye.extend(options(), engineOpts)
387
+ return new Faye.Engine.Proxy(opts)
388
+ }})
389
+
394
390
  define("options", function() { return {timeout: 1} })
395
391
 
396
392
  before(function() { with(this) {
@@ -418,35 +414,4 @@ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this)
418
414
  }})
419
415
  }})
420
416
  }})
421
-
422
- describe("Faye.Engine.Memory", function() { with(this) {
423
- before(function() {
424
- this.engineKlass = Faye.Engine.Memory
425
- this.engineOpts = {}
426
- })
427
-
428
- itShouldBehaveLike("faye engine")
429
- }})
430
-
431
- describe("Faye.Engine.Redis", function() { with(this) {
432
- before(function() {
433
- this.engineKlass = Faye.Engine.Redis
434
- this.engineOpts = {password: "foobared", namespace: new Date().getTime().toString()}
435
- })
436
- after(function() { this.clean_redis_db() })
437
-
438
- itShouldBehaveLike("faye engine")
439
-
440
- describe("distribution", function() { with(this) {
441
- itShouldBehaveLike("distributed engine")
442
- }})
443
-
444
- describe("using a Unix socket", function() { with(this) {
445
- before(function() { with(this) {
446
- this.engineOpts.socket = "/tmp/redis.sock"
447
- }})
448
-
449
- itShouldBehaveLike("faye engine")
450
- }})
451
- }})
452
417
  }})
@@ -3,6 +3,10 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
3
3
  this.engine = {}
4
4
  stub(Faye.Engine, "get").returns(engine)
5
5
  this.server = new Faye.Server()
6
+
7
+ this.connectionTypes = ["long-polling", "cross-origin-long-polling",
8
+ "callback-polling","websocket",
9
+ "eventsource","in-process"]
6
10
  }})
7
11
 
8
12
  describe("#handshake", function() { with(this) {
@@ -25,7 +29,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
25
29
  channel: "/meta/handshake",
26
30
  successful: true,
27
31
  version: "1.0",
28
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"],
32
+ supportedConnectionTypes: connectionTypes,
29
33
  clientId: "clientid"
30
34
  }, response)
31
35
  })
@@ -41,7 +45,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
41
45
  channel: "/meta/handshake",
42
46
  successful: true,
43
47
  version: "1.0",
44
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"],
48
+ supportedConnectionTypes: connectionTypes,
45
49
  clientId: "clientid",
46
50
  id: "foo"
47
51
  }, response)
@@ -65,7 +69,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
65
69
  successful: false,
66
70
  error: "402:version:Missing required parameter",
67
71
  version: "1.0",
68
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"]
72
+ supportedConnectionTypes: connectionTypes
69
73
  }, response)
70
74
  })
71
75
  }})
@@ -86,7 +90,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
86
90
  successful: false,
87
91
  error: "402:supportedConnectionTypes:Missing required parameter",
88
92
  version: "1.0",
89
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"]
93
+ supportedConnectionTypes: connectionTypes
90
94
  }, response)
91
95
  })
92
96
  }})
@@ -109,7 +113,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
109
113
  successful: false,
110
114
  error: "301:iframe,flash:Connection types not supported",
111
115
  version: "1.0",
112
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"]
116
+ supportedConnectionTypes: connectionTypes
113
117
  }, response)
114
118
  })
115
119
  }})
@@ -132,7 +136,7 @@ JS.ENV.Server.HandshakeSpec = JS.Test.describe("Server handshake", function() {
132
136
  successful: false,
133
137
  error: "invalid",
134
138
  version: "1.0",
135
- supportedConnectionTypes: ["long-polling", "cross-origin-long-polling", "callback-polling", "websocket","in-process"]
139
+ supportedConnectionTypes: connectionTypes
136
140
  }, response)
137
141
  })
138
142
  }})
@@ -28,16 +28,17 @@ JS.ENV.IntegrationSteps = JS.Test.asyncSteps({
28
28
  var n = channels.length
29
29
  if (n === 0) return this._clients[name].connect(callback)
30
30
 
31
- Faye.each(channels, function(channel) {
32
- var subscription = this._clients[name].subscribe(channel, function(message) {
33
- this._inboxes[name][channel] = this._inboxes[name][channel] || []
34
- this._inboxes[name][channel].push(message)
35
- }, this)
36
- subscription.callback(function() {
37
- n -= 1
38
- if (n === 0) callback()
39
- })
40
- }, this)
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]);
41
42
  },
42
43
 
43
44
  publish: function(name, channel, message, callback) {
@@ -0,0 +1,85 @@
1
+ JS.ENV.Server.PublishSpec = JS.Test.describe("Server publish", 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.message = {channel: "/some/channel", data: "publish"}
8
+ }})
9
+
10
+ describe("publishing a message", function() { with(this) {
11
+ it("tells the engine to publish the message", function() { with(this) {
12
+ expect(engine, "publish").given(message)
13
+ server.process(message, false, function() {})
14
+ }})
15
+
16
+ it("returns a successful response", function() { with(this) {
17
+ stub(engine, "publish")
18
+ server.process(message, false, function(response) {
19
+ assertEqual([
20
+ { channel: "/some/channel",
21
+ successful: true
22
+ }
23
+ ], response)
24
+ })
25
+ }})
26
+
27
+ describe("with an invalid channel", function() { with(this) {
28
+ before(function() { with(this) {
29
+ message.channel = "channel"
30
+ }})
31
+
32
+ it("does not tell the engine to publish the message", function() { with(this) {
33
+ expect(engine, "publish").exactly(0)
34
+ server.process(message, false, function() {})
35
+ }})
36
+
37
+ it("returns an unsuccessful response", function() { with(this) {
38
+ stub(engine, "publish")
39
+ server.process(message, false, function(response) {
40
+ assertEqual([
41
+ { channel: "channel",
42
+ successful: false,
43
+ error: "405:channel:Invalid channel"
44
+ }
45
+ ], response)
46
+ })
47
+ }})
48
+ }})
49
+
50
+ describe("with an error", function() { with(this) {
51
+ before(function() { with(this) {
52
+ message.error = "invalid"
53
+ }})
54
+
55
+ it("does not tell the engine to publish the message", function() { with(this) {
56
+ expect(engine, "publish").exactly(0)
57
+ server.process(message, false, function() {})
58
+ }})
59
+
60
+ it("returns an unsuccessful response", function() { with(this) {
61
+ stub(engine, "publish")
62
+ server.process(message, false, function(response) {
63
+ assertEqual([
64
+ { channel: "/some/channel",
65
+ successful: false,
66
+ error: "invalid"
67
+ }
68
+ ], response)
69
+ })
70
+ }})
71
+ }})
72
+
73
+ describe("to an invalid channel", function() { with(this) {
74
+ before(function() { with(this) {
75
+ message.channel = "/invalid/*"
76
+ }})
77
+
78
+ it("does not tell the engine to publish the message", function() { with(this) {
79
+ expect(engine, "publish").exactly(0)
80
+ server.process(message, false, function() {})
81
+ }})
82
+ }})
83
+ }})
84
+ }})
85
+