@@ -77,6 +77,29 @@ impl RedisSessionStore {
77
77
self
78
78
}
79
79
80
+ async fn ids ( & self ) -> Result < Vec < String > > {
81
+ Ok ( self . connection ( ) . await ?. keys ( self . prefix_key ( "*" ) ) . await ?)
82
+ }
83
+
84
+ /// returns the number of sessions in this store
85
+ pub async fn count ( & self ) -> Result < usize > {
86
+ if self . prefix . is_none ( ) {
87
+ let mut connection = self . connection ( ) . await ?;
88
+ Ok ( redis:: cmd ( "DBSIZE" ) . query_async ( & mut connection) . await ?)
89
+ } else {
90
+ Ok ( self . ids ( ) . await ?. len ( ) )
91
+ }
92
+ }
93
+
94
+ #[ cfg( test) ]
95
+ async fn ttl_for_session ( & self , session : & Session ) -> Result < usize > {
96
+ Ok ( self
97
+ . connection ( )
98
+ . await ?
99
+ . ttl ( self . prefix_key ( session. id ( ) ) )
100
+ . await ?)
101
+ }
102
+
80
103
fn prefix_key ( & self , key : impl AsRef < str > ) -> String {
81
104
if let Some ( ref prefix) = self . prefix {
82
105
format ! ( "{}{}" , prefix, key. as_ref( ) )
@@ -95,15 +118,15 @@ impl SessionStore for RedisSessionStore {
95
118
async fn load_session ( & self , cookie_value : String ) -> Option < Session > {
96
119
let id = Session :: id_from_cookie_value ( & cookie_value) . ok ( ) ?;
97
120
let mut connection = self . connection ( ) . await . ok ( ) ?;
98
- let record: Option < String > = connection. get ( id ) . await . ok ( ) ?;
121
+ let record: Option < String > = connection. get ( self . prefix_key ( id ) ) . await . ok ( ) ?;
99
122
match record {
100
123
Some ( value) => serde_json:: from_str ( & value) . ok ( ) ?,
101
124
None => None ,
102
125
}
103
126
}
104
127
105
128
async fn store_session ( & self , session : Session ) -> Option < String > {
106
- let id = session. id ( ) ;
129
+ let id = self . prefix_key ( session. id ( ) ) ;
107
130
let string = serde_json:: to_string ( & session) . ok ( ) ?;
108
131
109
132
let mut connection = self . connection ( ) . await . ok ( ) ?;
@@ -128,7 +151,192 @@ impl SessionStore for RedisSessionStore {
128
151
}
129
152
130
153
async fn clear_store ( & self ) -> Result {
131
- self . connection ( ) . await ?. del ( self . prefix_key ( "*" ) ) . await ?;
154
+ let mut connection = self . connection ( ) . await ?;
155
+
156
+ if self . prefix . is_none ( ) {
157
+ let _: ( ) = redis:: cmd ( "FLUSHDB" ) . query_async ( & mut connection) . await ?;
158
+ } else {
159
+ let ids = self . ids ( ) . await ?;
160
+ if !ids. is_empty ( ) {
161
+ connection. del ( ids) . await ?;
162
+ }
163
+ }
164
+ Ok ( ( ) )
165
+ }
166
+ }
167
+
168
+ #[ cfg( test) ]
169
+ mod tests {
170
+ use super :: * ;
171
+ use async_std:: task;
172
+ use std:: time:: Duration ;
173
+
174
+ async fn test_store ( ) -> RedisSessionStore {
175
+ let store = RedisSessionStore :: new ( "redis://127.0.0.1" ) . unwrap ( ) ;
176
+ store. clear_store ( ) . await . unwrap ( ) ;
177
+ store
178
+ }
179
+
180
+ #[ async_std:: test]
181
+ async fn creating_a_new_session_with_no_expiry ( ) -> Result {
182
+ let store = test_store ( ) . await ;
183
+ let session = Session :: new ( ) ;
184
+ session. insert ( "key" . into ( ) , "value" . into ( ) ) ;
185
+ let cloned = session. clone ( ) ;
186
+ let cookie_value = store. store_session ( session) . await . unwrap ( ) ;
187
+
188
+ let loaded_session = store. load_session ( cookie_value) . await . unwrap ( ) ;
189
+ assert_eq ! ( cloned. id( ) , loaded_session. id( ) ) ;
190
+ assert_eq ! ( "value" , loaded_session. get( "key" ) . unwrap( ) ) ;
191
+
192
+ assert ! ( !loaded_session. is_expired( ) ) ;
193
+ Ok ( ( ) )
194
+ }
195
+
196
+ #[ async_std:: test]
197
+ async fn updating_a_session ( ) -> Result {
198
+ let store = test_store ( ) . await ;
199
+ let session = Session :: new ( ) ;
200
+
201
+ session. insert ( "key" . into ( ) , "value" . into ( ) ) ;
202
+ let cookie_value = store. store_session ( session) . await . unwrap ( ) ;
203
+
204
+ let session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
205
+ session. insert ( "key" . into ( ) , "other value" . into ( ) ) ;
206
+ assert_eq ! ( None , store. store_session( session) . await ) ;
207
+
208
+ let session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
209
+ assert_eq ! ( session. get( "key" ) . unwrap( ) , "other value" ) ;
210
+
211
+ assert_eq ! ( 1 , store. count( ) . await . unwrap( ) ) ;
212
+ Ok ( ( ) )
213
+ }
214
+
215
+ #[ async_std:: test]
216
+ async fn updating_a_session_extending_expiry ( ) -> Result {
217
+ let store = test_store ( ) . await ;
218
+ let mut session = Session :: new ( ) ;
219
+ session. expire_in ( Duration :: from_secs ( 5 ) ) ;
220
+ let original_expires = session. expiry ( ) . unwrap ( ) . clone ( ) ;
221
+ let cookie_value = store. store_session ( session) . await . unwrap ( ) ;
222
+
223
+ let mut session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
224
+ let ttl = store. ttl_for_session ( & session) . await ?;
225
+ assert ! ( ttl > 3 && ttl < 5 ) ;
226
+
227
+ assert_eq ! ( session. expiry( ) . unwrap( ) , & original_expires) ;
228
+ session. expire_in ( Duration :: from_secs ( 10 ) ) ;
229
+ let new_expires = session. expiry ( ) . unwrap ( ) . clone ( ) ;
230
+ store. store_session ( session) . await ;
231
+
232
+ let session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
233
+ let ttl = store. ttl_for_session ( & session) . await ?;
234
+ assert ! ( ttl > 8 && ttl < 10 ) ;
235
+ assert_eq ! ( session. expiry( ) . unwrap( ) , & new_expires) ;
236
+
237
+ assert_eq ! ( 1 , store. count( ) . await . unwrap( ) ) ;
238
+
239
+ task:: sleep ( Duration :: from_secs ( 10 ) ) . await ;
240
+ assert_eq ! ( 0 , store. count( ) . await . unwrap( ) ) ;
241
+
242
+ Ok ( ( ) )
243
+ }
244
+
245
+ #[ async_std:: test]
246
+ async fn creating_a_new_session_with_expiry ( ) -> Result {
247
+ let store = test_store ( ) . await ;
248
+ let mut session = Session :: new ( ) ;
249
+ session. expire_in ( Duration :: from_secs ( 3 ) ) ;
250
+ session. insert ( "key" . into ( ) , "value" . into ( ) ) ;
251
+ let cloned = session. clone ( ) ;
252
+
253
+ let cookie_value = store. store_session ( session) . await . unwrap ( ) ;
254
+
255
+ assert ! ( store. ttl_for_session( & cloned) . await ? > 1 ) ;
256
+
257
+ let loaded_session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
258
+ assert_eq ! ( cloned. id( ) , loaded_session. id( ) ) ;
259
+ assert_eq ! ( "value" , loaded_session. get( "key" ) . unwrap( ) ) ;
260
+
261
+ assert ! ( !loaded_session. is_expired( ) ) ;
262
+
263
+ task:: sleep ( Duration :: from_secs ( 2 ) ) . await ;
264
+ assert_eq ! ( None , store. load_session( cookie_value) . await ) ;
265
+
266
+ Ok ( ( ) )
267
+ }
268
+
269
+ #[ async_std:: test]
270
+ async fn destroying_a_single_session ( ) -> Result {
271
+ let store = test_store ( ) . await ;
272
+ for _ in 0 ..3i8 {
273
+ store. store_session ( Session :: new ( ) ) . await ;
274
+ }
275
+
276
+ let cookie = store. store_session ( Session :: new ( ) ) . await . unwrap ( ) ;
277
+ assert_eq ! ( 4 , store. count( ) . await ?) ;
278
+ let session = store. load_session ( cookie. clone ( ) ) . await . unwrap ( ) ;
279
+ store. destroy_session ( session. clone ( ) ) . await . unwrap ( ) ;
280
+ assert_eq ! ( None , store. load_session( cookie) . await ) ;
281
+ assert_eq ! ( 3 , store. count( ) . await ?) ;
282
+
283
+ // attempting to destroy the session again is not an error
284
+ assert ! ( store. destroy_session( session) . await . is_ok( ) ) ;
285
+ Ok ( ( ) )
286
+ }
287
+
288
+ #[ async_std:: test]
289
+ async fn clearing_the_whole_store ( ) -> Result {
290
+ let store = test_store ( ) . await ;
291
+ for _ in 0 ..3i8 {
292
+ store. store_session ( Session :: new ( ) ) . await ;
293
+ }
294
+
295
+ assert_eq ! ( 3 , store. count( ) . await ?) ;
296
+ store. clear_store ( ) . await . unwrap ( ) ;
297
+ assert_eq ! ( 0 , store. count( ) . await ?) ;
298
+
299
+ Ok ( ( ) )
300
+ }
301
+
302
+ #[ async_std:: test]
303
+ async fn prefixes ( ) -> Result {
304
+ test_store ( ) . await ; // clear the db
305
+
306
+ let store = RedisSessionStore :: new ( "redis://127.0.0.1" ) ?. with_prefix ( "sessions/" ) ;
307
+ store. clear_store ( ) . await ?;
308
+
309
+ for _ in 0 ..3i8 {
310
+ store. store_session ( Session :: new ( ) ) . await ;
311
+ }
312
+
313
+ let session = Session :: new ( ) ;
314
+
315
+ session. insert ( "key" . into ( ) , "value" . into ( ) ) ;
316
+ let cookie_value = store. store_session ( session) . await . unwrap ( ) ;
317
+
318
+ let session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
319
+ session. insert ( "key" . into ( ) , "other value" . into ( ) ) ;
320
+ assert_eq ! ( None , store. store_session( session) . await ) ;
321
+
322
+ let session = store. load_session ( cookie_value. clone ( ) ) . await . unwrap ( ) ;
323
+ assert_eq ! ( session. get( "key" ) . unwrap( ) , "other value" ) ;
324
+
325
+ assert_eq ! ( 4 , store. count( ) . await . unwrap( ) ) ;
326
+
327
+ let other_store =
328
+ RedisSessionStore :: new ( "redis://127.0.0.1" ) ?. with_prefix ( "other-namespace/" ) ;
329
+
330
+ assert_eq ! ( 0 , other_store. count( ) . await . unwrap( ) ) ;
331
+ for _ in 0 ..3i8 {
332
+ other_store. store_session ( Session :: new ( ) ) . await ;
333
+ }
334
+
335
+ other_store. clear_store ( ) . await ?;
336
+
337
+ assert_eq ! ( 0 , other_store. count( ) . await ?) ;
338
+ assert_eq ! ( 4 , store. count( ) . await ?) ;
339
+
132
340
Ok ( ( ) )
133
341
}
134
342
}
0 commit comments