module Web.Spock.SessionManager
( openSessionManager
, SessionId, Session(..), SessionManager(..)
)
where
import Web.Spock.Types
import Web.Spock.Cookie
import Control.Concurrent.STM
import Control.Monad.Trans
import Data.Time
import System.Random
import Web.Scotty.Trans
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Base64 as B64
import qualified Data.HashMap.Strict as HM
import qualified Data.Text.Encoding as T
openSessionManager :: SessionCfg -> IO (SessionManager a)
openSessionManager cfg =
do cacheHM <- atomically $ newTVar HM.empty
return $ SessionManager
{ sm_loadSession = loadSessionImpl cfg cacheHM
, sm_sessionFromCookie = sessionFromCookieImpl cfg cacheHM
, sm_createCookieSession = createCookieSessionImpl cfg cacheHM
, sm_newSession = newSessionImpl cfg cacheHM
, sm_deleteSession = deleteSessionImpl cacheHM
}
createCookieSessionImpl :: (SpockError e, MonadIO m)
=> SessionCfg
-> UserSessions a
-> a
-> ActionT e m ()
createCookieSessionImpl sessCfg sessRef val =
do sess <- liftIO $ newSessionImpl sessCfg sessRef val
setCookie' (sc_cookieName sessCfg) (sess_id sess) (sess_validUntil sess)
newSessionImpl :: SessionCfg
-> UserSessions a
-> a
-> IO (Session a)
newSessionImpl sessCfg sessionRef content =
do sess <- createSession sessCfg content
atomically $ modifyTVar sessionRef (\hm -> HM.insert (sess_id sess) sess hm)
return sess
sessionFromCookieImpl :: (SpockError e, MonadIO m)
=> SessionCfg
-> UserSessions a
-> ActionT e m (Maybe (Session a))
sessionFromCookieImpl sessCfg sessionRef =
do mSid <- getCookie (sc_cookieName sessCfg)
case mSid of
Just sid ->
liftIO $ loadSessionImpl sessCfg sessionRef sid
Nothing ->
return Nothing
loadSessionImpl :: SessionCfg
-> UserSessions a
-> SessionId
-> IO (Maybe (Session a))
loadSessionImpl sessCfg sessionRef sid =
do sessHM <- atomically $ readTVar sessionRef
now <- getCurrentTime
case HM.lookup sid sessHM of
Just sess ->
do if addUTCTime (sc_sessionTTL sessCfg) (sess_validUntil sess) > now
then return $ Just sess
else do deleteSessionImpl sessionRef sid
return Nothing
Nothing ->
return Nothing
deleteSessionImpl :: UserSessions a
-> SessionId
-> IO ()
deleteSessionImpl sessionRef sid =
do atomically $ modifyTVar sessionRef (\hm -> HM.delete sid hm)
return ()
createSession :: SessionCfg -> a -> IO (Session a)
createSession sessCfg content =
do gen <- g
let sid = T.decodeUtf8 $ B64.encode $ BSC.pack $
take (sc_sessionIdEntropy sessCfg) $ randoms gen
now <- getCurrentTime
let validUntil = addUTCTime (sc_sessionTTL sessCfg) now
return (Session sid validUntil content)
where
g = newStdGen :: IO StdGen