BackendSession => SessionMap

This commit is contained in:
Michael Snoyman 2013-03-10 11:13:19 +02:00
parent 98613278d4
commit 4f1a6b461e
6 changed files with 30 additions and 32 deletions

View File

@ -38,7 +38,6 @@ module Yesod.Core
, clientSessionDateCacher , clientSessionDateCacher
, loadClientSession , loadClientSession
, Header(..) , Header(..)
, BackendSession
-- * JS loaders -- * JS loaders
, loadJsYepnope , loadJsYepnope
, ScriptLoadPosition (..) , ScriptLoadPosition (..)

View File

@ -27,6 +27,7 @@ import Data.Conduit (Flush, MonadThrow (..),
import Data.IntMap (IntMap) import Data.IntMap (IntMap)
import Data.IORef (IORef) import Data.IORef (IORef)
import Data.Map (Map, unionWith) import Data.Map (Map, unionWith)
import qualified Data.Map as Map
import Data.Monoid (Any, Endo (..), Last (..), import Data.Monoid (Any, Endo (..), Last (..),
Monoid (..)) Monoid (..))
import Data.Serialize (Serialize (..), import Data.Serialize (Serialize (..),
@ -53,30 +54,30 @@ import Yesod.Core.Trans.Class (MonadLift (..))
import Yesod.Routes.Class (RenderRoute (..)) import Yesod.Routes.Class (RenderRoute (..))
-- Sessions -- Sessions
type BackendSession = [(Text, ByteString)] type SessionMap = Map Text ByteString
type SaveSession = BackendSession -- ^ The session contents after running the handler type SaveSession = SessionMap -- ^ The session contents after running the handler
-> IO [Header] -> IO [Header]
newtype SessionBackend master = SessionBackend newtype SessionBackend master = SessionBackend
{ sbLoadSession :: master { sbLoadSession :: master
-> W.Request -> W.Request
-> IO (BackendSession, SaveSession) -- ^ Return the session data and a function to save the session -> IO (SessionMap, SaveSession) -- ^ Return the session data and a function to save the session
} }
data SessionCookie = SessionCookie (Either UTCTime ByteString) ByteString [(Text, ByteString)] data SessionCookie = SessionCookie (Either UTCTime ByteString) ByteString SessionMap
deriving (Show, Read) deriving (Show, Read)
instance Serialize SessionCookie where instance Serialize SessionCookie where
put (SessionCookie a b c) = do put (SessionCookie a b c) = do
either putTime putByteString a either putTime putByteString a
put b put b
put (map (first T.unpack) c) put (map (first T.unpack) $ Map.toList c)
get = do get = do
a <- getTime a <- getTime
b <- get b <- get
c <- map (first T.pack) <$> get c <- map (first T.pack) <$> get
return $ SessionCookie (Left a) b c return $ SessionCookie (Left a) b (Map.fromList c)
data ClientSessionDateCache = data ClientSessionDateCache =
ClientSessionDateCache { ClientSessionDateCache {
@ -174,8 +175,6 @@ data GHState = GHState
, ghsHeaders :: Endo [Header] , ghsHeaders :: Endo [Header]
} }
type SessionMap = Map Text ByteString
-- | An extension of the basic WAI 'W.Application' datatype to provide extra -- | An extension of the basic WAI 'W.Application' datatype to provide extra
-- features needed by Yesod. Users should never need to use this directly, as -- features needed by Yesod. Users should never need to use this directly, as
-- the 'GHandler' monad and template haskell code should hide it away. -- the 'GHandler' monad and template haskell code should hide it away.

View File

@ -28,7 +28,6 @@ module Yesod.Internal.Core
, clientSessionBackend , clientSessionBackend
, loadClientSession , loadClientSession
, clientSessionDateCacher , clientSessionDateCacher
, BackendSession
-- * jsLoader -- * jsLoader
, ScriptLoadPosition (..) , ScriptLoadPosition (..)
, BottomOfHeadAsync , BottomOfHeadAsync
@ -424,7 +423,7 @@ defaultYesodRunner logger handler' master sub murl toMasterRoute msb req
| otherwise = do | otherwise = do
let dontSaveSession _ = return [] let dontSaveSession _ = return []
(session, saveSession) <- liftIO $ do (session, saveSession) <- liftIO $ do
maybe (return ([], dontSaveSession)) (\sb -> sbLoadSession sb master req) msb maybe (return (Map.empty, dontSaveSession)) (\sb -> sbLoadSession sb master req) msb
rr <- liftIO $ parseWaiRequest req session (isJust msb) maxLen rr <- liftIO $ parseWaiRequest req session (isJust msb) maxLen
let h = {-# SCC "h" #-} do let h = {-# SCC "h" #-} do
case murl of case murl of
@ -443,14 +442,14 @@ defaultYesodRunner logger handler' master sub murl toMasterRoute msb req
redirect url' redirect url'
Unauthorized s' -> permissionDenied s' Unauthorized s' -> permissionDenied s'
handler handler
let sessionMap = Map.fromList . filter ((/=) tokenKey . fst) $ session let sessionMap = Map.filterWithKey (\k _v -> k /= tokenKey) $ session
let ra = resolveApproot master req let ra = resolveApproot master req
let log' = messageLoggerSource master logger let log' = messageLoggerSource master logger
yar <- handlerToYAR master sub (fileUpload master) log' toMasterRoute yar <- handlerToYAR master sub (fileUpload master) log' toMasterRoute
(yesodRender master ra) errorHandler rr murl sessionMap h (yesodRender master ra) errorHandler rr murl sessionMap h
extraHeaders <- case yar of extraHeaders <- case yar of
(YARPlain _ _ ct _ newSess) -> do (YARPlain _ _ ct _ newSess) -> do
let nsToken = Map.toList $ maybe let nsToken = maybe
newSess newSess
(\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess) (\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess)
(reqToken rr) (reqToken rr)
@ -745,13 +744,13 @@ loadClientSession :: Yesod master
-> S8.ByteString -- ^ session name -> S8.ByteString -- ^ session name
-> master -> master
-> W.Request -> W.Request
-> IO (BackendSession, SaveSession) -> IO (SessionMap, SaveSession)
loadClientSession key getCachedDate sessionName master req = load loadClientSession key getCachedDate sessionName master req = load
where where
load = do load = do
date <- getCachedDate date <- getCachedDate
return (sess date, save date) return (sess date, save date)
sess date = fromMaybe [] $ do sess date = fromMaybe Map.empty $ do
raw <- lookup "Cookie" $ W.requestHeaders req raw <- lookup "Cookie" $ W.requestHeaders req
val <- lookup sessionName $ parseCookies raw val <- lookup sessionName $ parseCookies raw
let host = "" -- fixme, properly lock sessions to client address let host = "" -- fixme, properly lock sessions to client address

View File

@ -44,9 +44,10 @@ import Data.Word (Word64)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Yesod.Core.Types import Yesod.Core.Types
import qualified Data.Map as Map
parseWaiRequest :: W.Request parseWaiRequest :: W.Request
-> [(Text, ByteString)] -- ^ session -> SessionMap
-> Bool -> Bool
-> Word64 -- ^ maximum allowed body size -> Word64 -- ^ maximum allowed body size
-> IO Request -> IO Request
@ -80,7 +81,7 @@ tooLargeResponse = W.responseLBS
parseWaiRequest' :: RandomGen g parseWaiRequest' :: RandomGen g
=> W.Request => W.Request
-> [(Text, ByteString)] -- ^ session -> SessionMap
-> Bool -> Bool
-> Word64 -- ^ max body size -> Word64 -- ^ max body size
-> g -> g
@ -95,7 +96,7 @@ parseWaiRequest' env session' useToken maxBodySize gen =
acceptLang = lookup "Accept-Language" $ W.requestHeaders env acceptLang = lookup "Accept-Language" $ W.requestHeaders env
langs = map (pack . S8.unpack) $ maybe [] NWP.parseHttpAccept acceptLang langs = map (pack . S8.unpack) $ maybe [] NWP.parseHttpAccept acceptLang
lookupText k = fmap (decodeUtf8With lenientDecode) . lookup k lookupText k = fmap (decodeUtf8With lenientDecode) . Map.lookup k
-- The language preferences are prioritized as follows: -- The language preferences are prioritized as follows:
langs' = catMaybes [ join $ lookup langKey gets' -- Query _LANG langs' = catMaybes [ join $ lookup langKey gets' -- Query _LANG
@ -116,7 +117,7 @@ parseWaiRequest' env session' useToken maxBodySize gen =
else Just $ maybe else Just $ maybe
(pack $ randomString 10 gen) (pack $ randomString 10 gen)
(decodeUtf8With lenientDecode) (decodeUtf8With lenientDecode)
(lookup tokenKey session') (Map.lookup tokenKey session')
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text] addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
addTwoLetters (toAdd, exist) [] = addTwoLetters (toAdd, exist) [] =

View File

@ -3,7 +3,6 @@ module Yesod.Internal.Session
, decodeClientSession , decodeClientSession
, clientSessionDateCacher , clientSessionDateCacher
, ClientSessionDateCache(..) , ClientSessionDateCache(..)
, BackendSession
, SaveSession , SaveSession
, SessionBackend(..) , SessionBackend(..)
) where ) where
@ -14,7 +13,6 @@ import Data.Time
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Control.Concurrent (forkIO, killThread, threadDelay) import Control.Concurrent (forkIO, killThread, threadDelay)
import Control.Monad (forever, guard) import Control.Monad (forever, guard)
import Data.Text (Text)
import Yesod.Core.Types import Yesod.Core.Types
import Yesod.Core.Time import Yesod.Core.Time
import qualified Data.IORef as I import qualified Data.IORef as I
@ -23,7 +21,7 @@ encodeClientSession :: CS.Key
-> CS.IV -> CS.IV
-> ClientSessionDateCache -- ^ expire time -> ClientSessionDateCache -- ^ expire time
-> ByteString -- ^ remote host -> ByteString -- ^ remote host
-> [(Text, ByteString)] -- ^ session -> SessionMap -- ^ session
-> ByteString -- ^ cookie value -> ByteString -- ^ cookie value
encodeClientSession key iv date rhost session' = encodeClientSession key iv date rhost session' =
CS.encrypt key iv $ encode $ SessionCookie expires rhost session' CS.encrypt key iv $ encode $ SessionCookie expires rhost session'
@ -33,7 +31,7 @@ decodeClientSession :: CS.Key
-> ClientSessionDateCache -- ^ current time -> ClientSessionDateCache -- ^ current time
-> ByteString -- ^ remote host field -> ByteString -- ^ remote host field
-> ByteString -- ^ cookie value -> ByteString -- ^ cookie value
-> Maybe [(Text, ByteString)] -> Maybe SessionMap
decodeClientSession key date rhost encrypted = do decodeClientSession key date rhost encrypted = do
decrypted <- CS.decrypt key encrypted decrypted <- CS.decrypt key encrypted
SessionCookie (Left expire) rhost' session' <- SessionCookie (Left expire) rhost' session' <-

View File

@ -9,6 +9,8 @@ import Network.Wai.Test
import Yesod.Internal.TestApi (randomString, parseWaiRequest') import Yesod.Internal.TestApi (randomString, parseWaiRequest')
import Yesod.Request (Request (..)) import Yesod.Request (Request (..))
import Test.Hspec import Test.Hspec
import Data.Monoid (mempty)
import Data.Map (singleton)
randomStringSpecs :: Spec randomStringSpecs :: Spec
randomStringSpecs = describe "Yesod.Internal.Request.randomString" $ do randomStringSpecs = describe "Yesod.Internal.Request.randomString" $ do
@ -38,19 +40,19 @@ tokenSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqToken)" $ do
noDisabledToken :: Bool noDisabledToken :: Bool
noDisabledToken = reqToken r == Nothing where noDisabledToken = reqToken r == Nothing where
r = parseWaiRequest' defaultRequest [] False 1000 g r = parseWaiRequest' defaultRequest mempty False 1000 g
ignoreDisabledToken :: Bool ignoreDisabledToken :: Bool
ignoreDisabledToken = reqToken r == Nothing where ignoreDisabledToken = reqToken r == Nothing where
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] False 1000 g r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") False 1000 g
useOldToken :: Bool useOldToken :: Bool
useOldToken = reqToken r == Just "old" where useOldToken = reqToken r == Just "old" where
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True 1000 g r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") True 1000 g
generateToken :: Bool generateToken :: Bool
generateToken = reqToken r /= Nothing where generateToken = reqToken r /= Nothing where
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True 1000 g r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") True 1000 g
langSpecs :: Spec langSpecs :: Spec
@ -64,21 +66,21 @@ langSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqLangs)" $ do
respectAcceptLangs :: Bool respectAcceptLangs :: Bool
respectAcceptLangs = reqLangs r == ["en-US", "es", "en"] where respectAcceptLangs = reqLangs r == ["en-US", "es", "en"] where
r = parseWaiRequest' defaultRequest r = parseWaiRequest' defaultRequest
{ requestHeaders = [("Accept-Language", "en-US, es")] } [] False 1000 g { requestHeaders = [("Accept-Language", "en-US, es")] } mempty False 1000 g
respectSessionLang :: Bool respectSessionLang :: Bool
respectSessionLang = reqLangs r == ["en"] where respectSessionLang = reqLangs r == ["en"] where
r = parseWaiRequest' defaultRequest [("_LANG", "en")] False 1000 g r = parseWaiRequest' defaultRequest (singleton "_LANG" "en") False 1000 g
respectCookieLang :: Bool respectCookieLang :: Bool
respectCookieLang = reqLangs r == ["en"] where respectCookieLang = reqLangs r == ["en"] where
r = parseWaiRequest' defaultRequest r = parseWaiRequest' defaultRequest
{ requestHeaders = [("Cookie", "_LANG=en")] { requestHeaders = [("Cookie", "_LANG=en")]
} [] False 1000 g } mempty False 1000 g
respectQueryLang :: Bool respectQueryLang :: Bool
respectQueryLang = reqLangs r == ["en-US", "en"] where respectQueryLang = reqLangs r == ["en-US", "en"] where
r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } [] False 1000 g r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } mempty False 1000 g
prioritizeLangs :: Bool prioritizeLangs :: Bool
prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "es"] where prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "es"] where
@ -87,7 +89,7 @@ prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "e
, ("Cookie", "_LANG=en-COOKIE") , ("Cookie", "_LANG=en-COOKIE")
] ]
, queryString = [("_LANG", Just "en-QUERY")] , queryString = [("_LANG", Just "en-QUERY")]
} [("_LANG", "en-SESSION")] False 10000 g } (singleton "_LANG" "en-SESSION") False 10000 g
internalRequestTest :: Spec internalRequestTest :: Spec