juggernaut 0.5.7 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
- History.txt
2
1
  Manifest.txt
3
2
  README.txt
4
3
  Rakefile
@@ -9,4 +8,4 @@ lib/juggernaut/message.rb
9
8
  lib/juggernaut/miscel.rb
10
9
  lib/juggernaut/runner.rb
11
10
  lib/juggernaut/server.rb
12
- lib/juggernaut/utils.rb
11
+ lib/juggernaut/utils.rb
data/README.txt CHANGED
@@ -1,26 +1,10 @@
1
1
  Juggernaut
2
- by Alex MacCaw
3
- http://www.eribium.org
2
+ by Alex MacCaw http://www.eribium.org
3
+ minor fixes and unit tests by Ripta Pasay
4
4
 
5
5
  == DESCRIPTION:
6
-
7
- FIX (describe your package)
8
-
9
- == FEATURES/PROBLEMS:
10
-
11
- * FIX (list of features or problems)
12
-
13
- == SYNOPSIS:
14
-
15
- FIX (code sample of usage)
16
-
17
- == REQUIREMENTS:
18
-
19
- * FIX (list of requirements)
20
-
21
- == INSTALL:
22
-
23
- * FIX (sudo gem install, anything else)
6
+ See Plugin README:
7
+ http://juggernaut.rubyforge.org/svn/trunk/juggernaut/README
24
8
 
25
9
  == LICENSE:
26
10
 
data/Rakefile CHANGED
@@ -8,10 +8,7 @@ Hoe.new('juggernaut', Juggernaut::VERSION) do |p|
8
8
  p.rubyforge_name = 'juggernaut'
9
9
  p.author = 'Alex MacCaw'
10
10
  p.email = 'info@eribium.org'
11
- # p.summary = 'FIX'
12
- # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
11
  p.url = 'http://juggernaut.rubyforge.org'
14
- p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
12
  p.extra_deps << ['eventmachine', '>=0.10.0']
16
13
  p.extra_deps << ['json', '>=1.1.2']
17
14
  end
File without changes
@@ -5,7 +5,7 @@ require 'json'
5
5
  $:.unshift(File.dirname(__FILE__))
6
6
 
7
7
  module Juggernaut
8
- VERSION = '0.5.7'
8
+ VERSION = '0.5.8'
9
9
 
10
10
  class JuggernautError < StandardError #:nodoc:
11
11
  end
@@ -110,38 +110,43 @@ module Juggernaut
110
110
  # value as :public_port in the juggernaut_hosts.yml file
111
111
  # :public_port: 5001
112
112
 
113
- EOF
114
-
115
- class << self
116
- def options
117
- @@options
118
- end
119
-
120
- def options=(val)
121
- @@options = val
122
- end
123
-
124
- def logger
125
- return @@logger if defined?(@@loggger)
126
- FileUtils.mkdir_p(File.dirname(log_path))
127
- @@logger = Logger.new(log_path)
128
- @@logger.level = Logger::INFO if options[:debug] == false
129
- @@logger
130
- rescue
131
- @@logger = Logger.new(STDOUT)
132
- end
113
+ EOF
114
+
115
+ class << self
116
+ def options
117
+ @@options
118
+ end
119
+
120
+ def options=(val)
121
+ @@options = val
122
+ end
123
+
124
+ def logger
125
+ return @@logger if defined?(@@logger) && !@@logger.nil?
126
+ FileUtils.mkdir_p(File.dirname(log_path))
127
+ @@logger = Logger.new(log_path)
128
+ @@logger.level = Logger::INFO if options[:debug] == false
129
+ @@logger
130
+ rescue
131
+ @@logger = Logger.new(STDOUT)
132
+ end
133
133
 
134
- def log_path
135
- options[:log_path] || File.join(%w( / var run juggernaut.log ))
136
- end
134
+ def logger=(logger)
135
+ @@logger = logger
136
+ end
137
137
 
138
- def pid_path
139
- options[:pid_path] || File.join(%w( / var run ), "juggernaut.#{options[:port]}.pid" )
140
- end
138
+ def log_path
139
+ options[:log_path] || File.join(%w( / var run juggernaut.log ))
140
+ end
141
+
142
+ def pid_path
143
+ options[:pid_path] || File.join(%w( / var run ), "juggernaut.#{options[:port]}.pid" )
144
+ end
141
145
 
142
- def config_path
143
- options[:config_path] || File.join(%w( / var run juggernaut.yml ))
144
- end
146
+ def config_path
147
+ options[:config_path] || File.join(%w( / var run juggernaut.yml ))
148
+ end
149
+
145
150
  end
146
151
  end
147
152
 
@@ -150,4 +155,4 @@ require 'juggernaut/miscel'
150
155
  require 'juggernaut/message'
151
156
  require 'juggernaut/client'
152
157
  require 'juggernaut/server'
153
- require 'juggernaut/runner'
158
+ require 'juggernaut/runner'
@@ -1,157 +1,263 @@
1
+ require 'timeout'
2
+ require 'net/http'
3
+ require 'net/https'
1
4
  require 'uri'
5
+
2
6
  module Juggernaut
3
7
  class Client
4
8
  include Juggernaut::Miscel
9
+
10
+ @@clients = [ ]
11
+
12
+ attr_reader :id
13
+ attr_accessor :session_id
14
+ attr_reader :connections
15
+
16
+ class << self
17
+ # Actually does a find_or_create_by_id
18
+ def find_or_create(subscriber, request)
19
+ if client = find_by_id(request[:client_id])
20
+ client.session_id = request[:session_id]
21
+ client.add_new_connection(subscriber)
22
+ client
23
+ else
24
+ self.new(subscriber, request)
25
+ end
26
+ end
27
+
28
+ # Client find methods
29
+ def find_all
30
+ @@clients
31
+ end
32
+
33
+ def find(&block)
34
+ @@clients.select(&block).uniq
35
+ end
36
+
37
+ def find_by_id(id)
38
+ find { |client| client.id == id }.first
39
+ end
40
+
41
+ def find_by_signature(signature)
42
+ # signature should be unique
43
+ find do |client|
44
+ client.connections.select { |connection| connection.signature == signature }.any?
45
+ end.first
46
+ end
47
+
48
+ def find_by_channels(channels)
49
+ find do |client|
50
+ client.has_channels?(channels)
51
+ end
52
+ end
53
+
54
+ def find_by_id_and_channels(id, channels)
55
+ find do |client|
56
+ client.has_channels?(channels) && client.id == id
57
+ end.first
58
+ end
59
+
60
+ def send_logouts_after_timeout
61
+ @@clients.each do |client|
62
+ if client.give_up?
63
+ client.logout_request
64
+ end
65
+ end
66
+ end
67
+
68
+ # Called when the server is shutting down
69
+ def send_logouts_to_all_clients
70
+ @@clients.each do |client|
71
+ client.logout_request
72
+ end
73
+ end
74
+
75
+ def reset!
76
+ @@clients.clear
77
+ end
78
+
79
+ def register_client(client)
80
+ @@clients << client unless @@clients.include?(client)
81
+ end
82
+
83
+ def client_registered?(client)
84
+ @@clients.include?(client)
85
+ end
86
+
87
+ def unregister_client(client)
88
+ @@clients.delete(client)
89
+ end
90
+ end
91
+
92
+ def initialize(subscriber, request)
93
+ @connections = []
94
+ @id = request[:client_id]
95
+ @session_id = request[:session_id]
96
+ @messages = []
97
+ @logout_timeout = 0
98
+ self.register
99
+ add_new_connection(subscriber)
100
+ end
101
+
102
+ def to_json
103
+ {
104
+ :client_id => @id,
105
+ :num_connections => @connections.size,
106
+ :session_id => @session_id
107
+ }.to_json
108
+ end
109
+
110
+ def add_new_connection(subscriber)
111
+ @connections << subscriber
112
+ end
113
+
114
+ def friendly_id
115
+ if self.id
116
+ "with ID #{self.id}"
117
+ else
118
+ "session #{self.session_id}"
119
+ end
120
+ end
121
+
122
+ def subscription_request(channels)
123
+ return true unless options[:subscription_url]
124
+ post_request(options[:subscription_url], channels, :timeout => options[:post_request_timeout] || 5)
125
+ end
126
+
127
+ def logout_connection_request(channels)
128
+ return true unless options[:logout_connection_url]
129
+ post_request(options[:logout_connection_url], channels, :timeout => options[:post_request_timeout] || 5)
130
+ end
131
+
132
+ def logout_request
133
+ self.unregister
134
+ logger.debug("Timed out client #{friendly_id}")
135
+ return true unless options[:logout_url]
136
+ post_request(options[:logout_url], [ ], :timeout => options[:post_request_timeout] || 5)
137
+ end
5
138
 
6
- attr_reader :id
7
- attr_accessor :session_id
8
- attr_reader :connections
9
- @@clients = []
10
-
11
- class << self
12
- # Actually does a find_or_create_by_id
13
- def find_or_create(subscriber, request)
14
- if client = find_by_id(request[:client_id])
15
- client.session_id = request[:session_id]
16
- client.add_new_connection(subscriber)
17
- client
18
- else
19
- self.new(subscriber, request)
20
- end
21
- end
22
-
23
- def add_client(client)
24
- @@clients << client unless @@clients.include?(client)
25
- end
26
-
27
- # Client find methods
28
-
29
- def find_all
30
- @@clients
31
- end
32
-
33
- def find(&block)
34
- @@clients.select(&block).uniq
35
- end
36
-
37
- def find_by_id(id)
38
- find {|client| client.id == id }.first
39
- end
40
-
41
- def find_by_signature(signature)
42
- # signature should be unique
43
- find {|client|
44
- client.connections.select {|connection| connection.signature == signature }.any?
45
- }.first
46
- end
47
-
48
- def find_by_channels(channels)
49
- find {|client|
50
- client.has_channels?(channels)
51
- }
52
- end
53
-
54
- def find_by_id_and_channels(id, channels)
55
- find {|client|
56
- client.has_channels?(channels) && client.id == id
57
- }.first
58
- end
59
-
60
- def send_logouts_after_timeout
61
- @@clients.each do |client|
62
- client.logout_request if !client.alive? and client.give_up? and !client.sent_logout?
63
- end
64
- end
65
-
66
- # Called when the server is shutting down
67
- def send_logouts_to_all_clients
68
- @@clients.each do |client|
69
- client.logout_request if !client.sent_logout?
70
- end
71
- end
72
- end
73
-
74
- def initialize(subscriber, request)
75
- @connections = []
76
- @id = request[:client_id]
77
- @session_id = request[:session_id]
78
- add_new_connection(subscriber)
79
- end
80
-
81
- def to_json
82
- {
83
- :id => @id,
84
- :session_id => @session_id
85
- }.to_json
86
- end
87
-
88
- def add_new_connection(subscriber)
89
- @connections << subscriber
90
- end
91
-
92
- def subscription_request(channels)
93
- return true unless options[:subscription_url]
94
- post_request(options[:subscription_url], channels)
95
- end
96
-
97
- def logout_connection_request(channels)
98
- return true unless options[:logout_connection_url]
99
- post_request(options[:logout_connection_url], channels)
100
- end
101
-
102
- def logout_request
103
- return true unless options[:logout_url]
104
- @sent_logout = true
105
- post_request(options[:logout_url])
106
- end
107
-
108
- def sent_logout?
109
- !!@sent_logout
110
- end
111
-
112
- def send_message(msg, channels = nil)
113
- @connections.each do |em|
114
- em.broadcast(msg) if !channels or channels.empty? or em.has_channels?(channels)
115
- end
116
- end
117
-
118
- def has_channels?(channels)
119
- @connections.each do |em|
120
- return true if em.has_channels?(channels)
121
- end
122
- false
123
- end
124
-
125
- def remove_channels!(channels)
126
- @connections.each do |em|
127
- em.remove_channels!(channels)
128
- end
129
- end
130
-
131
- def alive?
132
- @connections.select{|em| em.alive? }.any?
133
- end
134
-
135
- def give_up?
136
- @connections.select {|em| em.logout_timeout and Time.now > em.logout_timeout }.any?
137
- end
138
-
139
- private
140
-
141
- def post_request(url, channels = [])
142
- url = URI.parse(url)
143
- params = []
144
- params << "client_id=#{id}" if id
145
- params << "session_id=#{session_id}" if session_id
146
- channels.each {|chan| params << "channels[]=#{chan}" }
147
- url.query = params.join('&')
148
- begin
149
- open(url.to_s, "User-Agent" => "Ruby/#{RUBY_VERSION}")
150
- rescue => e
151
- logger.debug("Bad response from #{url.to_s}: #{e}")
152
- return false
153
- end
154
- true
155
- end
156
- end
157
- end
139
+ def remove_connection(connection)
140
+ @connections.delete(connection)
141
+ self.reset_logout_timeout!
142
+ self.logout_request if self.give_up?
143
+ end
144
+
145
+ def send_message(msg, channels = nil)
146
+ store_message(msg, channels) if options[:store_messages]
147
+ send_message_to_connections(msg, channels)
148
+ end
149
+
150
+ # Send messages that are queued up for this particular client.
151
+ # Messages are only queued for previously-connected clients.
152
+ def send_queued_messages(connection)
153
+ self.expire_queued_messages!
154
+
155
+ # Weird looping because we don't want to send messages that get
156
+ # added to the array after we start iterating (since those will
157
+ # get sent to the client anyway).
158
+ @length = @messages.length
159
+
160
+ logger.debug("Sending #{@length} queued message(s) to client #{friendly_id}")
161
+
162
+ @length.times do |id|
163
+ message = @messages[id]
164
+ send_message_to_connection(connection, message[:message], message[:channels])
165
+ end
166
+ end
167
+
168
+ def channels
169
+ @connections.collect { |em| em.channels }.flatten.uniq
170
+ end
171
+
172
+ def has_channels?(channels)
173
+ @connections.each do |em|
174
+ return true if em.has_channels?(channels)
175
+ end
176
+ false
177
+ end
178
+
179
+ def remove_channels!(channels)
180
+ @connections.each do |em|
181
+ em.remove_channels!(channels)
182
+ end
183
+ end
184
+
185
+ def alive?
186
+ @connections.select { |em| em.alive? }.any?
187
+ end
188
+
189
+ # This client is only dead if there are no connections and we are
190
+ # past the timeout (if we are within the timeout, the user could
191
+ # just be doing a page reload or going to a new page)
192
+ def give_up?
193
+ !alive? and (Time.now > @logout_timeout)
194
+ end
195
+
196
+ protected
197
+
198
+ def register
199
+ self.class.register_client(self)
200
+ end
201
+
202
+ def registered?
203
+ self.class.client_registered?(self)
204
+ end
205
+
206
+ def unregister
207
+ self.class.unregister_client(self)
208
+ end
209
+
210
+ def post_request(url, channels = [ ], options = { })
211
+ uri = URI.parse(url)
212
+ uri.path = '/' if uri.path == ''
213
+ params = []
214
+ params << "client_id=#{id}" if id
215
+ params << "session_id=#{session_id}" if session_id
216
+ channels.each {|chan| params << "channels[]=#{chan}" }
217
+ headers = {"User-Agent" => "Ruby/#{RUBY_VERSION}"}
218
+ begin
219
+ http = Net::HTTP.new(uri.host, uri.port)
220
+ if uri.scheme == 'https'
221
+ http.use_ssl = true
222
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
223
+ end
224
+ http.read_timeout = options[:timeout] || 5
225
+ resp, data = http.post(uri.path, params.join('&'), headers)
226
+ return resp.is_a?(Net::HTTPOK)
227
+ rescue => e
228
+ logger.error("Bad request #{url.to_s} (#{e.class}: #{e.message})")
229
+ return false
230
+ rescue Timeout::Error
231
+ logger.error("#{url.to_s} timeout")
232
+ return false
233
+ end
234
+ end
235
+
236
+ def send_message_to_connections(msg, channels)
237
+ @connections.each do |connection|
238
+ send_message_to_connection(connection, msg, channels)
239
+ end
240
+ end
241
+
242
+ def send_message_to_connection(connection, msg, channels)
243
+ connection.broadcast(msg) if !channels or channels.empty? or connection.has_channels?(channels)
244
+ end
245
+
246
+ # Queued messages are stored until a timeout is reached which is the
247
+ # same as the connection timeout. This takes care of messages that
248
+ # come in between page loads or ones that come in right when you are
249
+ # clicking off one page and loading the next one.
250
+ def store_message(msg, channels)
251
+ self.expire_queued_messages!
252
+ @messages << { :channels => channels, :message => msg, :timeout => Time.now + options[:timeout] }
253
+ end
254
+
255
+ def expire_queued_messages!
256
+ @messages.reject! { |message| Time.now > message[:timeout] }
257
+ end
258
+
259
+ def reset_logout_timeout!
260
+ @logout_timeout = Time.now + options[:timeout]
261
+ end
262
+ end
263
+ end