Class: HTML5Monitor

Inherits:
Object
  • Object
show all
Defined in:
lib/automate-em/interfaces/html5.rb

Constant Summary collapse

@@clients =
{}
@@special_events =
{
	"system" => :system,
	"authenticate" => :authenticate,
	"ls" => :ls,
	"ping" => :ping
}
@@special_commands =
{
	"register" => :register,
	"unregister" => :unregister
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket) ⇒ HTML5Monitor

Returns a new instance of HTML5Monitor.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/automate-em/interfaces/html5.rb', line 67

def initialize(socket)
	@data_lock = Mutex.new
	
	#
	# Must authenticate before any system details will be sent
	#
	@socket = socket
	@system = nil
	@user = nil
	
	
	@socket.send(JSON.generate({:event => "authenticate", :data => []}))
	
	#
	# TODO:: start a schedule here that sends a ping to the browser every so often
	#
end

Class Method Details

.countObject



45
46
47
# File 'lib/automate-em/interfaces/html5.rb', line 45

def self.count
	return @@clients.length
end

.receive(id, data) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/automate-em/interfaces/html5.rb', line 49

def self.receive(id, data)
	client = @@clients[id]
	
	EM.defer do
		begin
			client.receive(data)
		rescue => e
			AutomateEm.print_error(AutomateEm::System.logger, e, {
				:message => "in html5.rb, onmessage : client did not exist (we may have been shutting down)",
				:level => Logger::ERROR
			})
		ensure
			ActiveRecord::Base.clear_active_connections!	# Clear any unused connections
		end
	end
end

.register(id) ⇒ Object



25
26
27
# File 'lib/automate-em/interfaces/html5.rb', line 25

def self.register(id)
	@@clients[id] = HTML5Monitor.new(id)
end

.unregister(id) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/automate-em/interfaces/html5.rb', line 29

def self.unregister(id)
	client = @@clients.delete(id)
	
	EM.defer do
		begin
			client.disconnected
			AutomateEm::System.logger.debug "There are now #{HTML5Monitor.count} HTML5 clients connected"
		rescue => e
			AutomateEm.print_error(AutomateEm::System.logger, e, {
				:message => "in html5.rb, onclose : unregistering client did not exist (we may have been shutting down)",
				:level => Logger::ERROR
			})
		end
	end
end

Instance Method Details

#disconnectedObject



143
144
145
146
147
# File 'lib/automate-em/interfaces/html5.rb', line 143

def disconnected
	@data_lock.synchronize {
		@system.disconnected(self) if (!!@system)	# System could be nil or false
	}
end

#notify(mod_sym, stat_sym, data) ⇒ Object



234
235
236
237
238
239
240
241
242
# File 'lib/automate-em/interfaces/html5.rb', line 234

def notify(mod_sym, stat_sym, data)
	#
	# This should be re-entrant? So no need to protect
	#
	@system.logger.debug "#{mod_sym}.#{stat_sym} sent #{data.inspect}"
	EM.schedule do
		@socket.send(JSON.generate({"event" => "#{mod_sym}.#{stat_sym}", "data" => data}))
	end
end

#receive(data) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/automate-em/interfaces/html5.rb', line 149

def receive(data)
	data = JSON.parse(data, {:symbolize_names => true})
	return unless data[:command].class == String
	data[:data] = [] unless data[:data].class == Array

	@data_lock.synchronize {
		#
		# Ensure authenticated
		#
		if data[:command] == "authenticate"
			return unless try_auth(data[:data])
			send_system
			return
		else
			return unless (try_auth || data[:command] == "ping")
		end
		
		#
		# Ensure system is selected
		#	If a command is sent out of order
		#
		if @system.nil? && !@@special_events.has_key?(data[:command])
			send_system
			return
		end
		
		if @@special_events.has_key?(data[:command])		# system, auth, ls
			case @@special_events[data[:command]]
				when :system
					@system.disconnected(self) unless @system.nil?
					@system = nil
					@system = AutomateEm::Communicator.select(@user, self, data[:data][0]) unless data[:data].empty?
					if @system.nil?
						send_system
					elsif @system == false	# System offline
						EM.schedule do
							@socket.send(JSON.generate({:event => "offline", :data => []}))
							shutdown
						end
					else
						EM.schedule do
							@socket.send(JSON.generate({:event => "ready", :data => []}))
						end
					end
				when :ping
					EM.schedule do
						@socket.send(JSON.generate({:event => "pong", :data => []}))
					end
				when :ls
					systems = AutomateEm::Communicator.system_list(@user)
					EM.schedule do
						@socket.send(JSON.generate({:event => "ls", :data => systems}))
					end
			end
		elsif @@special_commands.has_key?(data[:command])	# reg, unreg
			array = data[:data]
			array.insert(0, self)
			@system.public_send(data[:command], *array)
		else									# All other commands
			command = data[:command].split('.')
			if command.length == 2
				@system.send_command(command[0], command[1], *data[:data])
			else
				AutomateEm::System.logger.info "-- in html5.rb, receive : invalid command received - #{data[:command]} --"
			end
		end
	}
rescue => e
	logger = nil
	@data_lock.synchronize {
		logger = @system.nil? ? AutomateEm::System.logger : @system.logger
	}
	AutomateEm.print_error(logger, e, {
		:message => "in html5.rb, receive : probably malformed JSON data",
		:level => Logger::ERROR
	})
	shutdown
end

#send_systemObject



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/automate-em/interfaces/html5.rb', line 130

def send_system
	return if @ignoreSys	
	@ignoreSys = true
	
	EM.add_timer(2) do
		begin
			@socket.send(JSON.generate({:event => "system", :data => []}))
		ensure
			@ignoreSys = false
		end
	end
end

#shutdownObject



228
229
230
231
232
# File 'lib/automate-em/interfaces/html5.rb', line 228

def shutdown
	EM.schedule do
		@socket.close_websocket
	end
end

#try_auth(data = nil) ⇒ Object

Instance methods:



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/automate-em/interfaces/html5.rb', line 91

def try_auth(data = nil)
	
	return false if @ignoreAuth

	if !!@user
		if data.nil?
			return true
		else
			@user = nil
			return try_auth(data)
		end
	else
		if !data.nil? && data.class == Array
			if data.length == 1	# one time key
				@user = TrustedDevice.(data[0])
			elsif data.length == 3
										#user, password, auth_source
				source = AuthSource.where("name = ?", data[2]).first
				@user = User.(data[0], data[1], source)
			end
			
			return try_auth	# no data
		end
		
		#
		# Prevent DOS/brute force Attacks
		#
		@ignoreAuth = true
		EM.add_timer(2) do
			begin
				@socket.send(JSON.generate({:event => "authenticate", :data => []}))
			ensure
				@ignoreAuth = false
			end
		end
	end
	return false
end