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 +4 -4
- data/History.txt +5 -0
- data/lib/sbsm/app.rb +39 -34
- data/lib/sbsm/cgi.rb +0 -1
- data/lib/sbsm/session.rb +85 -75
- data/lib/sbsm/session_store.rb +179 -0
- data/lib/sbsm/state.rb +1 -0
- data/lib/sbsm/validator.rb +4 -3
- data/lib/sbsm/version.rb +1 -1
- data/lib/sbsm/viralstate.rb +1 -1
- data/test/config.ru +11 -0
- data/test/simple_sbsm.rb +17 -15
- data/test/test_application.rb +55 -44
- data/test/test_customized_app.rb +232 -0
- data/test/test_logger.rb +0 -0
- data/test/test_session.rb +94 -61
- data/test/wrk_visit.lua +12 -0
- metadata +9 -7
- data/lib/cgi/drbsession.rb +0 -39
- data/lib/sbsm/drb.rb +0 -22
- data/lib/sbsm/drbserver.rb +0 -167
- data/test/test_drbserver.rb +0 -84
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 098df1b1f3f9be3383dc1adbc44087a8662ac7e7
|
|
4
|
+
data.tar.gz: 3836d50af930294d0fa5bdacec1bcc04085880a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cefada511b938ff19624f587ccc16feca6a33ac34b8875f24d9881c9504bd7e6a87df46bef3492d18b3ed9bb2d409d68c7230f83d5d9f38e060ce20888bdd3af
|
|
7
|
+
data.tar.gz: ccf6801e741324e0fc11df4f53a103e43085996a1793d6ad9a5207843440621c1f4fd778aeb3146ca1f2d81e92e2b55cdc9699db0132d2ab95a69e8206efae35
|
data/History.txt
CHANGED
|
@@ -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
|
data/lib/sbsm/app.rb
CHANGED
|
@@ -26,50 +26,59 @@
|
|
|
26
26
|
require 'cgi'
|
|
27
27
|
require 'cgi/session'
|
|
28
28
|
require 'sbsm/cgi'
|
|
29
|
-
require '
|
|
30
|
-
require 'sbsm/
|
|
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
|
|
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, :
|
|
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(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
100
|
+
# https://www.tutorialspoint.com/ruby/ruby_cgi_sessions.htm
|
|
92
101
|
args = {
|
|
93
|
-
'database_manager' =>
|
|
94
|
-
'drbsession_uri' => @drb_uri,
|
|
102
|
+
'database_manager' => @session_store,
|
|
95
103
|
'session_path' => '/',
|
|
96
104
|
@cookie_name => session_id,
|
|
97
105
|
}
|
|
98
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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!(
|
|
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(
|
|
119
|
+
response.set_cookie(session.cookie_name, :value => session.cookie_input)
|
|
117
120
|
response.set_cookie(SESSION_ID, :value => session_id)
|
|
118
|
-
|
|
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
|
data/lib/sbsm/cgi.rb
CHANGED
data/lib/sbsm/session.rb
CHANGED
|
@@ -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
|
|
39
|
-
attr_reader :user, :active_thread, :key, :cookie_input,
|
|
40
|
-
:unsafe_input, :valid_input, :request_path, :cgi
|
|
41
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
103
|
-
|
|
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}
|
|
108
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
311
|
-
|
|
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
|
-
|
|
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
|