tamashii-manager 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3615a69b16faac2730ca38fa2f42161db5e89d50
4
- data.tar.gz: 31482ad7e7c1c76fe0dee4651f0b7f21cf53e347
3
+ metadata.gz: b73434b81719979d45631fb55ee06dbc9c681dfb
4
+ data.tar.gz: 1271d4bbe0123c2bc284ba716958df9cf3e75f23
5
5
  SHA512:
6
- metadata.gz: f89bf3e52e8a1121518e385e07b96287d472159e1321ed9ba46dceb484eec2bf5cebd568244f5cf923e7e78307326ca3c9840a6725a352c04c7d9e7d6d1afceb
7
- data.tar.gz: c982c585c7088e7e795e28a221c00262d88ad3f88a8d061457002af66a47630b1ad830f37211dc0d345f7e9c4db655c56bb4f81bf1fbf0f1d8c2979faa04a753
6
+ metadata.gz: da6357c531d820f6f1147c7a8fcf972edaaaedba892db2a75b6553fb96eb09654e063666e4cc8ecaf6334ec5f7a27079ad29273643967bebd28a5c7d1ebe4f9c
7
+ data.tar.gz: ef266d3eaad776acbb73c66a6f1d3a5c99cc7bace32ec7d2e9b53541a7c13d1af17a26381e63752dd4ded688df3f542e32699c167878e779d57d6ce01a52d37b
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.1
data/Gemfile CHANGED
@@ -2,4 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in codeme-manager.gemspec
4
4
  gemspec
5
-
data/exe/tamashii-manager CHANGED
@@ -2,36 +2,37 @@
2
2
 
3
3
  require 'rack'
4
4
  require 'optparse'
5
- require 'tamashii/manager/version'
6
- require 'tamashii/manager/server'
7
5
  require 'tamashii/manager'
8
6
 
9
7
  options = {
10
8
  Port: ENV['PORT'] || 3000,
11
- Host: "0.0.0.0",
9
+ Host: '0.0.0.0',
12
10
  AccessLog: []
13
11
  }
14
12
 
15
13
  OptionParser.new do |opts|
16
- opts.on("-v", "--version", "Display Tamashii::Manager version") {
14
+ opts.on('-v', '--version', 'Display Tamashii::Manager version') do
17
15
  puts "Tamashii::Manager #{Tamashii::Manager::VERSION}"
18
16
  exit
19
- }
17
+ end
20
18
 
21
- opts.on("-h", "--help") {
19
+ opts.on('-h', '--help') do
22
20
  puts opts
23
21
  exit
24
- }
25
-
26
- opts.separator ""
27
- opts.on("-s", "--server SERVER", "Run Tamashii::Manager server") { |name| handlers.unshift(name.to_s) }
28
- opts.on("-o", "--host HOST", "The listen on HOST (default: 0.0.0.0)") { |host| options[:Host] = host.to_s; puts host }
29
- opts.on("-p", "--port PORT", "The listen on PORT (default: 3000)") { |port| options[:Port] = port.to_i }
30
- opts.on("-C", "--config FILE", "The external configuration file") do |config|
31
- if File.exists? config
22
+ end
23
+
24
+ # rubocop:disable Metrics/LineLength
25
+ opts.separator ''
26
+ opts.on('-s', '--server SERVER', 'Run Tamashii::Manager server') { |name| handlers.unshift(name.to_s) }
27
+ opts.on('-o', '--host HOST', 'The listen on HOST (default: 0.0.0.0)') { |host| options[:Host] = host.to_s }
28
+ opts.on('-p', '--port PORT', 'The listen on PORT (default: 3000)') { |port| options[:Port] = port.to_i }
29
+ opts.on('-C', '--config FILE', 'The external configuration file') do |config|
30
+ if File.exist? config
32
31
  require config
32
+ options[:Port] = Tamashii::Manager::Config.port
33
33
  end
34
34
  end
35
+ # rubocop:enable Metrics/LineLength
35
36
 
36
37
  opts.parse! ARGV
37
38
  end
@@ -40,10 +41,12 @@ begin
40
41
  config = Tamashii::Manager::Config
41
42
  case config.auth_type
42
43
  when :token
43
- raise LoadError.new("Token authorization require to set token") if config.token.nil?
44
+ # rubocop:disable Metrics/LineLength
45
+ raise LoadError, 'Token authorization require to set token' if config.token.nil?
46
+ # rubocop:enable Metrics/LineLength
44
47
  end
45
48
 
46
- Rack::Handler.default.run Tamashii::Manager::Server, options
49
+ Rack::Handler.default.run Tamashii::Manager.server, options
47
50
  rescue LoadError => e
48
51
  # TODO: Improve error message
49
52
  STDERR.puts e
@@ -1,23 +1,41 @@
1
- require "tamashii/manager/server"
2
- require "tamashii/manager/version"
3
- require "tamashii/manager/config"
4
- require "tamashii/manager/authorization"
5
- require "tamashii/manager/handler/broadcaster"
6
- require "tamashii/manager/clients"
7
- require "tamashii/common"
1
+ # frozen_string_literal: true
8
2
 
9
- Tamashii::Resolver.default_handler Tamashii::Manager::Handler::Broadcaster
10
- Tamashii::Resolver.handle Tamashii::Type::AUTH_TOKEN, Tamashii::Manager::Authorization
3
+ require 'tamashii/server'
4
+ require 'tamashii/common'
5
+ require 'tamashii/manager/version'
6
+
7
+ require 'tamashii/manager/subscription'
8
+ require 'tamashii/manager/config'
9
+ require 'tamashii/manager/client_manager'
10
+ require 'tamashii/manager/client'
11
+ require 'tamashii/manager/channel'
12
+ require 'tamashii/manager/channel_pool'
13
+ require 'tamashii/manager/authorization'
14
+ require 'tamashii/manager/authorizator'
15
+ require 'tamashii/manager/handler'
16
+ require 'tamashii/manager/error'
17
+ require 'tamashii/manager/server'
11
18
 
12
19
  module Tamashii
20
+ # :nodoc:
13
21
  module Manager
14
22
  def self.config(&block)
15
- return Config.class_eval(&block) if block_given?
23
+ return instance_exec(Config.instance, &block) if block_given?
16
24
  Config
17
25
  end
18
26
 
19
27
  def self.logger
20
- @logger ||= Tamashii::Logger.new(Config.log_file)
28
+ @logger ||= ::Logger.new(config.log_file)
29
+ end
30
+
31
+ def self.server
32
+ @server ||= Tamashii::Manager::Server.new
21
33
  end
22
34
  end
23
35
  end
36
+
37
+ # TODO: Use block mode to define resolver
38
+ # rubocop:disable Metrics/LineLength
39
+ Tamashii::Resolver.default_handler Tamashii::Manager::Handler::Broadcaster
40
+ Tamashii::Resolver.handle Tamashii::Type::AUTH_TOKEN, Tamashii::Manager::Authorization
41
+ # rubocop:enable Metrics/LineLength
@@ -1,16 +1,16 @@
1
- require "tamashii/manager/errors/authorization_error"
2
- require "tamashii/manager/authorizator/token"
3
- require "tamashii/common"
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Tamashii
6
4
  module Manager
5
+ # :nodoc:
7
6
  class Authorization < Tamashii::Handler
8
7
  def resolve(data = nil)
9
8
  type, client_id = case @type
10
9
  when Tamashii::Type::AUTH_TOKEN
11
10
  Authorizator::Token.new.verify!(data)
12
11
  else
13
- raise AuthorizationError.new("Invalid authorization type.")
12
+ raise Error::AuthorizationError,
13
+ 'Invalid authorization type.'
14
14
  end
15
15
  @env[:client].accept(type, client_id)
16
16
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ # :nodoc:
6
+ module Authorizator
7
+ autoload :Token, 'tamashii/manager/authorizator/token'
8
+ end
9
+ end
10
+ end
@@ -1,9 +1,9 @@
1
- require "tamashii/manager/errors/authorization_error"
2
- require "tamashii/manager/config"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Tamashii
5
4
  module Manager
6
5
  module Authorizator
6
+ # :nodoc:
7
7
  class Token
8
8
  attr_reader :client_id
9
9
 
@@ -16,8 +16,8 @@ module Tamashii
16
16
  def verify!(data)
17
17
  @type, @client_id, token = data.split(",")
18
18
  Manager.logger.debug("Client #{@client_id} try to verify token: #{Config.env.production? ? "FILTERED" : token}")
19
- raise AuthorizationError.new("Token mismatch!") unless @authorized = Config.token == token
20
- raise AuthorizationError.new("Device type not available!") unless Type::CLIENT.values.include?(@type.to_i)
19
+ raise Error::AuthorizationError, "Token mismatch!" unless @authorized = Config.token == token
20
+ raise Error::AuthorizationError, "Device type not available!" unless Type::CLIENT.values.include?(@type.to_i)
21
21
  [@type.to_i, @client_id]
22
22
  end
23
23
  end
@@ -1,7 +1,8 @@
1
- require 'tamashii/manager/channel_pool'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Tamashii
4
4
  module Manager
5
+ # :nodoc:
5
6
  class Channel < Set
6
7
  SERVER_ID = 0
7
8
 
@@ -23,7 +24,7 @@ module Tamashii
23
24
  when :checkin
24
25
  servers
25
26
  else
26
- return pool.get_idle || pool.create! if pool[client.tag].nil?
27
+ return pool.idle || pool.create! if pool[client.tag].nil?
27
28
  pool[client.tag]
28
29
  end
29
30
  end
@@ -35,7 +36,9 @@ module Tamashii
35
36
 
36
37
  pool.ready(channel)
37
38
 
38
- Manager.logger.info("Client #{client.id} subscribe to Channel ##{channel.id}")
39
+ Manager.logger.info(
40
+ "Client #{client.id} subscribe to Channel ##{channel.id}"
41
+ )
39
42
 
40
43
  channel
41
44
  end
@@ -44,12 +47,18 @@ module Tamashii
44
47
  channel = select_channel(client)
45
48
  channel.delete(client)
46
49
 
47
- Manager.logger.info("Client #{client.id} unsubscribe to Channel ##{channel.id}")
50
+ Manager.logger.info(
51
+ "Client #{client.id} unsubscribe to Channel ##{channel.id}"
52
+ )
48
53
 
49
- if channel.empty? && channel.id != SERVER_ID
50
- pool.idle(channel.id)
51
- Manager.logger.debug("Channel Pool add - ##{channel.id}, available channels: #{pool.idle.size}")
52
- end
54
+ idle_channel(channel) if channel.empty? && channel.id != SERVER_ID
55
+ end
56
+
57
+ def idle_channel(channel)
58
+ pool.idle(channel.id)
59
+ # rubocop:disable Metrics/LineLength
60
+ Manager.logger.debug("Channel Pool add - ##{channel.id}, available channels: #{pool.idle.size}")
61
+ # rubocop:enable Metrics/LineLength
53
62
  end
54
63
  end
55
64
 
@@ -61,7 +70,8 @@ module Tamashii
61
70
  end
62
71
 
63
72
  def send_to(channel_id, packet)
64
- return unless channel = Channel.pool[channel_id]
73
+ channel = Channel.pool[channel_id]
74
+ return unless channel.nil?
65
75
  channel.broadcast(packet)
66
76
  end
67
77
 
@@ -70,12 +80,15 @@ module Tamashii
70
80
  each do |client|
71
81
  client.send(packet)
72
82
  end
83
+
84
+ # rubocop:disable Metrics/LineLength
73
85
  Channel.servers.broadcast(packet) unless id == SERVER_ID || exclude_server
86
+ # rubocop:enable Metrics/LineLength
74
87
  end
75
88
 
76
89
  def broadcast_all(packet)
77
- Channel.pool.each do |id, channel|
78
- channel.broadcast(packet, true) unless channel.nil?
90
+ Channel.pool.each do |_id, channel|
91
+ channel&.broadcast(packet, true)
79
92
  end
80
93
  Channel.servers.broadcast(packet) unless id == SERVER_ID
81
94
  end
@@ -1,42 +1,39 @@
1
- require 'tamashii/manager/channel'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Tamashii
4
4
  module Manager
5
+ # :nodoc:
5
6
  class ChannelPool < Hash
7
+ attr_reader :idles
8
+
6
9
  def initialize(size = 10)
7
- @idle = []
10
+ @idles = []
8
11
  @ptr = 1
9
12
 
10
13
  size.times { create! }
11
14
  end
12
15
 
13
16
  def create!
14
- @idle << Channel.new(@ptr)
17
+ @idles << Channel.new(@ptr)
15
18
  @ptr += 1
16
19
  end
17
20
 
18
21
  def idle(channel_id = nil)
19
- return @idle if channel_id.nil?
22
+ return @idles.first if channel_id.nil?
20
23
  return unless self[channel_id]&.empty?
21
- @idle << self[channel_id]
24
+ @idles << self[channel_id]
22
25
  self[channel_id] = nil
23
26
  end
24
27
 
25
28
  def ready(channel)
26
29
  return if channel.empty?
27
30
  self[channel.id] = channel
28
- if @idle.include?(channel)
29
- @idle.delete(channel)
30
- end
31
+ @idles.delete(channel) if @idles.include?(channel)
31
32
  channel
32
33
  end
33
34
 
34
35
  def available?
35
- !@idle.empty?
36
- end
37
-
38
- def get_idle
39
- @idle.first
36
+ !@idles.empty?
40
37
  end
41
38
  end
42
39
  end
@@ -1,12 +1,10 @@
1
- require "websocket/driver"
2
- require "tamashii/manager/stream"
3
- require "tamashii/manager/channel"
4
- require "tamashii/manager/authorization"
5
- require "tamashii/common"
1
+ # frozen_string_literal: true
6
2
 
7
3
  module Tamashii
8
4
  module Manager
9
- class Client
5
+ # :nodoc:
6
+ class Client < Tamashii::Server::Connection::Base
7
+ include ClientManager
10
8
 
11
9
  attr_reader :env, :url
12
10
  attr_reader :channel
@@ -16,39 +14,6 @@ module Tamashii
16
14
 
17
15
  attr_accessor :tag
18
16
 
19
- def self.accepted_clients
20
- Clients.instance
21
- end
22
-
23
- def initialize(env, event_loop)
24
- @env = env
25
- @id = nil
26
- @type = Type::CLIENT[:agent]
27
- @last_beat_timestamp = Time.at(0)
28
- @last_response_time = Float::INFINITY
29
-
30
- secure = Rack::Request.new(env).ssl?
31
- scheme = secure ? 'wss:' : 'ws:'
32
- @url = "#{scheme}//#{env['HTTP_HOST']}#{env['REQUEST_URI']}"
33
-
34
- Manager.logger.info("Accept connection from #{env['REMOTE_ADDR']}")
35
-
36
- @driver = WebSocket::Driver.rack(self)
37
-
38
- env['rack.hijack'].call
39
- @io = env['rack.hijack_io']
40
-
41
- Connection.register(self)
42
- @stream = Stream.new(event_loop, @io, self)
43
-
44
- @driver.on(:open) { |e| on_open }
45
- @driver.on(:message) { |e| receive(e.data) }
46
- @driver.on(:close) { |e| on_close(e) }
47
- @driver.on(:error) { |e| emit_error(e.message) }
48
-
49
- @driver.start
50
- end
51
-
52
17
  def id
53
18
  return "<Unauthorized : #{@env['REMOTE_ADDR']}>" if @id.nil?
54
19
  @id
@@ -58,17 +23,9 @@ module Tamashii
58
23
  Type::CLIENT.key(@type)
59
24
  end
60
25
 
61
- def write(buffer)
62
- @io.write(buffer)
63
- end
64
-
65
26
  def send(packet)
66
27
  packet = packet.dump if packet.is_a?(Tamashii::Packet)
67
- @driver.binary(packet)
68
- end
69
-
70
- def parse(buffer)
71
- @driver.parse(buffer)
28
+ @socket.transmit(packet)
72
29
  end
73
30
 
74
31
  def authorized?
@@ -79,25 +36,18 @@ module Tamashii
79
36
  @id = id
80
37
  @type = type
81
38
  @channel = Channel.subscribe(self)
82
- Clients.register(self)
83
- send(Tamashii::Packet.new(Tamashii::Type::AUTH_RESPONSE, @channel.id, true).dump)
84
- end
85
-
86
- def close
87
- @driver.close
88
- end
89
-
90
- def shutdown
91
- Connection.unregister(self)
92
- if authorized?
93
- Channel.unsubscribe(self)
94
- Clients.unregister(self)
95
- end
39
+ packet = Tamashii::Packet.new(
40
+ Tamashii::Type::AUTH_RESPONSE,
41
+ @channel.id,
42
+ true
43
+ )
44
+ Client[id] = self
45
+ send packet.dump
96
46
  end
97
47
 
98
48
  def beat
99
49
  beat_time = Time.now
100
- @driver.ping("heart-beat-at-#{beat_time}") do
50
+ @socket.ping("heart-beat-at-#{beat_time}") do
101
51
  heartbeat_callback(beat_time)
102
52
  end
103
53
  end
@@ -105,36 +55,53 @@ module Tamashii
105
55
  def heartbeat_callback(beat_time)
106
56
  @last_beat_timestamp = Time.now
107
57
  @last_response_time = @last_beat_timestamp - beat_time
108
- Manager.logger.debug "[#{@id}] Heart beat #{beat_time} returns at #{@last_beat_timestamp}! Delay: #{(@last_response_time * 1000).round} ms"
58
+ Manager.logger.debug(
59
+ "[#{id}] Heart beat #{beat_time} " \
60
+ "returns at #{@last_beat_timestamp}!" \
61
+ " Delay: #{(@last_response_time * 1000).round} ms"
62
+ )
109
63
  end
110
64
 
111
- private
112
65
  def on_open
113
66
  Manager.logger.info("Client #{id} is ready")
114
67
  end
115
68
 
116
- def receive(data)
69
+ def on_message(data)
117
70
  Manager.logger.debug("Receive Data: #{data}")
118
71
  return unless data.is_a?(Array)
119
72
  Tamashii::Resolver.resolve(Tamashii::Packet.load(data), client: self)
120
- rescue AuthorizationError => e
121
- Manager.logger.error("Client #{id} authentication failed => #{e.message}")
122
- send(Tamashii::Packet.new(Tamashii::Type::AUTH_RESPONSE, 0, false))
123
- @driver.close
73
+ rescue AuthorizationError => reason
74
+ close_on_authorize_failed(reason)
124
75
  rescue => e
125
- Manager.logger.error("Error when receiving data from client #{id}: #{e.message}")
126
- Manager.logger.debug("Backtrace:")
127
- e.backtrace.each {|msg| Manager.logger.debug(msg)}
76
+ on_message_error(e)
128
77
  end
129
78
 
130
- def on_close(e)
79
+ def on_close
131
80
  Manager.logger.info("Client #{id} closed connection")
132
- @stream.close
81
+ Client[id] = nil
133
82
  end
134
83
 
135
84
  def emit_error(message)
136
85
  Manager.logger.error("Client #{id} has error => #{message}")
137
86
  end
87
+
88
+ private
89
+
90
+ def close_on_authorize_failed(reason)
91
+ Manager.logger.error(
92
+ "Client #{id} authentication failed => #{reason.message}"
93
+ )
94
+ send(Tamashii::Packet.new(Tamashii::Type::AUTH_RESPONSE, 0, false))
95
+ @socket.close
96
+ end
97
+
98
+ def on_message_error(e)
99
+ Manager.logger.error(
100
+ "Error when receiving data from client #{id}: #{e.message}"
101
+ )
102
+ Manager.logger.debug('Backtrace:')
103
+ e.backtrace.each { |msg| Manager.logger.debug(msg) }
104
+ end
138
105
  end
139
106
  end
140
107
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ # :nodoc:
6
+ module ClientManager
7
+ # rubocop:disable Metrics/MethodLength
8
+ def self.included(other)
9
+ other.class_eval do
10
+ class << self
11
+ def accepted_clients
12
+ @accepted_clients ||= {}
13
+ end
14
+
15
+ def [](name)
16
+ accepted_clients[name.to_s]
17
+ end
18
+
19
+ def []=(name, client)
20
+ return unless client.is_a?(Client)
21
+ accepted_clients[name.to_s] = client
22
+ end
23
+
24
+ def sent_to(id, packet)
25
+ Manager.server.pubsub.send_to(id, packet)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ # rubocop:enable Metrics/MethodLength
31
+ end
32
+ end
33
+ end
@@ -1,24 +1,64 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tamashii/common'
4
+ require 'tamashii/configurable'
2
5
 
3
6
  module Tamashii
4
7
  module Manager
5
- class Config < Tamashii::Config
6
- AUTH_TYPES = [:none, :token]
8
+ # :nodoc:
9
+ class Config
10
+ class << self
11
+ def instance
12
+ @instance ||= Config.new
13
+ end
14
+
15
+ def respond_to_missing?(name, _all = false)
16
+ super
17
+ end
18
+
19
+ def method_missing(name, *args, &block)
20
+ # rubocop:disable Metrics/LineLength
21
+ return instance.send(name, *args, &block) if instance.class.exist?(name)
22
+ # rubocop:enable Metrics/LineLength
23
+ super
24
+ end
25
+ end
26
+
27
+ include Tamashii::Configurable
28
+
29
+ AUTH_TYPES = %i[none token].freeze
7
30
 
8
31
  register :auth_type, :none
32
+ register :token, nil
9
33
  register :log_file, STDOUT
34
+ register :log_level, Logger::INFO
35
+ register :env, nil
10
36
  register :heartbeat_interval, 3
37
+ register :port, 3000
38
+
39
+ def [](key)
40
+ config(key)
41
+ end
42
+
43
+ def []=(key, value)
44
+ config(key, value)
45
+ end
11
46
 
12
47
  def auth_type(type = nil)
13
48
  return self[:auth_type] if type.nil?
14
49
  return unless AUTH_TYPES.include?(type)
15
- self[:auth_type] = type
50
+ self.auth_type = type
16
51
  end
17
52
 
18
53
  def log_level(level = nil)
19
54
  return Manager.logger.level if level.nil?
20
55
  Manager.logger.level = level
21
56
  end
57
+
58
+ def env(env = nil)
59
+ return Tamashii::Environment.new(self[:env]) if env.nil?
60
+ self.env = env.to_s
61
+ end
22
62
  end
23
63
  end
24
64
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ # :nodoc:
6
+ module Error
7
+ autoload :AuthorizationError, 'tamashii/manager/error/authorization_error'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ module Error
6
+ class AuthorizationError < RuntimeError
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ # :nodoc:
6
+ module Handler
7
+ autoload :Broadcaster, 'tamashii/manager/handler/broadcaster'
8
+ end
9
+ end
10
+ end
@@ -1,14 +1,18 @@
1
- require 'tamashii/common'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Tamashii
4
4
  module Manager
5
5
  module Handler
6
+ # :nodoc:
6
7
  class Broadcaster < Tamashii::Handler
7
8
  def resolve(data = nil)
8
9
  client = @env[:client]
9
- if client.authorized?
10
- client.channel.broadcast(Packet.new(@type, client.tag , data).dump)
11
- end
10
+ broadcast(client, data) if client.authorized?
11
+ end
12
+
13
+ def broadcast(client, data)
14
+ packet = Packet.new(@type, client.tag, data)
15
+ client.channel.broadcast(packet.dump)
12
16
  end
13
17
  end
14
18
  end
@@ -1,80 +1,23 @@
1
- require "json"
2
- require "securerandom"
3
- require "websocket/driver"
4
- require "monitor"
1
+ # frozen_string_literal: true
5
2
 
6
- require "tamashii/manager/client"
7
- require "tamashii/manager/stream"
8
- require "tamashii/manager/stream_event_loop"
3
+ Tamashii::Server.config do |config|
4
+ config.connection_class = Tamashii::Manager::Client
5
+ config.pubsub_class = Tamashii::Manager::Subscription
6
+ end
9
7
 
10
8
  module Tamashii
11
9
  module Manager
12
- class Server
13
- class << self
14
- attr_reader :instance
15
-
16
- LOCK = Monitor.new
17
-
18
- def compile
19
- @instance ||= new
20
- end
21
-
22
- def call(env)
23
- LOCK.synchronize { compile } unless instance
24
- call!(env)
25
- end
26
-
27
- def call!(env)
28
- instance.call(env)
29
- end
30
- end
31
-
10
+ # :nodoc:
11
+ class Server < Tamashii::Server::Base
32
12
  def initialize
33
- @event_loop = StreamEventLoop.new
13
+ super
34
14
  setup_heartbeat_timer
35
-
36
- Manager.logger.info("Server is created, read for accept connection")
37
15
  end
38
16
 
17
+ # NOTE: Move into Tamashii::Server maybe better
39
18
  def setup_heartbeat_timer
40
19
  @heartbeat_timer = @event_loop.timer(Config.heartbeat_interval) do
41
- @event_loop.post { Connection.instance.map(&:beat) }
42
- end
43
- end
44
-
45
- def call(env)
46
- if WebSocket::Driver.websocket?(env)
47
- Client.new(env, @event_loop)
48
- # A dummy rack response
49
- body = {
50
- message: "WS connected",
51
- version: Tamashii::Manager::VERSION
52
- }.to_json
53
-
54
- [
55
- 200,
56
- {
57
- "Content-Type" => "application/json",
58
- "Content-Length" => body.bytesize
59
- },
60
- [body]
61
- ]
62
- else
63
-
64
- # TODO: Handle HTTP API
65
- body = {
66
- message: "Invalid protocol or api request",
67
- version: Tamashii::Manager::VERSION
68
- }.to_json
69
-
70
- [
71
- 404,
72
- {
73
- "Content-Type" => "application/json",
74
- "Content-Length" => body.bytesize
75
- },
76
- [body]
77
- ]
20
+ @event_loop.post { Client.accepted_clients.values.map(&:beat) }
78
21
  end
79
22
  end
80
23
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Manager
5
+ # :nodoc:
6
+ class Subscription < Tamashii::Server::Subscription::Redis
7
+ def send_to(id, packet)
8
+ packet = packet.dump if packet.is_a?(Tamashii::Packet)
9
+ broadcast([id.bytesize, id.unpack('C*'), packet].flatten)
10
+ end
11
+
12
+ protected
13
+
14
+ def process_message(message)
15
+ operate = unpack(message)
16
+ head_size = operate.take(1).first
17
+ return if head_size.zero?
18
+ target = operate.take(head_size + 1).drop(1)
19
+ Client[target.pack('C*')]&.send(operate.drop(head_size + 1))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Tamashii
2
2
  module Manager
3
- VERSION = "0.1.7"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_runtime_dependency "puma"
33
33
  spec.add_runtime_dependency "concurrent-ruby"
34
34
  spec.add_runtime_dependency "rack"
35
+ spec.add_runtime_dependency "tamashii"
35
36
  spec.add_runtime_dependency "tamashii-common"
36
37
  spec.add_runtime_dependency "websocket-driver"
37
38
  spec.add_runtime_dependency "nio4r"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tamashii-manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 蒼時弦也
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-04-24 00:00:00.000000000 Z
13
+ date: 2017-05-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: puma
@@ -54,6 +54,20 @@ dependencies:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: tamashii
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
57
71
  - !ruby/object:Gem::Dependency
58
72
  name: tamashii-common
59
73
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,7 @@ extra_rdoc_files: []
206
220
  files:
207
221
  - ".gitignore"
208
222
  - ".rspec"
223
+ - ".ruby-version"
209
224
  - ".travis.yml"
210
225
  - Gemfile
211
226
  - Guardfile
@@ -217,18 +232,19 @@ files:
217
232
  - exe/tamashii-manager
218
233
  - lib/tamashii/manager.rb
219
234
  - lib/tamashii/manager/authorization.rb
235
+ - lib/tamashii/manager/authorizator.rb
220
236
  - lib/tamashii/manager/authorizator/token.rb
221
237
  - lib/tamashii/manager/channel.rb
222
238
  - lib/tamashii/manager/channel_pool.rb
223
239
  - lib/tamashii/manager/client.rb
224
- - lib/tamashii/manager/clients.rb
240
+ - lib/tamashii/manager/client_manager.rb
225
241
  - lib/tamashii/manager/config.rb
226
- - lib/tamashii/manager/connection.rb
227
- - lib/tamashii/manager/errors/authorization_error.rb
242
+ - lib/tamashii/manager/error.rb
243
+ - lib/tamashii/manager/error/authorization_error.rb
244
+ - lib/tamashii/manager/handler.rb
228
245
  - lib/tamashii/manager/handler/broadcaster.rb
229
246
  - lib/tamashii/manager/server.rb
230
- - lib/tamashii/manager/stream.rb
231
- - lib/tamashii/manager/stream_event_loop.rb
247
+ - lib/tamashii/manager/subscription.rb
232
248
  - lib/tamashii/manager/version.rb
233
249
  - tags
234
250
  - tamashii-manager.gemspec
@@ -1,23 +0,0 @@
1
- module Tamashii
2
- module Manager
3
- class Clients < Hash
4
- class << self
5
- def method_missing(name, *args, &block)
6
- self.instance.send(name, *args, &block)
7
- end
8
-
9
- def instance
10
- @instance ||= new
11
- end
12
- end
13
-
14
- def register(client)
15
- self[client.id] = client
16
- end
17
-
18
- def unregister(client)
19
- self.delete(client.id)
20
- end
21
- end
22
- end
23
- end
@@ -1,23 +0,0 @@
1
- module Tamashii
2
- module Manager
3
- class Connection < Set
4
- class << self
5
- def instance
6
- @instance ||= Connection.new
7
- end
8
-
9
- def register(client)
10
- instance.add(client)
11
- end
12
-
13
- def unregister(client)
14
- instance.delete(client)
15
- end
16
-
17
- def available?
18
- !instance.empty?
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,6 +0,0 @@
1
- module Tamashii
2
- module Manager
3
- class AuthorizationError < RuntimeError
4
- end
5
- end
6
- end
@@ -1,41 +0,0 @@
1
- require "nio"
2
-
3
- require "tamashii/manager/connection"
4
-
5
- Thread.abort_on_exception = true
6
-
7
- module Tamashii
8
- module Manager
9
- class Stream
10
-
11
- attr_reader :event_loop
12
-
13
- def initialize(event_loop, io, client)
14
- @client = client
15
- @io = io
16
- @event_loop = event_loop
17
- @event_loop.attach(io, self)
18
- end
19
-
20
- def receive(data)
21
- @client.parse(data)
22
- end
23
-
24
- def shutdown
25
- clean_rack_hijack
26
- end
27
-
28
- def close
29
- shutdown
30
- @client.shutdown
31
- end
32
-
33
- private
34
- def clean_rack_hijack
35
- return unless @io
36
- @event_loop.detach(@io, self)
37
- @io = nil
38
- end
39
- end
40
- end
41
- end
@@ -1,120 +0,0 @@
1
- require 'nio'
2
- require 'thread'
3
- require 'concurrent'
4
-
5
- Thread.abort_on_exception = true
6
-
7
- module Tamashii
8
- module Manager
9
- class StreamEventLoop
10
- def initialize
11
- @nio = @thread = nil
12
- @stopping = false
13
- @map = {}
14
-
15
- @todo = Queue.new
16
-
17
- @spawn_mutex = Mutex.new
18
- end
19
-
20
- def timer(interval, &block)
21
- Concurrent::TimerTask.new(execution_interval: interval, &block).tap(&:execute)
22
- end
23
-
24
- def post(task = nil, &block)
25
- task ||= block
26
- Concurrent.global_io_executor << task
27
- end
28
-
29
- def attach(io, stream)
30
- @todo << lambda do
31
- @map[io] = @nio.register(io, :r)
32
- @map[io].value = stream
33
- end
34
- wakeup
35
- end
36
-
37
- def detach(io, stream)
38
- @todo << lambda do
39
- @nio.deregister io
40
- @map.delete io
41
- io.close
42
- end
43
- wakeup
44
- end
45
-
46
- def stop
47
- @stopping = true
48
- wakeup if @nio
49
- end
50
-
51
- def stopped?
52
- @stopping
53
- end
54
-
55
- private
56
- def spawn
57
- return if @thread && @thread.status
58
-
59
- @spawn_mutex.synchronize do
60
- return if @thread && @thread.status
61
-
62
- @nio ||= NIO::Selector.new
63
-
64
- @thread = Thread.new { run }
65
-
66
- return true
67
- end
68
- end
69
-
70
- def wakeup
71
- spawn || @nio.wakeup
72
- end
73
-
74
- def run
75
- loop do
76
- if stopped?
77
- @nio.close
78
- break
79
- end
80
-
81
- until @todo.empty?
82
- @todo.pop(true).call
83
- end
84
-
85
- next unless monitors = @nio.select
86
-
87
- monitors.each do |monitor|
88
- io = monitor.io
89
- stream = monitor.value
90
-
91
- begin
92
- if monitor.writable?
93
- if stream.flush_writer_buffer
94
- monitor.interests = :r
95
- end
96
- next unless monitor.readable?
97
- end
98
-
99
- incoming = io.read_nonblock(4096, exception: false)
100
- case incoming
101
- when :wait_readable then next
102
- when nil then stream.close
103
- else
104
- stream.receive incoming
105
- end
106
- rescue
107
- begin
108
- stream.close
109
- rescue
110
- @nio.deregister io
111
- @map.delete io
112
- end
113
- end
114
- end
115
- end
116
- end
117
-
118
- end
119
- end
120
- end