Merge branch 'master' into limit_session_key
This commit is contained in:
commit
be6d9d2aaf
@ -56,7 +56,7 @@ before_install:
|
|||||||
- export PATH=$HOME/.local/bin:$PATH
|
- export PATH=$HOME/.local/bin:$PATH
|
||||||
- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
|
- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
|
||||||
- export PATH=/opt/ghc/$GHCVER/bin:$PATH
|
- export PATH=/opt/ghc/$GHCVER/bin:$PATH
|
||||||
- export RUNSTACK="stack --no-terminal --skip-ghc-check --resolver=$RESOLVER"
|
- export RUNSTACK="stack --no-terminal --skip-ghc-check --system-ghc --resolver=$RESOLVER"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- $RUNSTACK --version
|
- $RUNSTACK --version
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import Control.Monad (void, when)
|
|||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.List (partition)
|
import Data.List (partition)
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe, catMaybes)
|
||||||
import Data.Proxy (Proxy(..))
|
import Data.Proxy (Proxy(..))
|
||||||
import Data.Typeable (Typeable)
|
import Data.Typeable (Typeable)
|
||||||
import Web.PathPieces (toPathPiece)
|
import Web.PathPieces (toPathPiece)
|
||||||
@ -45,6 +45,7 @@ import qualified Data.ByteString.Char8 as B8
|
|||||||
import qualified Data.HashMap.Strict as HM
|
import qualified Data.HashMap.Strict as HM
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
import qualified Data.Time.Clock as TI
|
import qualified Data.Time.Clock as TI
|
||||||
|
import qualified Data.Time.Clock.POSIX as TP
|
||||||
import qualified Data.Time.Format as TI
|
import qualified Data.Time.Format as TI
|
||||||
|
|
||||||
#if MIN_VERSION_time(1,5,0)
|
#if MIN_VERSION_time(1,5,0)
|
||||||
@ -57,10 +58,14 @@ import System.Locale (defaultTimeLocale)
|
|||||||
|
|
||||||
|
|
||||||
-- | Session storage backend using Redis via the @hedis@ package.
|
-- | Session storage backend using Redis via the @hedis@ package.
|
||||||
newtype RedisStorage sess =
|
data RedisStorage sess =
|
||||||
RedisStorage
|
RedisStorage
|
||||||
{ connPool :: R.Connection
|
{ connPool :: R.Connection
|
||||||
-- ^ Connection pool to the Redis server.
|
-- ^ Connection pool to the Redis server.
|
||||||
|
, idleTimeout :: Maybe TI.NominalDiffTime
|
||||||
|
-- ^ How long should a session live after last access
|
||||||
|
, absoluteTimeout :: Maybe TI.NominalDiffTime
|
||||||
|
-- ^ How long should a session live after creation
|
||||||
} deriving (Typeable)
|
} deriving (Typeable)
|
||||||
|
|
||||||
|
|
||||||
@ -73,8 +78,8 @@ instance RedisSession sess => Storage (RedisStorage sess) where
|
|||||||
getSession _ = getSessionImpl
|
getSession _ = getSessionImpl
|
||||||
deleteSession _ = deleteSessionImpl
|
deleteSession _ = deleteSessionImpl
|
||||||
deleteAllSessionsOfAuthId _ = deleteAllSessionsOfAuthIdImpl
|
deleteAllSessionsOfAuthId _ = deleteAllSessionsOfAuthIdImpl
|
||||||
insertSession _ = insertSessionImpl
|
insertSession = insertSessionImpl
|
||||||
replaceSession _ = replaceSessionImpl
|
replaceSession = replaceSessionImpl
|
||||||
|
|
||||||
|
|
||||||
-- | An exception thrown by the @serversession-backend-redis@
|
-- | An exception thrown by the @serversession-backend-redis@
|
||||||
@ -271,8 +276,8 @@ deleteAllSessionsOfAuthIdImpl authId = do
|
|||||||
|
|
||||||
|
|
||||||
-- | Insert a new session.
|
-- | Insert a new session.
|
||||||
insertSessionImpl :: RedisSession sess => Session sess -> R.Redis ()
|
insertSessionImpl :: RedisSession sess => RedisStorage sess -> Session sess -> R.Redis ()
|
||||||
insertSessionImpl session = do
|
insertSessionImpl sto session = do
|
||||||
-- Check that no old session exists.
|
-- Check that no old session exists.
|
||||||
let sid = sessionKey session
|
let sid = sessionKey session
|
||||||
moldSession <- getSessionImpl sid
|
moldSession <- getSessionImpl sid
|
||||||
@ -282,14 +287,14 @@ insertSessionImpl session = do
|
|||||||
transaction $ do
|
transaction $ do
|
||||||
let sk = rSessionKey sid
|
let sk = rSessionKey sid
|
||||||
r <- batched (R.hmset sk) (printSession session)
|
r <- batched (R.hmset sk) (printSession session)
|
||||||
-- TODO: R.expireat
|
expireSession session sto
|
||||||
insertSessionForAuthId (sessionKey session) (sessionAuthId session)
|
insertSessionForAuthId (sessionKey session) (sessionAuthId session)
|
||||||
return (() <$ r)
|
return (() <$ r)
|
||||||
|
|
||||||
|
|
||||||
-- | Replace the contents of a session.
|
-- | Replace the contents of a session.
|
||||||
replaceSessionImpl :: RedisSession sess => Session sess -> R.Redis ()
|
replaceSessionImpl :: RedisSession sess => RedisStorage sess -> Session sess -> R.Redis ()
|
||||||
replaceSessionImpl session = do
|
replaceSessionImpl sto session = do
|
||||||
-- Check that the old session exists.
|
-- Check that the old session exists.
|
||||||
let sid = sessionKey session
|
let sid = sessionKey session
|
||||||
moldSession <- getSessionImpl sid
|
moldSession <- getSessionImpl sid
|
||||||
@ -301,6 +306,7 @@ replaceSessionImpl session = do
|
|||||||
let sk = rSessionKey sid
|
let sk = rSessionKey sid
|
||||||
_ <- R.del [sk]
|
_ <- R.del [sk]
|
||||||
r <- batched (R.hmset sk) (printSession session)
|
r <- batched (R.hmset sk) (printSession session)
|
||||||
|
expireSession session sto
|
||||||
|
|
||||||
-- Remove the old auth ID from the map if it has changed.
|
-- Remove the old auth ID from the map if it has changed.
|
||||||
let oldAuthId = sessionAuthId oldSession
|
let oldAuthId = sessionAuthId oldSession
|
||||||
@ -318,3 +324,21 @@ throwRS
|
|||||||
=> StorageException (RedisStorage sess)
|
=> StorageException (RedisStorage sess)
|
||||||
-> R.Redis a
|
-> R.Redis a
|
||||||
throwRS = liftIO . E.throwIO
|
throwRS = liftIO . E.throwIO
|
||||||
|
|
||||||
|
|
||||||
|
-- | Given a session, finds the next time the session will time out,
|
||||||
|
-- either by idle or absolute timeout and schedule the key in redis to
|
||||||
|
-- expire at that time. This is meant to be used on every write to a
|
||||||
|
-- session so that it is constantly setting the appropriate timeout.
|
||||||
|
expireSession :: Session sess -> RedisStorage sess -> R.RedisTx ()
|
||||||
|
expireSession Session {..} RedisStorage {..} =
|
||||||
|
case minimum' (catMaybes [viaIdle, viaAbsolute]) of
|
||||||
|
Nothing -> return ()
|
||||||
|
Just t -> let ts = round (TP.utcTimeToPOSIXSeconds t)
|
||||||
|
in void (R.expireat sk ts)
|
||||||
|
where
|
||||||
|
sk = rSessionKey sessionKey
|
||||||
|
minimum' [] = Nothing
|
||||||
|
minimum' xs = Just (minimum xs)
|
||||||
|
viaIdle = flip TI.addUTCTime sessionAccessedAt <$> idleTimeout
|
||||||
|
viaAbsolute = flip TI.addUTCTime sessionCreatedAt <$> absoluteTimeout
|
||||||
|
|||||||
@ -3,10 +3,11 @@ module Main (main) where
|
|||||||
import Database.Redis (connect, defaultConnectInfo)
|
import Database.Redis (connect, defaultConnectInfo)
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Web.ServerSession.Backend.Redis
|
import Web.ServerSession.Backend.Redis
|
||||||
|
import Web.ServerSession.Core
|
||||||
import Web.ServerSession.Core.StorageTests
|
import Web.ServerSession.Core.StorageTests
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
conn <- connect defaultConnectInfo
|
conn <- connect defaultConnectInfo
|
||||||
hspec $ describe "RedisStorage" $
|
hspec $ describe "RedisStorage" $
|
||||||
allStorageTests (RedisStorage conn) it runIO parallel shouldBe shouldReturn shouldThrow
|
allStorageTests (RedisStorage conn (Just 999999) (Just 999999)) it runIO parallel shouldBe shouldReturn shouldThrow
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user