sbsm 1.3.4 → 1.3.5

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: 8b27b0cd01f696045f8dcd2a676f8803b9f25ae7
4
- data.tar.gz: aa44fd845938ef1024d628ee414e39825d55fcb2
3
+ metadata.gz: 098df1b1f3f9be3383dc1adbc44087a8662ac7e7
4
+ data.tar.gz: 3836d50af930294d0fa5bdacec1bcc04085880a4
5
5
  SHA512:
6
- metadata.gz: 5a5ff8f1920bf41c325356a5e7277d0aa67f0b686296d170b2b1a0040a62462992e5668f2b815a9e2cb2b18b8840f73ea496737df63b063ce432ae77d1e49aec
7
- data.tar.gz: 5c208b5d7943f1e342d2eee87b40a03642f5811e8a5b85721081beb8c28a2425fbbb9049259a02b87b4506a082aaa2d29cbcc3bb359c75c1120a5850fa310763
6
+ metadata.gz: cefada511b938ff19624f587ccc16feca6a33ac34b8875f24d9881c9504bd7e6a87df46bef3492d18b3ed9bb2d409d68c7230f83d5d9f38e060ce20888bdd3af
7
+ data.tar.gz: ccf6801e741324e0fc11df4f53a103e43085996a1793d6ad9a5207843440621c1f4fd778aeb3146ca1f2d81e92e2b55cdc9699db0132d2ab95a69e8206efae35
@@ -1,3 +1,8 @@
1
+ === 1.3.5 /14.12.2016
2
+ * Refactored sbsm to work without DRb
3
+ * Added test/config.ru to be able to run tests via wrk
4
+ * The interface to Session.new changed and must be adapted by client where the SBSM::Session class is overridden.
5
+
1
6
  === 1.3.4 /12.12.2016
2
7
  * As we quite often got errors about recycled objects, I added a GC.start in the call method of sbsm/app
3
8
  It introduces a delay of 12 to 30 ms. Therefore we should look for a better solution later
@@ -26,50 +26,59 @@
26
26
  require 'cgi'
27
27
  require 'cgi/session'
28
28
  require 'sbsm/cgi'
29
- require 'cgi/drbsession'
30
- require 'sbsm/drbserver'
29
+ require 'sbsm/session_store'
30
+ require 'sbsm/trans_handler'
31
+ require 'sbsm/validator'
31
32
  require 'mimemagic'
32
33
 
33
34
  module SBSM
34
35
  ###
35
36
  # App a base class for Webrick server
36
- class App < SBSM::DRbServer
37
- include DRbUndumped
38
- attr_reader :sbsm, :my_self, :trans_handler, :validator, :drb_uri
37
+ class App
39
38
 
40
- OPTIONS = [ :app, :config_file, :trans_handler, :validator, :persistence_layer, :server_uri, :session, :unknown_user, :proxy ]
39
+ OPTIONS = [ :app, :config_file, :trans_handler, :validator, :persistence_layer, :server_uri, :unknown_user]
41
40
  OPTIONS.each{ |opt| eval "attr_reader :#{opt}" }
42
41
 
43
42
  # Base class for a SBSM based WebRick HTTP server
44
- # * offers a start_server() method to launch a DRB server for handling the DRB-requests
45
43
  # * offer a call(env) method form handling the WebRick requests
46
44
  # This is all what is needed to be compatible with WebRick
47
45
  #
48
- # === arguments
46
+ # === optional arguments
49
47
  #
50
- # * +app+ - The app we should handle requests for
51
48
  # * +validator+ - A Ruby class overriding the SBSM::Validator class
52
49
  # * +trans_handler+ - A Ruby class overriding the SBSM::TransHandler class
50
+ # * +session_class+ - A Ruby class overriding the SBSM::Session class
51
+ # * +unknown_user+ - A Ruby class overriding the SBSM::UnknownUser class
53
52
  # * +persistence_layer+ - Persistence Layer to use
54
53
  # * +cookie_name+ - The cookie to save persistent user data
55
- # * +drb_uri+ - URI for DRB-Server of app
56
54
  #
57
55
  # === Examples
58
56
  # Look at steinwies.ch
59
57
  # * https://github.com/zdavatz/steinwies.ch (simple, mostly static files, one form, no persistence layer)
60
58
  #
61
- def initialize(app:, validator:, trans_handler:, drb_uri:, persistence_layer: nil, cookie_name: nil)
62
- SBSM.info "initialize #{$0} app #{app.class} @app is now #{@app.class} validator #{validator} th #{trans_handler} drb_uri #{drb_uri}"
63
- @app = app unless @app
64
- @cookie_name = cookie_name || SBSM::Session::PERSISTENT_COOKIE_NAME
65
- @drb_uri = drb_uri
66
- @trans_handler = trans_handler
67
- @validator = validator
68
- super(persistence_layer)
59
+ def initialize(validator: nil,
60
+ trans_handler: nil,
61
+ session_class: nil,
62
+ persistence_layer: nil,
63
+ unknown_user: nil,
64
+ cookie_name: nil)
65
+ @@last_session = nil
66
+ SBSM.info "initialize validator #{validator} th #{trans_handler} cookie #{cookie_name} session #{session_class}"
67
+ @session_store = SessionStore.new(persistence_layer: persistence_layer,
68
+ trans_handler: trans_handler,
69
+ session_class: session_class,
70
+ cookie_name: cookie_name,
71
+ unknown_user: unknown_user,
72
+ app: self,
73
+ validator: validator)
69
74
  end
70
75
 
71
76
  SESSION_ID = '_session_id'
72
77
 
78
+ def last_session
79
+ @@last_session
80
+ end
81
+
73
82
  def call(env) ## mimick sbsm/lib/app.rb
74
83
  request = Rack::Request.new(env)
75
84
  response = Rack::Response.new
@@ -88,35 +97,31 @@ module SBSM
88
97
  end
89
98
 
90
99
  return [400, {}, []] if /favicon.ico/i.match(request.path)
91
- @drb_uri ||= @app.drb_uri
100
+ # https://www.tutorialspoint.com/ruby/ruby_cgi_sessions.htm
92
101
  args = {
93
- 'database_manager' => CGI::Session::DRbSession,
94
- 'drbsession_uri' => @drb_uri,
102
+ 'database_manager' => @session_store,
95
103
  'session_path' => '/',
96
104
  @cookie_name => session_id,
97
105
  }
98
- SBSM.debug "starting session_id #{session_id} #{request.path}: cookies #{@cookie_name} are #{request.cookies} @session #{@session.class} @cgi #{@cgi.class}"
106
+ session = @session_store[session_id]
107
+ session.app ||= self
108
+ SBSM.debug "starting session_id #{session_id} session #{session.class} #{request.path}: cookies #{@cookie_name} are #{request.cookies} @cgi #{@cgi.class}"
99
109
  @cgi = CGI.initialize_without_offline_prompt('html4') unless @cgi
100
- @session = CGI::Session.new(@cgi, args) unless @session
101
- saved = self[session_id]
102
- @start_time = Time.now.to_f
103
- GC.start
104
- SBSM.debug "GC.start took #{((Time.now.to_f)- @start_time)*1000.0} milliseconds"
105
- @proxy = DRbObject.new(saved, server_uri) unless @proxy.is_a?(DRbObject)
106
- @proxy.trans_handler = @trans_handler
107
- @proxy.app = @app unless @proxy.app
108
- res = @proxy.drb_process(self, request)
110
+ session = CGI::Session.new(@cgi, args) unless session
111
+ res = session.process_rack(rack_request: request)
109
112
  response.write res
110
113
  response.headers['Content-Type'] ||= 'text/html; charset=utf-8'
111
- response.headers.merge!(@proxy.http_headers)
114
+ response.headers.merge!(session.http_headers)
112
115
  if (result = response.headers.find { |k,v| /status/i.match(k) })
113
116
  response.status = result.last.to_i
114
117
  response.headers.delete(result.first)
115
118
  end
116
- response.set_cookie(@cookie_name, :value => @proxy.cookie_input)
119
+ response.set_cookie(session.cookie_name, :value => session.cookie_input)
117
120
  response.set_cookie(SESSION_ID, :value => session_id)
118
- SBSM.debug "finish session_id #{session_id}: header with cookies #{response.headers} from #{@proxy.cookie_input}"
121
+ @@last_session = session
122
+ SBSM.debug "finish session_id #{session_id}: header with cookies #{response.headers} from #{session.cookie_input}"
119
123
  response.finish
120
124
  end
125
+
121
126
  end
122
127
  end
@@ -25,7 +25,6 @@
25
25
  # CGI redefinitions
26
26
 
27
27
  require 'cgi'
28
- require 'drb/drb'
29
28
 
30
29
  class CGI
31
30
  # Lets satisfy cgi-offline prompt, even if request does not have
@@ -28,30 +28,28 @@
28
28
 
29
29
  require 'cgi'
30
30
  require 'sbsm/cgi'
31
- require 'sbsm/drb'
32
31
  require 'sbsm/state'
32
+ require 'sbsm/user'
33
33
  require 'sbsm/lookandfeelfactory'
34
34
  require 'delegate'
35
- require 'sbsm/trans_handler'
36
35
 
37
36
  module SBSM
38
- class Session < SimpleDelegator
39
- attr_reader :user, :active_thread, :key, :cookie_input,
40
- :unsafe_input, :valid_input, :request_path, :cgi
41
- attr_writer :trans_handler, :app, :validator
42
- include DRbUndumped
37
+ class Session
38
+ attr_reader :user, :active_thread, :key, :cookie_input, :cookie_name,
39
+ :unsafe_input, :valid_input, :request_path, :cgi, :attended_states
40
+ attr_accessor :validator, :trans_handler, :app
43
41
  PERSISTENT_COOKIE_NAME = "sbsm-persistent-cookie"
44
42
  DEFAULT_FLAVOR = 'sbsm'
45
43
  DEFAULT_LANGUAGE = 'en'
46
44
  DEFAULT_STATE = State
47
45
  DEFAULT_ZONE = nil
48
- DRB_LOAD_LIMIT = 255 * 102400
49
46
  EXPIRES = 60 * 60
50
47
  LF_FACTORY = nil
51
48
  LOOKANDFEEL = Lookandfeel
52
49
  CAP_MAX_THRESHOLD = 8
53
50
  MAX_STATES = 4
54
51
  SERVER_NAME = nil
52
+ UNKNOWN_USER = UnknownUser
55
53
  def Session.reset_stats
56
54
  @@stats = {}
57
55
  end
@@ -88,24 +86,54 @@ module SBSM
88
86
  requests, grand_total)
89
87
  ''
90
88
  end
91
- def initialize(key, app, validator=nil)
92
- SBSM.info "initialize app #{app.class} @app is now #{@app.class} validator #{validator} th #{@trans_handler}" # drb_uri #{drb_uri}"
93
- touch()
94
- reset_input()
95
- reset_cookie()
96
- raise "Must pass key and app and validator to session" unless key && app # && validator
89
+
90
+ # Session: It will be initialized indirectly whenever SessionStore cannot
91
+ # find a session in it cache. The parameters are given via SBSM::App.new
92
+ # which calls SessionStore.new
93
+ #
94
+ # === optional arguments
95
+ #
96
+ # * +validator+ - A Ruby class overriding the SBSM::Validator class
97
+ # * +trans_handler+ - A Ruby class overriding the SBSM::TransHandler class
98
+ # * +unknown_user+ - A Ruby class overriding the SBSM::UnknownUser class
99
+ # * +cookie_name+ - The cookie to save persistent user data
100
+ #
101
+ # === Examples
102
+ # Look at steinwies.ch
103
+ # * https://github.com/zdavatz/steinwies.ch (simple, mostly static files, one form, no persistence layer)
104
+ #
105
+ def initialize(app:,
106
+ trans_handler: nil,
107
+ validator: nil,
108
+ unknown_user: nil,
109
+ cookie_name: nil)
110
+ SBSM.info "initialize th #{trans_handler} validator #{validator}"
97
111
  @app = app
98
- @key = key
112
+ @unknown_user = unknown_user
113
+ @unknown_user ||= self.class::UNKNOWN_USER
99
114
  @validator = validator
115
+ @validator ||= Validator.new
116
+ fail "invalid validator #{@validator}" unless @validator.is_a?(SBSM::Validator)
117
+ @trans_handler = trans_handler
118
+ @trans_handler ||= TransHandler.instance
119
+ fail "invalid trans_handler #{@trans_handler}" unless @trans_handler.is_a?(SBSM::TransHandler)
120
+ @cookie_name = cookie_name
121
+ @cookie_name ||= self.class::PERSISTENT_COOKIE_NAME
100
122
  @attended_states = {}
101
123
  @persistent_user_input = {}
102
- logout()
103
- @unknown_user_class = @user.class
124
+ touch()
125
+ reset_input()
126
+ reset_cookie()
127
+ @user = @unknown_user
128
+ @unknown_user_class
129
+ @unknown_user_class = @unknown_user.class
104
130
  @variables = {}
105
131
  @mutex = Mutex.new
106
132
  @cgi = CGI.initialize_without_offline_prompt('html4')
107
- SBSM.debug "session initialized #{self} key #{key} app #{app.class} #{@validator.class} th #{@trans_handler.class} with @cgi #{@cgi}"
108
- super(app)
133
+ SBSM.debug "session initialized #{self} with @cgi #{@cgi}"
134
+ end
135
+ def unknown_user
136
+ @unknown_user_class.new
109
137
  end
110
138
  def age(now=Time.now)
111
139
  now - @mtime
@@ -155,9 +183,6 @@ module SBSM
155
183
  def get_cookie_input(key)
156
184
  @cookie_input[key]
157
185
  end
158
- def cookie_name
159
- self::class::PERSISTENT_COOKIE_NAME
160
- end
161
186
  def default_language
162
187
  self::class::DEFAULT_LANGUAGE
163
188
  end
@@ -165,19 +190,47 @@ module SBSM
165
190
  # used when
166
191
  @state.direct_event
167
192
  end
168
- def drb_process(app, rack_request)
193
+ def process_rack(rack_request:)
169
194
  start = Time.now
170
195
  @request_path ||= rack_request.path
171
196
  rack_request.params.each { |key, val| @cgi.params.store(key, val) }
172
197
  @trans_handler.translate_uri(rack_request)
173
198
  html = @mutex.synchronize do
174
- process(rack_request)
199
+ begin
200
+ @request_method =rack_request.request_method
201
+ @request_path = rack_request.path
202
+ logout unless @active_state
203
+ validator.reset_errors() if validator && validator.respond_to?(:reset_errors)
204
+ import_user_input(rack_request)
205
+ import_cookies(rack_request)
206
+ @state = active_state.trigger(event())
207
+ SBSM.debug "active_state.trigger state #{@state.object_id} remember #{persistent_user_input(:remember).inspect}"
208
+ #FIXME: is there a better way to distinguish returning states?
209
+ # ... we could simply refuse to init if event == :sort, but that
210
+ # would not solve the problem cleanly, I think.
211
+ unless(@state.request_path)
212
+ @state.request_path = @request_path
213
+ @state.init
214
+ end
215
+ unless @state.volatile?
216
+ SBSM.debug "Changing from #{@active_state.object_id} to state #{@state.class} #{@state.object_id} remember #{persistent_user_input(:remember).inspect}"
217
+ @active_state = @state
218
+ @attended_states.store(@state.object_id, @state)
219
+ else
220
+ SBSM.debug "Stay in volatile state #{@state.object_id}"
221
+ end
222
+ @zone = @active_state.zone
223
+ @active_state.touch
224
+ cap_max_states
225
+ ensure
226
+ @user_input_imported = false
227
+ end
175
228
  to_html
176
229
  end
177
230
  (@@stats[@request_path] ||= []).push(Time.now - start)
178
231
  html
179
232
  rescue => err
180
- SBSM.info "Error in drb_process #{err.backtrace[0..5].join("\n")}"
233
+ SBSM.info "Error in process_rack #{err.backtrace[0..5].join("\n")}"
181
234
  raise err
182
235
  end
183
236
  def error(key)
@@ -206,11 +259,11 @@ module SBSM
206
259
  age(now) > EXPIRES
207
260
  end
208
261
  def force_login(user)
262
+ binding.pry
209
263
  @user = user
210
264
  end
211
265
  def import_cookies(request)
212
266
  reset_cookie()
213
- return if request.cookies.is_a?(DRb::DRbUnknown)
214
267
  if(cuki_str = request.cookies[self::class::PERSISTENT_COOKIE_NAME])
215
268
  SBSM.debug "cuki_str #{self::class::PERSISTENT_COOKIE_NAME} #{cuki_str}"
216
269
  eval(cuki_str).each { |key, val|
@@ -224,8 +277,6 @@ module SBSM
224
277
  @@hash_ptrn = /([^\[]+)((\[[^\]]+\])+)/
225
278
  @@index_ptrn = /[^\[\]]+/
226
279
  def import_user_input(rack_req)
227
- # attempting to read the cgi-params more than once results in a
228
- # DRbConnectionRefused Exception. Therefore, do it only once...
229
280
  return if(@user_input_imported)
230
281
  hash = rack_req.env.merge rack_req.params
231
282
  hash.merge! rack_req.POST if rack_req.POST
@@ -298,7 +349,7 @@ module SBSM
298
349
  !@user.is_a?(@unknown_user_class)
299
350
  end
300
351
  def login
301
- if(user = @app.login(self))
352
+ if(user = (@app && @app.respond_to?(:login) && @app.login(self)))
302
353
  SBSM.debug "user is #{user} #{request_path.inspect}"
303
354
  @user = user
304
355
  else
@@ -307,8 +358,8 @@ module SBSM
307
358
  end
308
359
  def logout
309
360
  __checkout
310
- @user = @app.unknown_user()
311
- @active_state = @state = self::class::DEFAULT_STATE.new(self, @user)
361
+ @user = unknown_user()
362
+ @active_state = @state = self::class::DEFAULT_STATE.new(self, @user)
312
363
  SBSM.debug "logout #{request_path.inspect} setting @state #{@state.object_id} #{@state.class} remember #{persistent_user_input(:remember).inspect}"
313
364
  @state.init
314
365
  @attended_states.store(@state.object_id, @state)
@@ -339,8 +390,6 @@ module SBSM
339
390
  end
340
391
  def http_headers
341
392
  @state.http_headers
342
- rescue DRb::DRbConnError
343
- raise
344
393
  rescue NameError, StandardError => err
345
394
  SBSM.info "NameError, StandardError: #@request_path"
346
395
  {'Content-Type' => 'text/plain'}
@@ -369,40 +418,6 @@ module SBSM
369
418
  @persistent_user_input[key]
370
419
  end
371
420
  end
372
- def process(rack_request)
373
- begin
374
- @request_method =rack_request.request_method
375
- @request = rack_request
376
- @request_method ||= @request.request_method
377
- @request_path = @request.path
378
- @validator.reset_errors() if @validator && @validator.respond_to?(:reset_errors)
379
- import_user_input(rack_request)
380
- import_cookies(rack_request)
381
- @state = active_state.trigger(event())
382
- SBSM.debug "active_state.trigger state #{@state.object_id} remember #{persistent_user_input(:remember).inspect}"
383
- #FIXME: is there a better way to distinguish returning states?
384
- # ... we could simply refuse to init if event == :sort, but that
385
- # would not solve the problem cleanly, I think.
386
- unless(@state.request_path)
387
- @state.request_path = @request_path
388
- @state.init
389
- end
390
- unless @state.volatile?
391
- SBSM.debug "Changing from #{@active_state.object_id} to state #{@state.object_id} remember #{persistent_user_input(:remember).inspect}"
392
- @active_state = @state
393
- @attended_states.store(@state.object_id, @state)
394
- else
395
- SBSM.debug "Stay in volatile state #{@state.object_id}"
396
- end
397
- @zone = @active_state.zone
398
- @active_state.touch
399
- cap_max_states
400
- rescue DRb::DRbConnError
401
- raise
402
- ensure
403
- @user_input_imported = false
404
- end
405
- end
406
421
  def reset
407
422
  if @redirected
408
423
  SBSM.debug "reached Session::reset"
@@ -441,8 +456,6 @@ module SBSM
441
456
  else
442
457
  self::class::SERVER_NAME
443
458
  end
444
- rescue DRb::DRbConnError
445
- @server_name = self::class::SERVER_NAME
446
459
  end
447
460
  def state(event=nil)
448
461
  @active_state
@@ -453,8 +466,6 @@ module SBSM
453
466
  end
454
467
  def to_html
455
468
  @state.to_html(cgi)
456
- rescue DRb::DRbConnError
457
- raise
458
469
  end
459
470
  def user_agent
460
471
  @user_agent ||= (@request.user_agent if @request.respond_to?(:user_agent))
@@ -493,11 +504,10 @@ module SBSM
493
504
  @state.warning? if @state.respond_to?(:warning?)
494
505
  end
495
506
  # CGI::SessionHandler compatibility
507
+ # https://ruby-doc.org/stdlib-2.3.1/libdoc/cgi/rdoc/CGI.html
508
+ # should restore the values from a file, we return simply nothing
496
509
  def restore
497
- hash = {
498
- :proxy => self,
499
- }
500
- hash
510
+ {}
501
511
  end
502
512
  def update
503
513
  # nothing
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ #--
4
+ #
5
+ # State Based Session Management
6
+ # Copyright (C) 2004 Hannes Wyss
7
+ #
8
+ # This library is free software; you can redistribute it and/or
9
+ # modify it under the terms of the GNU Lesser General Public
10
+ # License as published by the Free Software Foundation; either
11
+ # version 2.1 of the License, or (at your option) any later version.
12
+ #
13
+ # This library is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
+ # Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public
19
+ # License along with this library; if not, write to the Free Software
20
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
+ #
22
+ # ywesee - intellectual capital connected, Winterthurerstrasse 52, CH-8006 Zürich, Switzerland
23
+ # hwyss@ywesee.com
24
+ #
25
+ # SessionStore -- sbsm -- niger@ywesee.com
26
+ # 2016: ported this code from the old DrbServer
27
+ #++
28
+
29
+ require 'delegate'
30
+ require 'sbsm/session'
31
+ require 'sbsm/user'
32
+ require 'thread'
33
+ require 'digest/md5'
34
+ require 'sbsm/logger'
35
+ require 'sbsm/validator'
36
+
37
+ module SBSM
38
+ #
39
+ # SessionStore manages session, which are kept in memory
40
+ # Sessions are limited and expire after some time
41
+ # The maximal amount of concurrent session is given via CAP_MAX_THRESHOLD
42
+ #
43
+ class SessionStore
44
+ CLEANING_INTERVAL = 30
45
+ CAP_MAX_THRESHOLD = 120
46
+ ENABLE_ADMIN = false
47
+ MAX_SESSIONS = 100
48
+ RUN_CLEANER = true
49
+ SESSION = Session
50
+ UNKNOWN_USER = UnknownUser
51
+ VALIDATOR = nil
52
+ attr_reader :cleaner, :updater, :persistence_layer
53
+ def initialize(app:,
54
+ persistence_layer: nil,
55
+ trans_handler: nil,
56
+ session_class: nil,
57
+ validator: nil,
58
+ cookie_name: nil,
59
+ unknown_user: nil)
60
+ fail "You must specify an app!" unless app
61
+ @sessions = {}
62
+ @mutex = Mutex.new
63
+ @cleaner = run_cleaner if(self.class.const_get(:RUN_CLEANER))
64
+ @admin_threads = ThreadGroup.new
65
+ @async = ThreadGroup.new
66
+ @system = persistence_layer
67
+ @persistence_layer = persistence_layer
68
+ @cookie_name = cookie_name
69
+ @trans_handler = trans_handler
70
+ @trans_handler ||= TransHandler.instance
71
+ @session_class = session_class
72
+ @session_class ||= SBSM::Session
73
+ @unknown_user = unknown_user
74
+ @unknown_user ||= UNKNOWN_USER
75
+ @validator = validator
76
+ end
77
+ def _admin(src, result, priority=0)
78
+ raise "admin interface disabled" unless(self::class::ENABLE_ADMIN)
79
+ t = Thread.new {
80
+ Thread.current.abort_on_exception = false
81
+ result << begin
82
+ response = begin
83
+ instance_eval(src)
84
+ rescue NameError => e
85
+ e
86
+ end
87
+ str = response.to_s
88
+ if(str.length > 200)
89
+ response.class
90
+ else
91
+ str
92
+ end
93
+ rescue StandardError => e
94
+ e.message
95
+ end.to_s
96
+ }
97
+ t[:source] = src
98
+ t.priority = priority
99
+ @admin_threads.add(t)
100
+ t
101
+ end
102
+ def async(&block)
103
+ @async.add(Thread.new(&block))
104
+ end
105
+ def cap_max_sessions(now = Time.now)
106
+ if(@sessions.size > self::class::CAP_MAX_THRESHOLD)
107
+ SBSM.info "too many sessions! Keeping only #{self::class::MAX_SESSIONS}"
108
+ sess = nil
109
+ sorted = @sessions.values.sort
110
+ sorted[0...(-self::class::MAX_SESSIONS)].each { |sess|
111
+ sess.__checkout
112
+ @sessions.delete(sess.key)
113
+ }
114
+ if(sess)
115
+ age = sess.age(now)
116
+ SBSM.info sprintf("deleted all sessions that had not been accessed for more than %im %is", age / 60, age % 60)
117
+ end
118
+ end
119
+ end
120
+ def clean
121
+ now = Time.now
122
+ @sessions.delete_if { |key, s|
123
+ begin
124
+ if s.respond_to?(:expired?)
125
+ if s.expired?(now)
126
+ s.__checkout
127
+ true
128
+ else
129
+ s.cap_max_states
130
+ false
131
+ end
132
+ else
133
+ true
134
+ end
135
+ rescue
136
+ true
137
+ end
138
+ }
139
+ #cap_max_sessions(now)
140
+ end
141
+ def clear
142
+ @sessions.each_value { |sess| sess.__checkout }
143
+ @sessions.clear
144
+ end
145
+ def delete_session(key)
146
+ if(sess = @sessions.delete(key))
147
+ sess.__checkout
148
+ end
149
+ end
150
+ def reset
151
+ @mutex.synchronize {
152
+ @sessions.clear
153
+ }
154
+ end
155
+ def run_cleaner
156
+ # puts "running cleaner thread"
157
+ Thread.new do
158
+ Thread.current.abort_on_exception = true
159
+ #Thread.current.priority = 1
160
+ loop do
161
+ sleep self::class::CLEANING_INTERVAL
162
+ @mutex.synchronize do
163
+ clean()
164
+ end
165
+ end
166
+ end
167
+ end
168
+ def [](key)
169
+ @mutex.synchronize do
170
+ unless((s = @sessions[key]) && !s.expired?)
171
+ s = @sessions[key] = @session_class.new(app: @app, cookie_name: @cookie_name, trans_handler: @trans_handler, validator: @validator, unknown_user: @unknown_user)
172
+ end
173
+ s.reset()
174
+ s.touch()
175
+ s
176
+ end
177
+ end
178
+ end
179
+ end