myxi 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afd97a9d7707f7273a9cc1526c16b838840de982
4
- data.tar.gz: c3a82e6d83e6da41657ba23d17bcfec4f4bea14a
3
+ metadata.gz: f04936ddd50485e99372aa1248f99bf41fbc6c61
4
+ data.tar.gz: 16b9d860964f5f8c733697cc13001977949ca541
5
5
  SHA512:
6
- metadata.gz: c25c8b738510f2e80d1897a85353705474157b3ac6446f4109b896dddede06ca87bd38123b972d1cc6624799560ed2f96c4a7cfc7608778388e64adc775e44f1
7
- data.tar.gz: bf1995d685b86924c6a346a932c30f46ba8ce633d8593cb3133a04bc6923d68de22bbb5fd3d0df57dbf621ada5093e32c7e3701b6b24002940a688b7e1150f5c
6
+ metadata.gz: 0aabc52d678b70c8fc7d929bb00d9b876b5fc8a7c847cc9d7f8be0f3350a21579f7cb309cc88d1b25f9f57dc6f2effe74de9e59278ae97143a7932822ae265f8
7
+ data.tar.gz: 4e9b22ef5ba86dc807a8f8240a510c40ae2602fde0f47f9cc177e9a1857724b0114ab0fc2e21395d87c6595b483718dca8bd9a2e05b90924198f58f7ab63b5d7
@@ -1,6 +1,11 @@
1
+ require 'myxi/exchange'
2
+ require 'myxi/railtie' if defined?(Rails)
3
+
1
4
  module Myxi
2
5
  class << self
3
6
 
7
+ class Error < StandardError; end
8
+
4
9
  #
5
10
  # Return a bunny client instance which will be used by the web socket service.
6
11
  # This can be overriden if you already have a connection RabbitMQ available
@@ -1,3 +1,5 @@
1
+ require 'myxi/environment'
2
+
1
3
  module Myxi
2
4
  class Action
3
5
 
@@ -13,7 +15,12 @@ module Myxi
13
15
  end
14
16
 
15
17
  def execute(session, payload = {})
16
- @block.call(session, payload)
18
+ environment = Environment.new(session, payload)
19
+ environment.instance_exec(session, payload, &@block)
20
+ rescue Environment::Error => e
21
+ session.send('Error', :error => e.class.to_s.split('::').last)
22
+ rescue => e
23
+ session.send('InternalError', :error => e.class.to_s, :message => e.message)
17
24
  end
18
25
 
19
26
  end
@@ -1,13 +1,33 @@
1
1
  Myxi::Action.add(:Subscribe) do |session, payload|
2
- session.subscribe(payload['exchange'], payload['routing_key'])
2
+ if payload['routing_keys'].is_a?(Array)
3
+ for key in payload['routing_keys']
4
+ session.subscribe(payload['exchange'], key)
5
+ end
6
+ else
7
+ session.subscribe(payload['exchange'], payload['routing_key'])
8
+ end
3
9
  end
4
10
 
5
11
  Myxi::Action.add(:Unsubscribe) do |session, payload|
6
12
  if payload['exchange'] && payload['routing_key']
7
- session.unsubscribe(payload['exchange'], payload['routing_key'])
13
+ if payload['routing_keys'].is_a?(Array)
14
+ for key in payload['routing_keys']
15
+ session.unsubscribe(payload['exchange'], key)
16
+ end
17
+ else
18
+ session.unsubscribe(payload['exchange'], payload['routing_key'])
19
+ end
8
20
  elsif payload['exchange'] && payload['routing_key'].nil?
9
21
  session.unsubscribe_all_for_exchange(payload['exchange'])
10
22
  else
11
23
  session.unsubscribe_all
12
24
  end
13
25
  end
26
+
27
+ Myxi::Action.add(:ListSubscriptions) do |session, payload|
28
+ session.send "YourSubscriptions", :subscriptions => session.subscriptions
29
+ end
30
+
31
+ Myxi::Action.add(:Ping) do |session, payload|
32
+ session.send "Pong", :time => Time.now.to_i
33
+ end
@@ -0,0 +1,21 @@
1
+ module Myxi
2
+ class Environment
3
+
4
+ class Error < StandardError; end
5
+ class AuthRequired < Error; end
6
+
7
+ def initialize(session, payload = {})
8
+ @session, @payload = session, payload
9
+ end
10
+
11
+ attr_reader :session
12
+ attr_reader :payload
13
+
14
+ def auth_required!
15
+ if session.auth_object.nil?
16
+ raise AuthRequired, "Authentication is required for this action"
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module Myxi
2
+ class Railtie < ::Rails::Engine
3
+
4
+ initializer 'myxi.initialize' do |app|
5
+ end
6
+
7
+ end
8
+ end
@@ -19,14 +19,30 @@ module Myxi
19
19
  end
20
20
  end
21
21
 
22
+ def sessions
23
+ @sessions ||= []
24
+ end
25
+
26
+ def monitor_sessions
27
+ unless options[:touch_interval] == 0
28
+ Thread.new do
29
+ loop do
30
+ sessions.each(&:touch)
31
+ sleep options[:touch_interval] || 60
32
+ end
33
+ end
34
+ end
35
+ end
36
+
22
37
  def run
23
38
  Myxi::Exchange.declare_all
24
39
  port = (options[:port] || ENV['MYXI_PORT'] || ENV['PORT'] || 5005).to_i
25
40
  puts "Running Myxi Web Socket Server on 0.0.0.0:#{port}"
41
+ monitor_sessions
26
42
  EM.run do
27
43
  EM::WebSocket.run(:host => options[:bind_address] || ENV['MYXI_BIND_ADDRESS'] || '0.0.0.0', :port => port) do |ws|
28
44
 
29
- session = Session.new(ws)
45
+ sessions << session = Session.new(self, ws)
30
46
 
31
47
  ws.onopen do |handshake|
32
48
  case handshake.path
@@ -36,7 +52,10 @@ module Myxi
36
52
 
37
53
  session.queue = Myxi.channel.queue("", :exclusive => true)
38
54
  session.queue.subscribe do |delivery_info, properties, body|
39
- ws.send(body)
55
+ if hash = JSON.parse(body) rescue nil
56
+ hash['mq'] = {'e' => delivery_info.exchange, 'rk' => delivery_info.routing_key}
57
+ ws.send(hash.to_json.force_encoding('UTF-8'))
58
+ end
40
59
  end
41
60
  else
42
61
  log "[#{session.id}] Invalid path"
@@ -48,11 +67,13 @@ module Myxi
48
67
  ws.onclose do
49
68
  log "[#{session.id}] Disconnected"
50
69
  session.queue.delete if session.queue
70
+ sessions.delete(session)
51
71
  end
52
72
 
53
73
  ws.onmessage do |msg|
54
74
  if ws.state == :connected
55
- if json = JSON.parse(msg) rescue nil
75
+ json = JSON.parse(msg) rescue nil
76
+ if json.is_a?(Hash)
56
77
  session.tag = json['tag'] || nil
57
78
  payload = json['payload'] || {}
58
79
  if action = Myxi::Action::ACTIONS[json['action'].to_sym]
@@ -4,12 +4,14 @@ require 'myxi/exchange'
4
4
  module Myxi
5
5
  class Session
6
6
 
7
- def initialize(ws)
7
+ def initialize(server, ws)
8
+ @server = server
8
9
  @ws = ws
9
10
  @id = SecureRandom.hex(8)
10
11
  end
11
12
 
12
13
  attr_reader :id
14
+ attr_reader :server
13
15
  attr_reader :ws
14
16
  attr_accessor :queue
15
17
  attr_accessor :auth_object
@@ -26,7 +28,7 @@ module Myxi
26
28
  # Send an event back to the client on this session
27
29
  #
28
30
  def send(name, payload = {})
29
- ws.send({:event => name, :tag => tag, :payload => payload}.to_json)
31
+ ws.send({:event => name, :tag => tag, :payload => payload}.to_json.force_encoding('UTF-8'))
30
32
  end
31
33
 
32
34
  #
@@ -38,7 +40,7 @@ module Myxi
38
40
  queue.bind(exchange.exchange_name.to_s, :routing_key => routing_key.to_s)
39
41
  subscriptions[exchange_name.to_s] ||= []
40
42
  subscriptions[exchange_name.to_s] << routing_key.to_s
41
- puts "[#{id}] Subscribed to #{exchange_name} / #{routing_key}"
43
+ server.log "[#{id}] Subscribed to #{exchange_name} / #{routing_key}"
42
44
  send('Subscribed', :exchange => exchange_name, :routing_key => routing_key)
43
45
  else
44
46
  send('Error', :error => 'SubscriptionDenied', :exchange => exchange_name, :routing_key => routing_key)
@@ -51,12 +53,13 @@ module Myxi
51
53
  #
52
54
  # Unsubscribe this session from the given exchange name and routing key
53
55
  #
54
- def unsubscribe(exchange_name, routing_key)
56
+ def unsubscribe(exchange_name, routing_key, auto = false)
55
57
  queue.unbind(exchange_name.to_s, :routing_key => routing_key.to_s)
56
58
  if subscriptions[exchange_name.to_s]
57
59
  subscriptions[exchange_name.to_s].delete(routing_key.to_s)
58
60
  end
59
- send('Unsubscribed', :exchange_name => exchange_name, :routing_key => routing_key)
61
+ server.log "[#{id}] Unsubscribed from #{exchange_name}/#{routing_key}"
62
+ send('Unsubscribed', :exchange_name => exchange_name, :routing_key => routing_key, :auto => auto)
60
63
  end
61
64
 
62
65
  #
@@ -79,5 +82,22 @@ module Myxi
79
82
  end
80
83
  end
81
84
 
85
+ #
86
+ # Called by the server every so often whenever this session is active. This
87
+ # should verify that subscriptions are still valid etc...
88
+ #
89
+ def touch
90
+ subscriptions.each do |exchange_name, routing_keys|
91
+ if exchange = Myxi::Exchange::EXCHANGES[exchange_name.to_sym]
92
+ routing_keys.each do |routing_key|
93
+ unless exchange.can_subscribe?(routing_key, self.auth_object)
94
+ puts "[#{id}] Session is not longer allowed to subscibe to #{exchange_name}/#{routing_key}"
95
+ unsubscribe(exchange_name, routing_key, true)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
82
102
  end
83
103
  end
@@ -1,3 +1,3 @@
1
1
  module Myxi
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myxi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Cooke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-20 00:00:00.000000000 Z
11
+ date: 2016-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -60,7 +60,9 @@ files:
60
60
  - lib/myxi.rb
61
61
  - lib/myxi/action.rb
62
62
  - lib/myxi/default_actions.rb
63
+ - lib/myxi/environment.rb
63
64
  - lib/myxi/exchange.rb
65
+ - lib/myxi/railtie.rb
64
66
  - lib/myxi/server.rb
65
67
  - lib/myxi/session.rb
66
68
  - lib/myxi/version.rb
@@ -89,3 +91,4 @@ signing_key:
89
91
  specification_version: 4
90
92
  summary: A RabbitMQ-based web socket server & framework
91
93
  test_files: []
94
+ has_rdoc: