isomorfeus-transport 1.0.0.zeta23 → 2.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -21
  3. data/README.md +27 -36
  4. data/lib/isomorfeus/transport/client_processor.rb +35 -30
  5. data/lib/isomorfeus/transport/config.rb +182 -158
  6. data/lib/isomorfeus/transport/hamster_session_store.rb +96 -0
  7. data/lib/isomorfeus/transport/handler/authentication_handler.rb +70 -70
  8. data/lib/isomorfeus/transport/imports.rb +9 -0
  9. data/lib/isomorfeus/transport/middlewares.rb +13 -13
  10. data/lib/isomorfeus/transport/rack_middleware.rb +59 -55
  11. data/lib/isomorfeus/transport/request_agent.rb +34 -34
  12. data/lib/isomorfeus/transport/response_agent.rb +23 -23
  13. data/lib/isomorfeus/transport/server_processor.rb +129 -101
  14. data/lib/isomorfeus/transport/server_socket_processor.rb +54 -54
  15. data/lib/isomorfeus/transport/ssr_login.rb +28 -28
  16. data/lib/isomorfeus/transport/version.rb +5 -5
  17. data/lib/isomorfeus/transport/{websocket.rb → websocket_client.rb} +123 -123
  18. data/lib/isomorfeus/transport.rb +200 -213
  19. data/lib/isomorfeus-transport.rb +70 -62
  20. data/lib/lucid_authentication/mixin.rb +122 -124
  21. data/lib/lucid_channel/base.rb +8 -11
  22. data/lib/lucid_channel/mixin.rb +105 -50
  23. data/lib/lucid_handler/base.rb +8 -9
  24. data/lib/lucid_handler/mixin.rb +27 -27
  25. data/node_modules/.package-lock.json +27 -0
  26. data/node_modules/ws/LICENSE +19 -0
  27. data/node_modules/ws/README.md +496 -0
  28. data/node_modules/ws/browser.js +8 -0
  29. data/node_modules/ws/index.js +13 -0
  30. data/node_modules/ws/lib/buffer-util.js +126 -0
  31. data/node_modules/ws/lib/constants.js +12 -0
  32. data/node_modules/ws/lib/event-target.js +266 -0
  33. data/node_modules/ws/lib/extension.js +203 -0
  34. data/node_modules/ws/lib/limiter.js +55 -0
  35. data/node_modules/ws/lib/permessage-deflate.js +511 -0
  36. data/node_modules/ws/lib/receiver.js +612 -0
  37. data/node_modules/ws/lib/sender.js +414 -0
  38. data/node_modules/ws/lib/stream.js +180 -0
  39. data/node_modules/ws/lib/subprotocol.js +62 -0
  40. data/node_modules/ws/lib/validation.js +124 -0
  41. data/node_modules/ws/lib/websocket-server.js +485 -0
  42. data/node_modules/ws/lib/websocket.js +1144 -0
  43. data/node_modules/ws/package.json +61 -0
  44. data/node_modules/ws/wrapper.mjs +8 -0
  45. data/package.json +6 -0
  46. metadata +76 -54
  47. data/lib/isomorfeus/transport/dbm_session_store.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f880aa11773aa387acfd1e8bcee0b3dc1678c6657341a4dff9612b4e0cbd73d6
4
- data.tar.gz: a17830e51f92e94ea20b1e059dcb6832578a6688583e1579edc1ccbc32d51729
3
+ metadata.gz: 51966e33a5412f5d3e38054cc87b91ee92d8ed273fecc4a2e8714cc7ef0e765d
4
+ data.tar.gz: 17fd590a9850f434d6a7a6d59d45e60af8d2efd1b37d2fd0bddff0ca1f9c6040
5
5
  SHA512:
6
- metadata.gz: 8cc684ec49a19d7aedb5f1f59c284e78ae426bd9ff03f33874f07a37bf5085f902b803049efebb24ceea982e71ae97c23b59ae323fe510d8d97e09aeb6fab74a
7
- data.tar.gz: 102b5ff34f5127d81ed4cd026b02b7beb87f9c595b2d5cfe49a4a42a675a8147cc9faff7ae5797293d3bf5982e86c5b1321e06a092acf7c195cf5374c4facb52
6
+ metadata.gz: 0cdd94a7cb3adcb4d6230abcf83d77c282d1cffb838dc4e024cd56215c3c2ca13657bdcb328e6bb3c463e7e3dad3029d5b44ea697fbccaf1a095a960b81c3dae
7
+ data.tar.gz: ce3e0da3e1caea29de684f7fb1e6b91713509e99e355f124f5e0f89134dd6eb817ab3af56623d668e6a3adfded0fa69bf1f39c4c3a8ad036b3c2d1345435cfbe
data/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2018, 2019 Jan Biedermann
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2018, 2019 Jan Biedermann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,36 +1,27 @@
1
- # isomorfeus-transport
2
-
3
- Transport and PubSub for Isomorfeus.
4
-
5
- ### Community and Support
6
- At the [Isomorfeus Framework Project](http://isomorfeus.com)
7
-
8
- ## Installation
9
- isomorfeus-transport is usually installed with the installer.
10
- Otherwise add to your Gemfile:
11
- ```ruby
12
- gem 'isomorfeus-transport'
13
- ```
14
- and bundle install/update
15
-
16
- ## Server Side Rendering
17
- `yarn add ws`
18
-
19
- The 'ws' module then needs to be imported in application_ssr.js:
20
- ```
21
- import WebSocket from 'ws';
22
- global.WebSocket = WebSocket;
23
- ```
24
-
25
- ## Configuration options
26
-
27
- Client and Server:
28
- - Isomorfeus.api_websocket_path - path for server side endpoint, default: `/isomorfeus/api/websocket`
29
-
30
- Server only:
31
- - Isomorfeus.middlewares - all the rack middlewares to load
32
-
33
- ## Usage
34
-
35
- - [Authentication and Current User](https://github.com/isomorfeus/isomorfeus-project/blob/master/ruby/isomorfeus-policy/docs/authentication.md)
36
- - [Channels (PubSub)](https://github.com/isomorfeus/isomorfeus-project/blob/master/ruby/isomorfeus-policy/docs/channels.md)
1
+ # isomorfeus-transport
2
+
3
+ Transport and PubSub for Isomorfeus.
4
+
5
+ ### Community and Support
6
+ At the [Isomorfeus Framework Project](http://isomorfeus.com)
7
+
8
+ ## Installation
9
+ isomorfeus-transport is usually installed with the installer.
10
+ Otherwise add to your Gemfile:
11
+ ```ruby
12
+ gem 'isomorfeus-transport'
13
+ ```
14
+ and bundle install/update
15
+
16
+ ## Configuration options
17
+
18
+ Client and Server:
19
+ - Isomorfeus.api_websocket_path - path for server side endpoint, default: `/isomorfeus/api/websocket`
20
+
21
+ Server only:
22
+ - Isomorfeus.middlewares - all the rack middlewares to load
23
+
24
+ ## Usage
25
+
26
+ - [Authentication and Current User](https://github.com/isomorfeus/isomorfeus-project/blob/master/ruby/isomorfeus-transport/docs/authentication.md)
27
+ - [Channels (PubSub)](https://github.com/isomorfeus/isomorfeus-project/blob/master/ruby/isomorfeus-transport/docs/channels.md)
@@ -1,30 +1,35 @@
1
- module Isomorfeus
2
- module Transport
3
- class ClientProcessor
4
- def self.process(json_hash)
5
- if json_hash.key?(:response)
6
- process_response(json_hash)
7
- elsif json_hash.key?(:notification)
8
- process_notification(json_hash)
9
- end
10
- end
11
-
12
- def self.process_notification(notification_hash)
13
- processor_class = "::#{notification_hash[:notification][:class]}".constantize
14
- processor_class.process_message(notification_hash[:notification][:channel], notification_hash[:notification][:message])
15
- end
16
-
17
- def self.process_response(response_hash)
18
- response_hash[:response][:agent_ids].keys.each do |agent_id|
19
- agent = Isomorfeus::Transport::RequestAgent.get!(agent_id)
20
- Isomorfeus::Transport.unregister_request_in_progress(agent_id)
21
- unless agent.promise.realized?
22
- agent.full_response = response_hash
23
- agent.response = response_hash[:response][:agent_ids][agent_id]
24
- agent.promise.resolve(agent)
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
1
+ module Isomorfeus
2
+ module Transport
3
+ class ClientProcessor
4
+ def self.process(json_hash)
5
+ if json_hash.key?(:response)
6
+ process_response(json_hash)
7
+ elsif json_hash.key?(:notification)
8
+ process_message(json_hash)
9
+ end
10
+ end
11
+
12
+ def self.process_message(message_hash)
13
+ processor_class_name = message_hash[:notification][:class]
14
+ Isomorfeus.raise_error(message: "Not a valid channel class #{processor_class_name}!") unless Isomorfeus.valid_channel_class_name?(processor_class_name)
15
+ processor_class = Isomorfeus.cached_channel_class(processor_class_name)
16
+ unless processor_class.respond_to?(:process_message)
17
+ Isomorfeus.raise_error(message: "Cannot process message, #{processor_class} must be a Channel and must have the on_message block defined!")
18
+ end
19
+ processor_class.process_message(message_hash[:notification][:message], message_hash[:notification][:error], message_hash[:notification][:channel])
20
+ end
21
+
22
+ def self.process_response(response_hash)
23
+ response_hash[:response][:agent_ids].keys.each do |agent_id|
24
+ agent = Isomorfeus::Transport::RequestAgent.get!(agent_id)
25
+ Isomorfeus::Transport.unregister_request_in_progress(agent_id)
26
+ unless agent.promise.realized?
27
+ agent.full_response = response_hash
28
+ agent.response = response_hash[:response][:agent_ids][agent_id]
29
+ agent.promise.resolve(agent)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,158 +1,182 @@
1
- module Isomorfeus
2
- # available settings
3
-
4
- if RUBY_ENGINE == 'opal'
5
- add_client_option(:api_websocket_path)
6
- add_client_option(:cookie_eater_path)
7
- add_client_option(:transport_init_class_names, [])
8
-
9
- def self.add_transport_init_class_name(init_class_name)
10
- transport_init_class_names << init_class_name
11
- end
12
-
13
- def self.current_user
14
- @current_user ||= init_current_user
15
- end
16
-
17
- def self.init_current_user
18
- if Isomorfeus.current_user_sid
19
- Isomorfeus.instance_from_sid(Isomorfeus.current_user_sid)
20
- else
21
- Anonymous.new
22
- end
23
- end
24
-
25
- def self.set_current_user(user)
26
- if user
27
- @current_user = user
28
- Isomorfeus.current_user_sid = user.to_sid
29
- else
30
- @current_user = Anonymous.new
31
- end
32
- end
33
- else
34
- class << self
35
- attr_accessor :api_websocket_path
36
- attr_accessor :cookie_eater_path
37
- attr_accessor :session_store
38
- attr_accessor :cookie_dbm_path
39
-
40
- def add_middleware(middleware)
41
- Isomorfeus.middlewares << middleware
42
- end
43
-
44
- def insert_middleware_after(existing_middleware, new_middleware)
45
- index_of_existing = Isomorfeus.middlewares.index(existing_middleware)
46
- unless Isomorfeus.middlewares.include?(new_middleware)
47
- if index_of_existing
48
- Isomorfeus.middlewares.insert(index_of_existing + 1, new_middleware)
49
- else
50
- Isomorfeus.middlewares << new_middleware
51
- end
52
- end
53
- end
54
-
55
- def insert_middleware_before(existing_middleware, new_middleware)
56
- index_of_existing = Isomorfeus.middlewares.index(existing_middleware)
57
- unless Isomorfeus.middlewares.include?(new_middleware)
58
- if index_of_existing
59
- Isomorfeus.middlewares.insert(index_of_existing, new_middleware)
60
- else
61
- Isomorfeus.middlewares << new_middleware
62
- end
63
- end
64
- end
65
-
66
- def middlewares
67
- @middlewares ||= Set.new
68
- end
69
-
70
- def cached_channel_classes
71
- @cached_channel_classes ||= {}
72
- end
73
-
74
- def cached_channel_class(class_name)
75
- return "::#{class_name}".constantize if Isomorfeus.development?
76
- return cached_channel_classes[class_name] if cached_channel_classes.key?(class_name)
77
- cached_channel_classes[class_name] = "::#{class_name}".constantize
78
- end
79
-
80
- def valid_channel_class_names
81
- @valid_channel_class_names ||= Set.new
82
- end
83
-
84
- def valid_channel_class_name?(class_name)
85
- valid_channel_class_names.include?(class_name)
86
- end
87
-
88
- def add_valid_channel_class(klass)
89
- valid_channel_class_names << raw_class_name(klass)
90
- end
91
-
92
- def valid_handler_class_names
93
- @valid_handler_class_names ||= Set.new
94
- end
95
-
96
- def valid_handler_class_name?(class_name)
97
- valid_handler_class_names.include?(class_name)
98
- end
99
-
100
- def add_valid_handler_class(klass)
101
- valid_handler_class_names << raw_class_name(klass)
102
- end
103
-
104
- def cached_handler_classes
105
- @cached_handler_classes ||= {}
106
- end
107
-
108
- def cached_handler_class(class_name)
109
- return "::#{class_name}".constantize if Isomorfeus.development?
110
- return cached_handler_classes[class_name] if cached_handler_classes.key?(class_name)
111
- cached_handler_classes[class_name] = "::#{class_name}".constantize
112
- end
113
-
114
- def valid_user_class_names
115
- @valid_user_class_names ||= Set.new
116
- end
117
-
118
- def valid_user_class_name?(class_name)
119
- valid_user_class_names.include?(class_name)
120
- end
121
-
122
- def add_valid_user_class(klass)
123
- valid_user_class_names << raw_class_name(klass)
124
- end
125
-
126
- def cached_user_classes
127
- @cached_user_classes ||= {}
128
- end
129
-
130
- def cached_user_class(class_name)
131
- return "::#{class_name}".constantize if Isomorfeus.development?
132
- return cached_user_classes[class_name] if cached_user_classes.key?(class_name)
133
- cached_user_classes[class_name] = "::#{class_name}".constantize
134
- end
135
-
136
- def raw_class_name(klass)
137
- class_name = klass.name
138
- class_name = class_name.split('>::').last if class_name.start_with?('#<')
139
- class_name
140
- end
141
-
142
- def current_user
143
- Thread.current[:isomorfeus_user]
144
- end
145
-
146
- def pub_sub_client
147
- Thread.current[:isomorfeus_pub_sub_client]
148
- end
149
- end
150
-
151
- self.cookie_dbm_path = 'cookie'
152
- self.session_store = Isomorfeus::Transport::DbmSessionStore.new # dont use this one, but we keep it here to have at least something
153
- end
154
-
155
- # defaults
156
- self.api_websocket_path = '/isomorfeus/api/websocket'
157
- self.cookie_eater_path = '/isomorfeus/cookie/eat'
158
- end
1
+ module Isomorfeus
2
+ # available settings
3
+ class << self
4
+ def cached_channel_classes
5
+ @cached_channel_classes ||= {}
6
+ end
7
+
8
+ def cached_channel_class(class_name)
9
+ return "::#{class_name}".constantize if Isomorfeus.development?
10
+ return cached_channel_classes[class_name] if cached_channel_classes.key?(class_name)
11
+ cached_channel_classes[class_name] = "::#{class_name}".constantize
12
+ end
13
+
14
+ def valid_channel_class_names
15
+ @valid_channel_class_names ||= Set.new
16
+ end
17
+
18
+ def add_valid_channel_class(klass)
19
+ valid_channel_class_names << raw_class_name(klass)
20
+ end
21
+
22
+ def raw_class_name(klass)
23
+ class_name = klass.name
24
+ class_name = class_name.split('>::').last if class_name.start_with?('#<')
25
+ class_name
26
+ end
27
+ end
28
+
29
+ if RUBY_ENGINE == 'opal'
30
+ add_client_option(:api_websocket_host)
31
+ add_client_option(:api_websocket_port)
32
+ add_client_option(:api_websocket_path)
33
+ add_client_option(:cookie_eater_path)
34
+ add_client_option(:transport_init_class_names, [])
35
+
36
+ class << self
37
+ def valid_channel_class_name?(class_name)
38
+ cached_channel_class(class_name) # because of autoloader
39
+ valid_channel_class_names.include?(class_name)
40
+ end
41
+
42
+ def add_transport_init_class_name(init_class_name)
43
+ transport_init_class_names << init_class_name
44
+ end
45
+
46
+ def current_user
47
+ @current_user ||= init_current_user
48
+ end
49
+
50
+ def init_current_user
51
+ if Isomorfeus.current_user_sid
52
+ Isomorfeus.instance_from_sid(Isomorfeus.current_user_sid)
53
+ else
54
+ Anonymous.new
55
+ end
56
+ end
57
+
58
+ def set_current_user(user)
59
+ if user
60
+ @current_user = user
61
+ Isomorfeus.current_user_sid = user.sid
62
+ else
63
+ @current_user = Anonymous.new
64
+ end
65
+ end
66
+ end
67
+ else
68
+ class << self
69
+ attr_accessor :api_websocket_host
70
+ attr_accessor :api_websocket_port
71
+ attr_accessor :api_websocket_path
72
+ attr_accessor :cookie_eater_path
73
+ attr_reader :session_store
74
+
75
+ def valid_channel_class_name?(class_name)
76
+ valid_channel_class_names.include?(class_name)
77
+ end
78
+
79
+ def add_middleware(middleware)
80
+ Isomorfeus.middlewares << middleware
81
+ end
82
+
83
+ def insert_middleware_after(existing_middleware, new_middleware)
84
+ index_of_existing = Isomorfeus.middlewares.index(existing_middleware)
85
+ unless Isomorfeus.middlewares.include?(new_middleware)
86
+ if index_of_existing
87
+ Isomorfeus.middlewares.insert(index_of_existing + 1, new_middleware)
88
+ else
89
+ Isomorfeus.middlewares << new_middleware
90
+ end
91
+ end
92
+ end
93
+
94
+ def insert_middleware_before(existing_middleware, new_middleware)
95
+ index_of_existing = Isomorfeus.middlewares.index(existing_middleware)
96
+ unless Isomorfeus.middlewares.include?(new_middleware)
97
+ if index_of_existing
98
+ Isomorfeus.middlewares.insert(index_of_existing, new_middleware)
99
+ else
100
+ Isomorfeus.middlewares << new_middleware
101
+ end
102
+ end
103
+ end
104
+
105
+ def middlewares
106
+ @middlewares ||= Set.new
107
+ end
108
+
109
+ def valid_handler_class_names
110
+ @valid_handler_class_names ||= Set.new
111
+ end
112
+
113
+ def valid_handler_class_name?(class_name)
114
+ valid_handler_class_names.include?(class_name)
115
+ end
116
+
117
+ def add_valid_handler_class(klass)
118
+ valid_handler_class_names << raw_class_name(klass)
119
+ end
120
+
121
+ def cached_handler_classes
122
+ @cached_handler_classes ||= {}
123
+ end
124
+
125
+ def cached_handler_class(class_name)
126
+ return "::#{class_name}".constantize if Isomorfeus.development?
127
+ return cached_handler_classes[class_name] if cached_handler_classes.key?(class_name)
128
+ cached_handler_classes[class_name] = "::#{class_name}".constantize
129
+ end
130
+
131
+ def valid_user_class_names
132
+ @valid_user_class_names ||= Set.new
133
+ end
134
+
135
+ def valid_user_class_name?(class_name)
136
+ valid_user_class_names.include?(class_name)
137
+ end
138
+
139
+ def add_valid_user_class(klass)
140
+ valid_user_class_names << raw_class_name(klass)
141
+ end
142
+
143
+ def cached_user_classes
144
+ @cached_user_classes ||= {}
145
+ end
146
+
147
+ def cached_user_class(class_name)
148
+ return "::#{class_name}".constantize if Isomorfeus.development?
149
+ return cached_user_classes[class_name] if cached_user_classes.key?(class_name)
150
+ cached_user_classes[class_name] = "::#{class_name}".constantize
151
+ end
152
+
153
+ def current_user
154
+ Thread.current[:isomorfeus_user]
155
+ end
156
+
157
+ def pub_sub_client
158
+ Thread.current[:isomorfeus_pub_sub_client]
159
+ end
160
+
161
+ def session_store
162
+ @session_store ||= @session_store_init.call
163
+ end
164
+
165
+ def session_store_init(&block)
166
+ @session_store_init = block
167
+ end
168
+ end
169
+
170
+ self.session_store_init do
171
+ store_path = File.expand_path(File.join(Isomorfeus.root, 'data', Isomorfeus.env), 'session_store')
172
+ Isomorfeus::Transport::HamsterSessionStore.new(store_path) # dont use this one, but we keep it here to have at least something
173
+ end
174
+ end
175
+
176
+ # defaults
177
+ self.api_websocket_host = 'localhost'
178
+ self.api_websocket_port = '3000'
179
+ self.api_websocket_path = '/isomorfeus/api/websocket'
180
+
181
+ self.cookie_eater_path = '/isomorfeus/cookie/eat'
182
+ end
@@ -0,0 +1,96 @@
1
+ module Isomorfeus
2
+ module Transport
3
+ class HamsterSessionStore
4
+ class << self
5
+ def environment
6
+ @environment
7
+ end
8
+
9
+ def environment=(env)
10
+ @environment = env
11
+ end
12
+
13
+ def ref
14
+ @ref ||= 0
15
+ end
16
+
17
+ def ref=(val)
18
+ @ref = val
19
+ end
20
+
21
+ def refa
22
+ self.ref += 1
23
+ end
24
+
25
+ def refs
26
+ self.ref -= 1 if self.ref > 0
27
+ end
28
+
29
+ def finalize(cls)
30
+ proc do
31
+ cls.refs
32
+ if cls.ref == 0
33
+ cls.environment.close rescue nil
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def initialize(cookie_hamster_path)
40
+ @cookie_hamster_path = cookie_hamster_path
41
+ open_environment
42
+ @db = self.class.environment.database('cookies', create: true)
43
+ ObjectSpace.define_finalizer(self, self.class.finalize(self.class))
44
+ end
45
+
46
+ def add(session_id:, cookie:, user:, accessor:)
47
+ @db.env.transaction do
48
+ @db.put(session_id, Oj.dump([user.class.to_s, user.key], mode: :strict))
49
+ @db.put(accessor, cookie)
50
+ end
51
+ end
52
+
53
+ def take_cookie(accessor:)
54
+ @db.env.transaction do
55
+ cookie = @db.get(accessor)
56
+ if cookie
57
+ session_info = cookie.split('; ').first
58
+ session_id = session_info.split('=').last.strip
59
+ @db.put("eaten_#{accessor}", session_id)
60
+ @db.delete(accessor)
61
+ else
62
+ # asked for the same cookie a second time
63
+ # can probably only be due to session hijacking
64
+ # so delete all sessions associated with that accessor
65
+ session_id = @db.get("eaten_#{accessor}")
66
+ @db.delete(session_id) if session_id
67
+ end
68
+ cookie
69
+ end
70
+ end
71
+
72
+ def get_user(session_id:)
73
+ json = @db.get(session_id)
74
+ if json
75
+ user_info = Oj.load(json, mode: :strict)
76
+ user_info[0].constantize.load(key: user_info[1]) if user_info
77
+ end
78
+ end
79
+
80
+ def remove(session_id:)
81
+ @db.env.transaction do
82
+ @db.delete(session_id)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def open_environment
89
+ return self.class.refa if self.class.environment
90
+ FileUtils.mkdir_p(@cookie_hamster_path) unless Dir.exist?(@cookie_hamster_path)
91
+ self.class.environment = Isomorfeus::Hamster.new(@cookie_hamster_path)
92
+ self.class.refa
93
+ end
94
+ end
95
+ end
96
+ end