Sutto-marvin 0.4.0 → 0.8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/bin/marvin +22 -156
  2. data/handlers/keiki_thwopper.rb +21 -0
  3. data/handlers/tweet_tweet.rb +1 -3
  4. data/lib/marvin/abstract_client.rb +75 -189
  5. data/lib/marvin/abstract_parser.rb +9 -11
  6. data/lib/marvin/base.rb +134 -101
  7. data/lib/marvin/client/actions.rb +104 -0
  8. data/lib/marvin/client/default_handlers.rb +97 -0
  9. data/lib/marvin/command_handler.rb +60 -49
  10. data/lib/marvin/console.rb +4 -31
  11. data/lib/marvin/core_commands.rb +30 -12
  12. data/lib/marvin/distributed/client.rb +225 -0
  13. data/lib/marvin/distributed/handler.rb +85 -0
  14. data/lib/marvin/distributed/protocol.rb +88 -0
  15. data/lib/marvin/distributed/server.rb +154 -0
  16. data/lib/marvin/distributed.rb +4 -10
  17. data/lib/marvin/dsl.rb +103 -0
  18. data/lib/marvin/exception_tracker.rb +7 -4
  19. data/lib/marvin/irc/client.rb +127 -99
  20. data/lib/marvin/irc/event.rb +14 -10
  21. data/lib/marvin/irc.rb +0 -1
  22. data/lib/marvin/middle_man.rb +1 -1
  23. data/lib/marvin/parsers/command.rb +10 -8
  24. data/lib/marvin/parsers/prefixes/host_mask.rb +12 -7
  25. data/lib/marvin/parsers/prefixes/server.rb +1 -1
  26. data/lib/marvin/parsers/ragel_parser.rb +59 -52
  27. data/lib/marvin/parsers/ragel_parser.rl +6 -7
  28. data/lib/marvin/parsers/simple_parser.rb +4 -9
  29. data/lib/marvin/parsers.rb +1 -2
  30. data/lib/marvin/settings.rb +29 -79
  31. data/lib/marvin/test_client.rb +20 -26
  32. data/lib/marvin/util.rb +10 -3
  33. data/lib/marvin.rb +42 -39
  34. data/templates/boot.erb +3 -0
  35. data/templates/connections.yml.erb +10 -0
  36. data/templates/debug_handler.erb +5 -0
  37. data/templates/hello_world.erb +10 -0
  38. data/templates/rakefile.erb +15 -0
  39. data/templates/settings.yml.erb +8 -0
  40. data/{config/setup.rb → templates/setup.erb} +8 -10
  41. data/templates/test_helper.erb +17 -0
  42. data/test/abstract_client_test.rb +63 -0
  43. data/test/parser_comparison.rb +2 -2
  44. data/test/parser_test.rb +3 -3
  45. data/test/test_helper.rb +58 -6
  46. metadata +51 -83
  47. data/README.textile +0 -105
  48. data/TUTORIAL.textile +0 -54
  49. data/VERSION.yml +0 -4
  50. data/config/boot.rb +0 -14
  51. data/config/connections.yml.sample +0 -5
  52. data/config/settings.yml.sample +0 -13
  53. data/handlers/logging_handler.rb +0 -89
  54. data/lib/marvin/core_ext.rb +0 -11
  55. data/lib/marvin/daemon.rb +0 -71
  56. data/lib/marvin/data_store.rb +0 -73
  57. data/lib/marvin/dispatchable.rb +0 -99
  58. data/lib/marvin/distributed/dispatch_handler.rb +0 -83
  59. data/lib/marvin/distributed/drb_client.rb +0 -78
  60. data/lib/marvin/distributed/ring_server.rb +0 -41
  61. data/lib/marvin/handler.rb +0 -12
  62. data/lib/marvin/irc/server/abstract_connection.rb +0 -84
  63. data/lib/marvin/irc/server/base_connection.rb +0 -66
  64. data/lib/marvin/irc/server/channel.rb +0 -115
  65. data/lib/marvin/irc/server/named_store.rb +0 -14
  66. data/lib/marvin/irc/server/remote_interface.rb +0 -77
  67. data/lib/marvin/irc/server/user/handle_mixin.rb +0 -140
  68. data/lib/marvin/irc/server/user.rb +0 -5
  69. data/lib/marvin/irc/server/user_connection.rb +0 -134
  70. data/lib/marvin/irc/server/virtual_user_connection.rb +0 -80
  71. data/lib/marvin/irc/server.rb +0 -71
  72. data/lib/marvin/loader.rb +0 -149
  73. data/lib/marvin/logger.rb +0 -86
  74. data/lib/marvin/options.rb +0 -42
  75. data/lib/marvin/parsers/regexp_parser.rb +0 -93
  76. data/lib/marvin/status.rb +0 -72
  77. data/script/client +0 -3
  78. data/script/console +0 -3
  79. data/script/distributed_client +0 -3
  80. data/script/install +0 -1
  81. data/script/ring_server +0 -4
  82. data/script/server +0 -4
  83. data/script/status +0 -3
  84. data/spec/marvin/abstract_client_test.rb +0 -38
  85. data/spec/spec_helper.rb +0 -14
@@ -5,7 +5,7 @@ module Marvin
5
5
 
6
6
  module BaseExtensions
7
7
  def parse(line)
8
- Marvin::Settings.default_parser.parse(line)
8
+ Marvin::Settings.parser.parse(line)
9
9
  end
10
10
 
11
11
  def logger
@@ -13,35 +13,7 @@ module Marvin
13
13
  end
14
14
 
15
15
  def client
16
- $client ||= Marvin::Settings.default_client.new(:port => 6667, :server => "irc.freenode.net")
17
- end
18
-
19
- class ServerMock < Marvin::IRC::Server::BaseConnection
20
- def send_line(line)
21
- puts ">> #{line}"
22
- end
23
- def kill_connection!
24
- puts "Killing connection"
25
- end
26
-
27
- def get_peername
28
- # Localhost, HTTP
29
- "\034\036\000P\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000"
30
- end
31
-
32
- def host
33
- "localhost"
34
- end
35
-
36
- def port
37
- 6667
38
- end
39
-
40
- end
41
-
42
- def server(reset = false)
43
- $server = ServerMock.new(:port => 6667, :host => "localhost") if $server.blank? || reset
44
- return $server
16
+ $client ||= Marvin::Settings.client.new(:port => 6667, :server => "irc.freenode.net")
45
17
  end
46
18
 
47
19
  def user(reset = false)
@@ -66,7 +38,8 @@ module Marvin
66
38
  end
67
39
 
68
40
  def run
69
- IRB.start(@file)
41
+ ARGV.replace []
42
+ IRB.start
70
43
  end
71
44
 
72
45
  def self.run
@@ -1,20 +1,38 @@
1
1
  module Marvin
2
2
  class CoreCommands < CommandHandler
3
3
 
4
+ # Returns a hash of doccumented method names
5
+ def self.method_documentation
6
+ documented = Hash.new { |h,k| h[k] = [] }
7
+ @@method_descriptions.each_key do |klass|
8
+ next unless klass.registered?
9
+ @@exposed_method_mapping[klass].each do |m|
10
+ desc = @@method_descriptions[klass][m]
11
+ documented[m.to_s] << desc if desc.present?
12
+ end
13
+ end
14
+ return documented
15
+ end
16
+
17
+ def registered_and_exposed_handlers
18
+ end
19
+
4
20
  exposes :help
5
21
  desc "Generates this usage statement"
6
- def help(methods)
7
- method_names = exposed_method_names.map { |n| n.to_s }
8
- documented_names = descriptions.keys.map { |k| k.to_s } & method_names
9
- if methods.empty?
10
- width = documented_names.map { |s| s.length }.max
11
- say "Hello there, I know the following commands:"
12
- documented_names.each { |name| say "#{name.ljust(width)} - #{descriptions[name.to_sym]}" }
13
- say "As well as the following undescribed commands: #{(method_names - documented_names).sort.join(", ")}"
22
+ def help(method)
23
+ method = method.strip
24
+ documentation = self.class.method_documentation
25
+ names = documentation.keys.sort
26
+ if method.blank?
27
+ display_names = names.map { |n| exposed_name(n) }
28
+ width = display_names.map { |d| d.length }.max
29
+ say "Hello there, I know the following documented commands:"
30
+ names.each_with_index do |name, index|
31
+ say "#{display_names[index].ljust(width)} - #{documentation[name].join("; ")}"
32
+ end
14
33
  else
15
- m = methods.first
16
- if documented_names.include? m.to_s
17
- reply "#{m}: #{descriptions[m.to_sym]}"
34
+ if names.include? method
35
+ reply "#{exposed_name(method)} - #{documentation[method].join("; ")}"
18
36
  else
19
37
  reply "I'm sorry, I can't help with #{m} - it seems to be undocumented."
20
38
  end
@@ -24,7 +42,7 @@ module Marvin
24
42
  exposes :about
25
43
  desc "Displays the current marvin and ruby versions."
26
44
  def about(*args)
27
- reply "Marvin v#{Marvin::VERSION::STRING} running on Ruby #{RUBY_VERSION} (#{RUBY_PLATFORM})"
45
+ reply "Marvin v#{Marvin::VERSION} running on Ruby #{RUBY_VERSION} (#{RUBY_PLATFORM})"
28
46
  end
29
47
 
30
48
  end
@@ -0,0 +1,225 @@
1
+ require 'json'
2
+ require 'digest/sha2'
3
+ require 'eventmachine'
4
+ require 'socket'
5
+
6
+ module Marvin
7
+ module Distributed
8
+ class Client < Marvin::AbstractClient
9
+
10
+ attr_accessor :em_connection, :remote_client_host, :remote_client_nick
11
+
12
+ class RemoteClientProxy
13
+ is :loggable
14
+
15
+ def initialize(conn, host_with_port, nickname)
16
+ @connection = conn
17
+ @host_with_port = host_with_port
18
+ @nickname = nickname
19
+ end
20
+
21
+ def nickname
22
+ @nickname
23
+ end
24
+
25
+ def host_with_port
26
+ @host_with_port
27
+ end
28
+
29
+ def method_missing(name, *args)
30
+ logger.debug "Proxying #{name}(#{args.inspect[1..-2]}) to #{@host_with_port}"
31
+ @connection.send_message(:action, {
32
+ "action" => name.to_s,
33
+ "arguments" => args,
34
+ "client-host" => @host_with_port
35
+ })
36
+ end
37
+
38
+ end
39
+
40
+ class EMConnection < Marvin::Distributed::Protocol
41
+
42
+ register_handler_method :event
43
+ register_handler_method :authentication_failed
44
+ register_handler_method :authenticated
45
+ register_handler_method :unauthorized
46
+
47
+ cattr_accessor :stopping
48
+ self.stopping = false
49
+
50
+ attr_accessor :client, :port, :connection_host, :connection_port, :configuration
51
+
52
+ def initialize(*args)
53
+ @configuration = args.last.is_a?(Marvin::Nash) ? args.pop : Marvin::Nash.new
54
+ super(*args)
55
+ @callbacks = {}
56
+ @client = Marvin::Distributed::Client.new(self)
57
+ @authenticated = false
58
+ end
59
+
60
+ def post_init
61
+ super
62
+ logger.info "Connected to distributed server"
63
+ if should_use_tls?
64
+ logger.info "Attempting to initialize tls"
65
+ start_tls
66
+ else
67
+ process_authentication
68
+ end
69
+ end
70
+
71
+ def ssl_handshake_completed
72
+ logger.info "tls handshake completed"
73
+ process_authentication if should_use_tls?
74
+ end
75
+
76
+
77
+ def unbind
78
+ if self.stopping
79
+ logger.info "Stopping distributed client"
80
+ else
81
+ logger.info "Lost connection to distributed client - Scheduling reconnect"
82
+ EventMachine.add_timer(15) { EMConnection.connect(connection_host, connection_port, @configuration) }
83
+ end
84
+ super
85
+ end
86
+
87
+ def process_authentication
88
+ if configuration.token?
89
+ logger.info "Attempting to authenticate..."
90
+ send_message(:authenticate, {:token => configuration.token})
91
+ end
92
+ end
93
+
94
+ def handle_event(options = {})
95
+ event = options["event-name"]
96
+ client_host = options["client-host"]
97
+ client_nick = options["client-nick"]
98
+ options = options["event-options"]
99
+ options = {} unless options.is_a?(Hash)
100
+ return if event.blank?
101
+ begin
102
+ logger.debug "Handling #{event}"
103
+ @client.remote_client_host = client_host
104
+ @client.remote_client_nick = client_nick
105
+ @client.setup_handlers
106
+ @client.dispatch(event.to_sym, options)
107
+ rescue Exception => e
108
+ logger.warn "Got Exception - Forwarding to Remote"
109
+ Marvin::ExceptionTracker.log(e)
110
+ send_message(:exception, {
111
+ "name" => e.class.name,
112
+ "message" => e.message,
113
+ "backtrace" => e.backtrace
114
+ })
115
+ ensure
116
+ logger.debug "Sending completed message"
117
+ send_message(:completed)
118
+ @client.reset!
119
+ end
120
+ end
121
+
122
+ def handle_unauthorized(options = {})
123
+ logger.warn "Attempted action when unauthorized. Stopping client."
124
+ Marvin::Distributed::Client.stop
125
+ end
126
+
127
+ def handle_authenticated(options = {})
128
+ @authenticated = true
129
+ logger.info "Successfully authenticated with #{host_with_port}"
130
+ end
131
+
132
+ def handle_authentication_failed(options = {})
133
+ logger.info "Authentication with #{host_with_port} failed. Stopping."
134
+ Marvin::Distributed::Client.stop
135
+ end
136
+
137
+ def self.connect(host, port, config = Marvin::Nash.new)
138
+ logger.info "Attempting to connect to #{host}:#{port}"
139
+ EventMachine.connect(host, port, self, config) do |c|
140
+ c.connection_host = host
141
+ c.connection_port = port
142
+ end
143
+ end
144
+
145
+ protected
146
+
147
+ def options_for_callback(blk)
148
+ return {} if blk.blank?
149
+ cb_id = "callback-#{seld.object_id}-#{Time.now.to_f}"
150
+ count = 0
151
+ count += 1 while @callbacks.has_key?(Digest::SHA256.hexdigest("#{cb_id}-#{count}"))
152
+ final_id = Digest::SHA256.hexdigest("#{cb_id}-#{count}")
153
+ @callbacks[final_id] = blk
154
+ {"callback-id" => final_id}
155
+ end
156
+
157
+ def process_callback(hash)
158
+ if hash.is_a?(Hash) && hash.has_key?("callback-id")
159
+ callback = @callbacks.delete(hash["callback-id"])
160
+ callback.call(self, hash)
161
+ end
162
+ end
163
+
164
+ def host_with_port
165
+ @host_with_port ||= begin
166
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
167
+ "#{ip}:#{port}"
168
+ end
169
+ end
170
+
171
+ def should_use_tls?
172
+ @using_tls ||= configuration.encrypted?
173
+ end
174
+
175
+ end
176
+
177
+ def initialize(em_connection)
178
+ @em_connection = em_connection
179
+ end
180
+
181
+ def remote_client
182
+ @remote_client ||= RemoteClientProxy.new(@em_connection, @remote_client_host, @remote_client_nick)
183
+ end
184
+
185
+ def reset!
186
+ @remote_client = nil
187
+ @remote_client_nick = nil
188
+ @remote_client_host = nil
189
+ reset_handlers
190
+ end
191
+
192
+ def setup_handlers
193
+ self.class.handlers.each { |h| h.client = remote_client if h.respond_to?(:client=) }
194
+ end
195
+
196
+ def reset_handlers
197
+ self.class.handlers.each { |h| h.client = nil if h.respond_to?(:client=) }
198
+ end
199
+
200
+ class << self
201
+
202
+ def run
203
+ logger.info "Preparing to start distributed client"
204
+ EventMachine.kqueue
205
+ EventMachine.epoll
206
+ EventMachine.run do
207
+ opts = Marvin::Settings.distributed || Marvin::Nash.new
208
+ opts = opts.client || Marvin::Nash.new
209
+ host = opts.host || "0.0.0.0"
210
+ port = (opts.port || 8943).to_i
211
+ EMConnection.connect(host, port, opts)
212
+ end
213
+ end
214
+
215
+ def stop
216
+ logger.info "Stopping distributed client..."
217
+ EMConnection.stopping = true
218
+ EventMachine.stop_event_loop
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,85 @@
1
+ module Marvin
2
+ module Distributed
3
+ class Handler < Marvin::Base
4
+
5
+
6
+ EVENT_WHITELIST = [:incoming_message, :incoming_action]
7
+ QUEUE_PROCESSING_SPACING = 3
8
+
9
+ attr_accessor :message_queue
10
+
11
+ def initialize
12
+ super
13
+ @message_queue = []
14
+ end
15
+
16
+ def handle(message, options)
17
+ return unless EVENT_WHITELIST.include?(message)
18
+ super(message, options)
19
+ dispatch(message, options)
20
+ end
21
+
22
+ def dispatch(name, options, client = self.client)
23
+ return if client.blank?
24
+ server = Marvin::Distributed::Server.next
25
+ if server.blank?
26
+ logger.debug "Distributed handler is currently busy - adding to queue"
27
+ # TODO: Add to queued messages, wait
28
+ @message_queue << [name, options, client]
29
+ run! unless running?
30
+ else
31
+ server.dispatch(client, name, options)
32
+ end
33
+ rescue Exception => e
34
+ logger.warn "Error dispatching #{name}"
35
+ Marvin::ExceptionTracker.log(e)
36
+ end
37
+
38
+ def process_queue
39
+ count = [@message_queue.size, Server.free_connections.size].min
40
+ logger.debug "Processing #{count} item(s) from the message queue"
41
+ count.times { |item| dispatch(*@message_queue.shift) }
42
+ if @message_queue.empty?
43
+ logger.debug "The message queue is now empty"
44
+ else
45
+ logger.debug "The message queue still has #{count} item(s)"
46
+ end
47
+ check_queue_progress
48
+ end
49
+
50
+ def running?
51
+ @running_timer.present?
52
+ end
53
+
54
+ def run!
55
+ @running_timer = EventMachine::PeriodicTimer.new(QUEUE_PROCESSING_SPACING) { process_queue }
56
+ end
57
+
58
+ def check_queue_progress
59
+ if @message_queue.blank? && running?
60
+ @running_timer.cancel
61
+ @running_timer = nil
62
+ elsif @message_queue.present? && !running?
63
+ run!
64
+ end
65
+ end
66
+
67
+ class << self
68
+
69
+ def whitelist_event(name)
70
+ EVENT_WHITELIST << name.to_sym
71
+ EVENT_WHITELIST.uniq!
72
+ end
73
+
74
+ def register!(*args)
75
+ # DO NOT register if this is not a normal client.
76
+ return unless Marvin::Loader.client?
77
+ logger.info "Registering distributed handler on #{Marvin::Settings.client}"
78
+ super
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,88 @@
1
+ module Marvin
2
+ module Distributed
3
+ class Protocol < EventMachine::Protocols::LineAndTextProtocol
4
+ is :loggable
5
+
6
+ class_inheritable_accessor :handler_methods
7
+ self.handler_methods = {}
8
+
9
+ attr_accessor :callbacks
10
+
11
+ def receive_line(line)
12
+ line.strip!
13
+ logger.debug "<< #{line}"
14
+ response = JSON.parse(line)
15
+ handle_response(response)
16
+ rescue JSON::ParserError
17
+ logger.debug "JSON parsing error for #{line.inspect}"
18
+ rescue Exception => e
19
+ Marvin::ExceptionTracker.log(e)
20
+ end
21
+
22
+ def send_message(name, arguments = {}, &callback)
23
+ logger.debug "Sending #{name.inspect} to #{self.host_with_port}"
24
+ payload = {
25
+ "message" => name.to_s,
26
+ "options" => arguments,
27
+ "sent-at" => Time.now
28
+ }
29
+ payload.merge!(options_for_callback(callback))
30
+ payload = JSON.dump(payload)
31
+ logger.debug ">> #{payload}"
32
+ send_data "#{payload}\n"
33
+ end
34
+
35
+ def handle_response(response)
36
+ logger.debug "Handling response in distributed protocol (response => #{response.inspect})"
37
+ return unless response.is_a?(Hash) && response.has_key?("message")
38
+ options = response["options"] || {}
39
+ process_response_message(response["message"], options)
40
+ end
41
+
42
+ def host_with_port
43
+ @host_with_port ||= begin
44
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
45
+ "#{ip}:#{port}"
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ def options_for_callback(blk)
52
+ return {} if blk.blank?
53
+ cb_id = "callback-#{seld.object_id}-#{Time.now.to_f}"
54
+ count = 0
55
+ count += 1 while @callbacks.has_key?(Digest::SHA256.hexdigest("#{cb_id}-#{count}"))
56
+ final_id = Digest::SHA256.hexdigest("#{cb_id}-#{count}")
57
+ @callbacks ||= {}
58
+ @callbacks[final_id] = blk
59
+ {"callback-id" => final_id}
60
+ end
61
+
62
+ def process_callback(hash)
63
+ @callbacks ||= {}
64
+ if hash.is_a?(Hash) && hash.has_key?("callback-id")
65
+ callback = @callbacks.delete(hash["callback-id"])
66
+ callback.call(self, hash)
67
+ end
68
+ end
69
+
70
+ def process_response_message(message, options)
71
+ method = self.handler_methods[message.to_s]
72
+ if method.present? && respond_to?(method)
73
+ logger.debug "Dispatching #{message} to #{method}"
74
+ send(method, options)
75
+ else
76
+ logger.warn "Got unknown message (#{message}) with options: #{options.inspect}"
77
+ end
78
+ end
79
+
80
+ def self.register_handler_method(name, method = nil)
81
+ name = name.to_s
82
+ method ||= "handle_#{name}".to_sym
83
+ self.handler_methods[name] = method
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,154 @@
1
+ require 'json'
2
+ require 'digest/sha2'
3
+ require 'eventmachine'
4
+ require 'socket'
5
+
6
+ module Marvin
7
+ module Distributed
8
+ class Server < Protocol
9
+
10
+ register_handler_method :completed
11
+ register_handler_method :exception
12
+ register_handler_method :action
13
+ register_handler_method :authenticate
14
+
15
+ cattr_accessor :free_connections, :action_whitelist
16
+ self.free_connections = []
17
+ self.action_whitelist = [:nick, :pong, :action, :msg, :quit, :part, :join, :command]
18
+
19
+ attr_accessor :processing, :configuration, :using_tls
20
+
21
+ def initialize(*args)
22
+ @configuration = args.last.is_a?(Marvin::Nash) ? args.pop : Marvin::nash.new
23
+ super(*args)
24
+ end
25
+
26
+ def post_init
27
+ super
28
+ @callbacks = {}
29
+ logger.info "Got distributed client connection with #{self.host_with_port}"
30
+ if should_use_tls?
31
+ start_tls
32
+ else
33
+ complete_processing
34
+ end
35
+ end
36
+
37
+ def ssl_handshake_completed
38
+ complete_processing if should_use_tls?
39
+ end
40
+
41
+ def unbind
42
+ logger.info "Lost distributed client connection with #{self.host_with_port}"
43
+ @@free_connections.delete(self)
44
+ super
45
+ end
46
+
47
+ def dispatch(client, name, options)
48
+ @processing = true
49
+ send_message(:event, {
50
+ "event-name" => name.to_s,
51
+ "event-options" => options,
52
+ "client-host" => client.host_with_port,
53
+ "client-nick" => client.nickname
54
+ })
55
+ end
56
+
57
+ def handle_authenticate(options = {})
58
+ return unless requires_auth?
59
+ logger.info "Attempting authentication for distributed client"
60
+ if options["token"].present? && options["token"] == configuration.token
61
+ @authenticated = true
62
+ send_message(:authenticated)
63
+ else
64
+ send_message(:authentication_failed)
65
+ end
66
+ end
67
+
68
+ def handle_completed(options = {})
69
+ return if fails_auth!
70
+ logger.debug "Completed message from #{self.host_with_port}"
71
+ complete_processing
72
+ end
73
+
74
+ def handle_exception(options = {})
75
+ return if fails_auth!
76
+ logger.info "Handling exception on #{self.host_with_port}"
77
+ name = options["name"]
78
+ message = options["message"]
79
+ backtrace = options["backtrace"]
80
+ logger.warn "Error in remote client - #{name}: #{message}"
81
+ [*backtrace].each { |line| logger.warn "--> #{line}" } if backtrace.present?
82
+ end
83
+
84
+ def handle_action(options = {})
85
+ return if fails_auth!
86
+ logger.debug "Handling action from on #{self.host_with_port}"
87
+ server = lookup_client_for(options["client-host"])
88
+ action = options["action"]
89
+ arguments = [*options["arguments"]]
90
+ return if server.blank? || action.blank?
91
+ begin
92
+ a = action.to_sym
93
+ if self.action_whitelist.include?(a)
94
+ server.send(a, *arguments) if server.respond_to?(a)
95
+ else
96
+ logger.warn "Client attempted invalid action #{a.inspect}"
97
+ end
98
+ rescue Exception => e
99
+ Marvin::ExceptionTracker.log(e)
100
+ end
101
+ end
102
+
103
+ def complete_processing
104
+ @@free_connections << self
105
+ @processing = false
106
+ end
107
+
108
+ def start_processing
109
+ @processing = true
110
+ end
111
+
112
+ def lookup_client_for(key)
113
+ Marvin::IRC::Client.connections.detect do |c|
114
+ c.host_with_port == key
115
+ end
116
+ end
117
+
118
+ def requires_auth?
119
+ configuration.token? && !authenticated?
120
+ end
121
+
122
+ def authenticated?
123
+ @authenticated ||= false
124
+ end
125
+
126
+ def should_use_tls?
127
+ @using_tls ||= configuration.encrypted?
128
+ end
129
+
130
+ def fails_auth!
131
+ if requires_auth?
132
+ logger.debug "Authentication missing for distributed client"
133
+ send_message(:unauthorized)
134
+ close_connection_after_writing
135
+ return true
136
+ end
137
+ end
138
+
139
+ def self.start
140
+ opts = Marvin::Settings.distributed || Marvin::Nash.new
141
+ opts = opts.server || Marvin::Nash.new
142
+ host = opts.host || "0.0.0.0"
143
+ port = (opts.port || 8943).to_i
144
+ logger.info "Starting distributed server on #{host}:#{port} (requires authentication = #{opts.token?})"
145
+ EventMachine.start_server(host, port, self, opts)
146
+ end
147
+
148
+ def self.next
149
+ @@free_connections.shift
150
+ end
151
+
152
+ end
153
+ end
154
+ end
@@ -1,14 +1,8 @@
1
- require 'drb'
2
- require 'rinda/ring'
3
-
4
-
5
1
  module Marvin
6
- # Distributed tools for Marvin instances.
7
- # Uses a tuple space etc + DRb to provide
8
- # IRC Processing across the network.
9
2
  module Distributed
10
- autoload :RingServer, 'marvin/distributed/ring_server'
11
- autoload :DispatchHandler, 'marvin/distributed/dispatch_handler'
12
- autoload :DRbClient, 'marvin/distributed/drb_client'
3
+ autoload :Protocol, 'marvin/distributed/protocol'
4
+ autoload :Server, 'marvin/distributed/server'
5
+ autoload :Handler, 'marvin/distributed/handler'
6
+ autoload :Client, 'marvin/distributed/client'
13
7
  end
14
8
  end