fix(serversession-backend-memcached): don't throw on deleteSession

This commit is contained in:
Gregor Kleen 2020-05-18 08:53:16 +02:00
parent ec020c5486
commit bcd3e467d6
4 changed files with 43 additions and 41 deletions

View File

@ -5,7 +5,7 @@ module Application
, appMain , appMain
, develMain , develMain
, makeFoundation , makeFoundation
, makeLogWare , makeMiddleware
-- * for DevelMain -- * for DevelMain
, foundationStoreNum , foundationStoreNum
, getApplicationRepl , getApplicationRepl
@ -330,40 +330,14 @@ createMemcached MemcachedConf{memcachedConnectInfo} = snd <$> allocate (Memcache
-- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and -- | Convert our foundation to a WAI Application by calling @toWaiAppPlain@ and
-- applying some additional middlewares. -- applying some additional middlewares.
makeApplication :: MonadIO m => UniWorX -> m Application makeApplication :: MonadIO m => UniWorX -> m Application
makeApplication foundation = liftIO $ do makeApplication foundation = liftIO $ makeMiddleware foundation <*> toWaiAppPlain foundation
logWare <- makeLogWare foundation
-- Create the WAI application and apply middlewares makeMiddleware :: MonadIO m => UniWorX -> m Middleware
appPlain <- toWaiAppPlain foundation makeMiddleware app = do
return . observeHTTPRequestLatency classifyHandler . logWare . normalizeCookies $ defaultMiddlewaresNoLogging appPlain logWare <- makeLogWare
return $ observeHTTPRequestLatency classifyHandler . logWare . normalizeCookies . defaultMiddlewaresNoLogging
where where
normalizeCookies :: Wai.Middleware makeLogWare = do
normalizeCookies app req respond = app req $ \res -> do
resHdrs' <- go $ Wai.responseHeaders res
respond $ Wai.mapResponseHeaders (const resHdrs') res
where parseSetCookie' :: ByteString -> IO (Maybe SetCookie)
parseSetCookie' = fmap (either (\(_ :: SomeException) -> Nothing) Just) . try . evaluate . force . parseSetCookie
go [] = return []
go (hdr@(hdrName, hdrValue) : hdrs)
| hdrName == hSetCookie = do
mcookieHdr <- parseSetCookie' hdrValue
case mcookieHdr of
Nothing -> (hdr :) <$> go hdrs
Just cookieHdr -> do
let cookieHdrMatches hdrValue' = maybeT (return False) $ do
cookieHdr' <- MaybeT $ parseSetCookie' hdrValue'
-- See https://tools.ietf.org/html/rfc6265
guard $ setCookiePath cookieHdr' == setCookiePath cookieHdr
guard $ setCookieName cookieHdr' == setCookieName cookieHdr
guard $ setCookieDomain cookieHdr' == setCookieDomain cookieHdr
return True
others <- filterM (\(hdrName', hdrValue') -> and2M (pure $ hdrName' == hSetCookie) (cookieHdrMatches hdrValue')) hdrs
if | null others -> (hdr :) <$> go hdrs
| otherwise -> go hdrs
| otherwise = (hdr :) <$> go hdrs
makeLogWare :: MonadIO m => UniWorX -> m Middleware
makeLogWare app = do
logWareMap <- liftIO $ newTVarIO HashMap.empty logWareMap <- liftIO $ newTVarIO HashMap.empty
let let
@ -390,6 +364,34 @@ makeLogWare app = do
logWare <- either mkLogWare return lookupRes logWare <- either mkLogWare return lookupRes
logWare wai req fin logWare wai req fin
normalizeCookies :: Wai.Middleware
normalizeCookies waiApp req respond = waiApp req $ \res -> do
resHdrs' <- go $ Wai.responseHeaders res
respond $ Wai.mapResponseHeaders (const resHdrs') res
where parseSetCookie' :: ByteString -> IO (Maybe SetCookie)
parseSetCookie' = fmap (either (\(_ :: SomeException) -> Nothing) Just) . try . evaluate . force . parseSetCookie
go [] = return []
go (hdr@(hdrName, hdrValue) : hdrs)
| hdrName == hSetCookie = do
mcookieHdr <- parseSetCookie' hdrValue
case mcookieHdr of
Nothing -> (hdr :) <$> go hdrs
Just cookieHdr -> do
let cookieHdrMatches hdrValue' = maybeT (return False) $ do
cookieHdr' <- MaybeT $ parseSetCookie' hdrValue'
-- See https://tools.ietf.org/html/rfc6265
guard $ setCookiePath cookieHdr' == setCookiePath cookieHdr
guard $ setCookieName cookieHdr' == setCookieName cookieHdr
guard $ setCookieDomain cookieHdr' == setCookieDomain cookieHdr
return True
others <- filterM (\(hdrName', hdrValue') -> and2M (pure $ hdrName' == hSetCookie) (cookieHdrMatches hdrValue')) hdrs
if | null others -> (hdr :) <$> go hdrs
| otherwise -> go hdrs
| otherwise = (hdr :) <$> go hdrs
-- | Warp settings for the given foundation value. -- | Warp settings for the given foundation value.
warpSettings :: UniWorX -> Settings warpSettings :: UniWorX -> Settings
warpSettings foundation = defaultSettings warpSettings foundation = defaultSettings

View File

@ -1,5 +1,5 @@
module Utils.Sql module Utils.Sql
( setSerializable ( setSerializable, setSerializable'
) where ) where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
@ -17,13 +17,13 @@ import Control.Lens ((&))
setSerializable :: forall m a. (MonadLogger m, MonadMask m, MonadIO m) => ReaderT SqlBackend m a -> ReaderT SqlBackend m a setSerializable :: forall m a. (MonadLogger m, MonadMask m, MonadIO m) => ReaderT SqlBackend m a -> ReaderT SqlBackend m a
setSerializable act = do setSerializable = setSerializable' $ fullJitterBackoff 1e3 & limitRetriesByCumulativeDelay 10e6
setSerializable' :: forall m a. (MonadLogger m, MonadMask m, MonadIO m) => RetryPolicyM (ReaderT SqlBackend m) -> ReaderT SqlBackend m a -> ReaderT SqlBackend m a
setSerializable' policy act = do
didCommit <- newTVarIO False didCommit <- newTVarIO False
recovering policy (skipAsyncExceptions `snoc` logRetries suggestRetry logRetry) $ act' didCommit recovering policy (skipAsyncExceptions `snoc` logRetries suggestRetry logRetry) $ act' didCommit
where where
policy :: RetryPolicyM (ReaderT SqlBackend m)
policy = fullJitterBackoff 1e3 & limitRetriesByCumulativeDelay 10e6
suggestRetry :: SqlError -> ReaderT SqlBackend m Bool suggestRetry :: SqlError -> ReaderT SqlBackend m Bool
suggestRetry = return . isSerializationError suggestRetry = return . isSerializationError

View File

@ -135,7 +135,7 @@ instance (IsSessionData sess, Binary (Decomposed sess)) => Storage (MemcachedSql
where expiry = maybe 0 ceiling mcdSqlMemcachedExpiration where expiry = maybe 0 ceiling mcdSqlMemcachedExpiration
deleteSession MemcachedSqlStorage{..} sessId deleteSession MemcachedSqlStorage{..} sessId
= liftIO $ Memcached.delete (memcachedSqlSessionId # sessId) mcdSqlMemcached = liftIO . handleIf Memcached.isKeyNotFound (const $ return ()) $ Memcached.delete (memcachedSqlSessionId # sessId) mcdSqlMemcached
deleteAllSessionsOfAuthId MemcachedSqlStorage{..} authId = do deleteAllSessionsOfAuthId MemcachedSqlStorage{..} authId = do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime

View File

@ -3,7 +3,7 @@ module TestImport
, module X , module X
) where ) where
import Application (makeFoundation, makeLogWare, shutdownApp) import Application (makeFoundation, makeMiddleware, shutdownApp)
import ClassyPrelude as X import ClassyPrelude as X
hiding ( delete, deleteBy hiding ( delete, deleteBy
, Handler, Index , Handler, Index
@ -90,7 +90,7 @@ withApp = around $ \act -> runResourceT $ do
foundation <- makeFoundation settings foundation <- makeFoundation settings
wipeDB foundation wipeDB foundation
runAppLoggingT foundation $ handleJobs foundation runAppLoggingT foundation $ handleJobs foundation
logWare <- makeLogWare foundation logWare <- makeMiddleware foundation
lift $ act (foundation, logWare) `finally` shutdownApp foundation lift $ act (foundation, logWare) `finally` shutdownApp foundation
-- This function will truncate all of the tables in your database. -- This function will truncate all of the tables in your database.