1 module hunt.http.server.LocalSessionStore;
2 
3 import hunt.http.server.HttpSession;
4 import hunt.http.server.GlobalSettings;
5 
6 import hunt.http.Exceptions;
7 import hunt.http.Util;
8 
9 import hunt.collection.HashMap;
10 // import hunt.concurrency.Executors;
11 // import hunt.concurrency.Scheduler;
12 import hunt.concurrency.ScheduledThreadPoolExecutor;
13 import hunt.logging;
14 import hunt.util.AbstractLifecycle;
15 import hunt.util.Common;
16 import hunt.util.DateTime;
17 import hunt.util.Runnable;
18 
19 import core.sync.mutex;
20 import core.time;
21 import std.range;
22 
23 
24 /**
25  * 
26  */
27 class LocalSessionStore : AbstractLifecycle, SessionStore {
28 
29     private HashMap!(string, HttpSession) map;
30     private Mutex mapMutex;
31     private ScheduledThreadPoolExecutor executor;
32     // TODO: Tasks pending completion -@zhangxueping at 2019-10-23T11:21:26+08:00
33     // 
34     // private final ConcurrentMap<string, HttpSession map = new ConcurrentHashMap<>();
35 
36     this() {
37         map = new HashMap!(string, HttpSession);
38         mapMutex = new Mutex();
39         start();
40     }
41 
42 
43     bool contains(string key) {
44         return map.containsKey(key);
45     }
46 
47 
48     override
49     bool remove(string key) {
50         if (!key.empty()) {
51             mapMutex.lock();
52             scope(exit) mapMutex.unlock();
53         
54             map.remove(key);
55         }
56         return true;
57     }
58 
59     override
60     bool put(string key, HttpSession value) {
61         if (!key.empty() && value !is null) {
62             if (!value.isNewSession()) {
63                 value.setLastAccessedTime(DateTime.currentTimeMillis());
64             }
65             mapMutex.lock();
66             scope(exit) mapMutex.unlock();
67             map.put(key, value);
68         }
69         return true;
70     }
71 
72     override
73     HttpSession get(string key) {
74         if (key.empty()) {
75             throw new SessionNotFoundException();
76         }
77 
78         mapMutex.lock();
79         scope(exit) mapMutex.unlock();
80         HttpSession session = map.get(key);
81         if (session is null) {
82             throw new SessionNotFoundException(key);
83         } else {
84             if (!session.isValid()) {
85                 map.remove(session.getId());
86                 throw new SessionInvalidException("the session is expired");
87             } else {
88                 session.setLastAccessedTime(DateTime.currentTimeMillis());
89                 session.setNewSession(false);
90                 return session;
91             }
92         }
93     }
94 
95     override
96     int size() {
97         return map.size();
98     }
99 
100     private void cleaup() {
101         mapMutex.lock();
102         scope(exit) mapMutex.unlock();
103         string[] ids;
104         foreach(string id, HttpSession session; map) {
105             if (!session.isValid()) {
106                 ids ~= id; 
107             }
108         }
109 
110         if(ids.length>0) {
111             foreach(string id; ids) {
112                 map.remove(id);
113                 version(HUNT_DEBUG) tracef("remove expired session - %s", id);
114             }
115 
116             version(HUNT_HTTP_DEBUG) infof("session size: %d", map.size());
117         }
118     }
119 
120     override
121     protected void initialize() {
122         executor = CommonUtil.scheduler();
123         executor.setRemoveOnCancelPolicy(true);
124         executor.scheduleWithFixedDelay(new class Runnable {
125             void run() {
126                 cleaup();
127             }
128         }, 
129         seconds(1), seconds(1));
130     }
131 
132     override
133     protected void destroy() {
134         // if (executor !is null) {
135         //     executor.shutdown();
136         // }
137     }
138 }