-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | client-side session data -- -- uses the clientsession library to store session data in an HTTP cookie @package happstack-clientsession @version 7.3.2 -- | This module provides a simple session implementation which stores -- session data on the client as a cookie value. -- -- The cookie values are stored in an encrypted cookie to make it more -- difficult for users to tamper with the values. However, this does not -- prevent replay attacks, and should not be seen as a substitute for -- using HTTPS. Additionally, the cryptography libraries used to encrypt -- the cookie have never been audited. Hence you are encouraged to think -- carefully about what data you put in the session data. -- -- Another important thing to realize is client-side sessions do not -- provide Isolation. Imagine if the browser makes multiple simultaneous -- requests, which each modify the session data. The browser will submit -- the same cookie for each the requests, and each request handling -- thread will get their own copy of the session data. The threads will -- then modify their local copies independently and send their modified -- values back to the browser, overwriting each other. The final value -- will be determined by which ever request is sent last, and any changes -- made by the other request will be entirely lost. -- -- This means that clientsessions would not be suitable for implementing -- a request counter, because if overlapping requests are made, the count -- will be off. The count will only be accurate if the requests are -- processed sequentially. That said, the demo code implements a request -- counter anyway, because it is short and sweet. Also, this caveat was -- forgotten when the example code was being written. -- -- If you only modify the session data for POST requests, but not GET -- requests you are less likely to run into situations where you are -- losing changes, because there are not a lot of cases where a client -- will be submitting multiple POST requests in parallel. Though there is -- no guarantee. -- -- Alternatively, you can choose to only store data where it is OK -- if modifications are lost. For example, if the session data contains -- only a userid and the time of the last request they made, then there -- is no great loss if some of the modifications are lost, because the -- access times are going to all be about the same anyway. -- -- By default the client will need to submit the cookie that contains the -- client session data for every request (including images, and other -- static assets). So, storing a large amount of data in the client -- session will make requests slower and is not recommended. If you have -- assets which can be served with out examining the client session data -- you can use the sessionPath and sessionDomain parameters -- of SessionConf to limit when the browser sends the session data -- cookie. -- -- The first thing you need to do is enable some extensions which can be -- done via a LANGUAGE pragma at the top of your app: -- -- {-# LANGUAGE DeriveDataTypeable, TemplateHaskell #-} -- -- Then you will need some imports: -- --
--   module Main where
--   
--   import Happstack.Server   (ServerPartT, Response, simpleHTTP
--                            , nullConf, nullDir, ok, toResponse
--                            )
--   import Happstack.Server.ClientSession
--                             ( ClientSession(..), ClientSessionT(..)
--                            , getDefaultKey, mkSessionConf
--                            , liftSessionStateT, withClientSessionT
--                            )
--   import Data.Data          (Data, Typeable)
--   import Data.Lens          ((+=))
--   import Data.Lens.Template (makeLens)
--   import Data.SafeCopy      (base, deriveSafeCopy)
--   
-- -- Next you will want to create a type to hold your session data. Here we -- use a simple record which we will update using data-lens-fd. -- But, you could also store a, Map Text Text, or whatever suits -- your fancy as long as it can be serialized. (So no data types that -- include functions, existential types, etc). -- --
--   data SessionData = SessionData
--       { _count    :: Integer
--       }
--      deriving (Eq, Ord, Read, Show, Data, Typeable)
--   
--   -- | here we make it a lens, but that is not required
--   $(makeLens ''SessionData)
--   
-- -- We use the safecopy library to serialize the data so we can -- encrypt it and store it in a cookie. safecopy provides -- version migration, which means that we will be able to read-in old -- session data if we change the data type. The easiest way to create a -- SafeCopy instance is with deriveSafeCopy: -- --
--   $(deriveSafeCopy 0 'base ''SessionData)
--   
-- -- We also need to define what an emptySession looks like. This -- will be used for creating new sessions when the client does not -- already have one: -- --
--   instance ClientSession SessionData where
--       emptySession = SessionData { _count = 0 }
--   
-- -- Next we have a function which reads a client-specific page counter and -- returns the number of times the page has been reloaded. -- -- In this function we use, liftSessionStateT to lift the -- += lens function into ClientSessionT to increment and -- return the value stored in the client session. -- -- Alternatively, we could have used the getSession and -- putSession functions from MonadClientSession. Those -- functions do not require the use of liftSessionStateT. -- --
--   routes :: ClientSessionT SessionData (ServerPartT IO) Response
--   routes =
--       do nullDir
--          c <- liftSessionStateT $ count += 1
--          ok $ toResponse $ "you have viewed this page " ++ (show c) ++ " time(s)."
--   
-- -- Finally, we unwrap the ClientSessionT monad transformer using -- withClientSessionT. -- -- The SessionConf type requires an encryption key. You can -- generate the key using getDefaultKey uses a default filename. -- Alternatively, you can specific the name you want to use explicitly -- using getKey. The key will be created automatically if it does -- not already exist. -- -- If you change the key, all existing client sessions will be -- invalidated. -- --
--   main :: IO ()
--   main =
--       do key <- getDefaultKey
--          let sessionConf = mkSessionConf key
--          simpleHTTP nullConf $ withClientSessionT sessionConf $ routes
--   
-- -- In a real application you might want to use a newtype wrapper -- around ClientSessionT to keep your type signatures sane. An -- alternative version of this demo which does that can be found here: -- -- -- http://patch-tag.com/r/mae/happstack/snapshot/current/content/pretty/happstack-clientsession/demo/demo.hs module Happstack.Server.ClientSession -- | Your session type must have an instance for this class. class SafeCopy st => ClientSession st -- | An empty session, i.e. what you get when there is no existing session -- stored. emptySession :: ClientSession st => st -- | Wrapper around the sessionData which tracks it state so we can avoid -- decoding or encoding/sending the cookie when not required data SessionStatus sessionData Unread :: SessionStatus sessionData NoChange :: sessionData -> SessionStatus sessionData Modified :: sessionData -> SessionStatus sessionData Expired :: SessionStatus sessionData -- | MonadClientSession provides the primary interface to get -- sessionData, put sessionData or expire -- sessionData. -- -- This is a class so you can use newtype deriving to make the functions -- available in your custom server monad. class MonadClientSession sessionData m | m -> sessionData getSession :: MonadClientSession sessionData m => m sessionData putSession :: MonadClientSession sessionData m => sessionData -> m () expireSession :: MonadClientSession sessionData m => m () -- | Configuration for the session cookie for passing to -- runClientSessionT or withClientSessionT. data SessionConf SessionConf :: String -> CookieLife -> Key -> String -> String -> Bool -> Bool -> SessionConf -- | Name of the cookie to hold your session data. [sessionCookieName] :: SessionConf -> String -- | Lifetime of that cookie. [sessionCookieLife] :: SessionConf -> CookieLife -- | Encryption key, usually from one of getKey, -- getDefaultKey and randomKey. [sessionKey] :: SessionConf -> Key -- | cookie domain [sessionDomain] :: SessionConf -> String -- | cookie path [sessionPath] :: SessionConf -> String -- | Only use a session over secure transports. [sessionSecure] :: SessionConf -> Bool -- | Only use session over HTTP (to prevent it from being stolen via -- cross-site scripting) [sessionHttpOnly] :: SessionConf -> Bool -- | Create a SessionConf using defaults for everything except -- sessionKey. You can use record update syntax to override -- individual fields. -- --
--   main = do key <- getDefaultKey
--             let sessConf = (mkSessionConf key) { sessionCookieLife = oneWeek }
--             simpleHTTP nullConf $ withClientSessionT sessConf handlers
--     where
--       oneWeek  = MaxAge $ 60 * 60 * 24 * 7
--       handlers = msum [...]
--   
-- -- mkSessionConf is currently defined as: -- --
--   mkSessionConf :: Key -> SessionConf
--   mkSessionConf key = SessionConf
--      { sessionCookieName = "Happstack.ClientSession"
--      , sessionCookieLife = Session
--      , sessionKey        = key
--      , sessionDomain     = ""
--      , sessionPath       = "/"
--      , sessionSecure     = False
--      , sessionHttpOnly   = True
--      }
--   
-- -- see also: getKey, getDefaultKey mkSessionConf :: Key -> SessionConf -- | ClientSessionT provides an environment in which we can access -- and update the client-side session state -- -- The inner monad needs to provide an instance of Happstack so -- that the cookie value can be read and set. According -- ClientSessionT must appear outside ServerPartT not -- inside it. newtype ClientSessionT sessionData m a ClientSessionT :: ReaderT SessionConf (SessionStateT sessionData m) a -> ClientSessionT sessionData m a [unClientSessionT] :: ClientSessionT sessionData m a -> ReaderT SessionConf (SessionStateT sessionData m) a -- | transform the inner monad, but leave the session data alone. mapClientSessionT :: (forall s. m (a, s) -> n (b, s)) -> ClientSessionT sessionData m a -> ClientSessionT sessionData n b -- | run the ClientSessionT monad and get the result plus the final -- SessionStatus sessionData -- -- This function does not automatically update the cookie if the -- session has been modified. It is up to you to do that. You probably -- want to use withClientSessionT instead. -- -- see also: withClientSessionT, mkSessionConf runClientSessionT :: ClientSessionT sessionData m a -> SessionConf -> m (a, SessionStatus sessionData) -- | Wrapper around your handlers that use the session. -- -- This function automatically takes care of expiring or updating the -- cookie if the expireSession or modifySession is -- called. -- -- If no changes are made to the session, then the cookie will not be -- resent (because there is no need to). withClientSessionT :: (Happstack m, Functor m, Monad m, FilterMonad Response m, ClientSession sessionData) => SessionConf -> ClientSessionT sessionData m a -> m a -- | SessionStateT is like StateT, except it records if -- put was ever called data SessionStateT s m a -- | Transform the inner monad. (similar to mapStateT) -- -- The forall s. is to prevent you from modifying the session -- state. -- -- In theory we want this function to have the type: -- --
--   mapSessionStateT :: (m a -> n b) -> SessionStateT s m a -> SessionStateT s n b
--   
-- -- But that can not be done, so this is the next best thing. mapSessionStateT :: (forall s. m (a, s) -> n (b, s)) -> SessionStateT sessionData m a -> SessionStateT sessionData n b -- | lift a computation from the SessionStateT monad -- -- The primary purpose of this function is to make it possible to use the -- MonadState functions such as get and set to get -- and set the current session data. -- -- That makes it possible to use the MonadState based functions -- provided by Lens, e.g.: -- --
--   do c <- liftSessionStateT $ count += 1
--   
liftSessionStateT :: (Monad m, MonadTrans t, MonadClientSession sessionData (t m), Monad (t m)) => SessionStateT sessionData m a -> t m a -- | The keys used to store the cookies. We have an AES key used to encrypt -- the cookie and a Skein-MAC-512-256 key used verify the authencity and -- integrity of the cookie. The AES key must have exactly 32 bytes (256 -- bits) while Skein-MAC-512-256 must have 64 bytes (512 bits). -- -- See also getDefaultKey and initKey. data Key -- | Get a key from the given text file. -- -- If the file does not exist or is corrupted a random key will be -- generated and stored in that file. getKey :: FilePath -> IO Key -- | Simply calls getKey defaultKeyFile. getDefaultKey :: IO Key -- | Generate a random Key. Besides the Key, the -- ByteString passed to initKey is returned so that it -- can be saved for later use. randomKey :: IO (ByteString, Key) instance GHC.Show.Show sessionData => GHC.Show.Show (Happstack.Server.ClientSession.SessionStatus sessionData) instance GHC.Read.Read sessionData => GHC.Read.Read (Happstack.Server.ClientSession.SessionStatus sessionData) instance GHC.Classes.Ord sessionData => GHC.Classes.Ord (Happstack.Server.ClientSession.SessionStatus sessionData) instance GHC.Classes.Eq sessionData => GHC.Classes.Eq (Happstack.Server.ClientSession.SessionStatus sessionData) instance Happstack.Server.Internal.Monads.ServerMonad m => Happstack.Server.Internal.Monads.ServerMonad (Happstack.Server.ClientSession.SessionStateT s m) instance Happstack.Server.Internal.Monads.WebMonad r m => Happstack.Server.Internal.Monads.WebMonad r (Happstack.Server.ClientSession.SessionStateT s m) instance Happstack.Server.Internal.Monads.FilterMonad r m => Happstack.Server.Internal.Monads.FilterMonad r (Happstack.Server.ClientSession.SessionStateT s m) instance (GHC.Base.Monad m, Happstack.Server.RqData.HasRqData m) => Happstack.Server.RqData.HasRqData (Happstack.Server.ClientSession.SessionStateT s m) instance Control.Monad.Trans.Class.MonadTrans (Happstack.Server.ClientSession.SessionStateT s) instance Control.Monad.Cont.Class.MonadCont m => Control.Monad.Cont.Class.MonadCont (Happstack.Server.ClientSession.SessionStateT s m) instance Control.Monad.Error.Class.MonadError e m => Control.Monad.Error.Class.MonadError e (Happstack.Server.ClientSession.SessionStateT s m) instance Control.Monad.Fix.MonadFix m => Control.Monad.Fix.MonadFix (Happstack.Server.ClientSession.SessionStateT s m) instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Happstack.Server.ClientSession.SessionStateT s m) instance Control.Monad.Base.MonadBase b m => Control.Monad.Base.MonadBase b (Happstack.Server.ClientSession.SessionStateT s m) instance GHC.Base.MonadPlus m => GHC.Base.MonadPlus (Happstack.Server.ClientSession.SessionStateT s m) instance GHC.Base.Monad m => GHC.Base.Monad (Happstack.Server.ClientSession.SessionStateT s m) instance GHC.Base.MonadPlus m => GHC.Base.Alternative (Happstack.Server.ClientSession.SessionStateT s m) instance GHC.Base.Monad m => GHC.Base.Applicative (Happstack.Server.ClientSession.SessionStateT s m) instance GHC.Base.Functor m => GHC.Base.Functor (Happstack.Server.ClientSession.SessionStateT s m) instance Happstack.Server.Internal.Monads.ServerMonad m => Happstack.Server.Internal.Monads.ServerMonad (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Happstack.Server.Internal.Monads.WebMonad r m => Happstack.Server.Internal.Monads.WebMonad r (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Happstack.Server.Internal.Monads.FilterMonad r m => Happstack.Server.Internal.Monads.FilterMonad r (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance (GHC.Base.Monad m, Happstack.Server.RqData.HasRqData m) => Happstack.Server.RqData.HasRqData (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.Cont.Class.MonadCont m => Control.Monad.Cont.Class.MonadCont (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.Error.Class.MonadError e m => Control.Monad.Error.Class.MonadError e (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.Fix.MonadFix m => Control.Monad.Fix.MonadFix (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.MonadPlus m => GHC.Base.MonadPlus (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.Base.MonadBase b m => Control.Monad.Base.MonadBase b (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.Monad m => GHC.Base.Monad (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.MonadPlus m => GHC.Base.Alternative (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.Monad m => GHC.Base.Applicative (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.Functor m => GHC.Base.Functor (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance (GHC.Base.Functor m, GHC.Base.MonadPlus m, Happstack.Server.RqData.HasRqData m, Happstack.Server.ClientSession.ClientSession sessionData) => Happstack.Server.ClientSession.MonadClientSession sessionData (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.Cont.ContT c m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m, Control.Monad.Trans.Error.Error e) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.Error.ErrorT e m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.Reader.ReaderT r m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.State.Lazy.StateT s m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m, GHC.Base.Monoid w) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.Writer.Lazy.WriterT w m) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.MonadClientSession sessionData m, GHC.Base.Monoid w) => Happstack.Server.ClientSession.MonadClientSession sessionData (Control.Monad.Trans.RWS.Lazy.RWST r w s m) instance Happstack.Server.Monads.Happstack m => Happstack.Server.Monads.Happstack (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance GHC.Base.MonadPlus m => GHC.Base.Semigroup (Happstack.Server.ClientSession.ClientSessionT sessionData m a) instance GHC.Base.MonadPlus m => GHC.Base.Monoid (Happstack.Server.ClientSession.ClientSessionT sessionData m a) instance Control.Monad.Trans.Class.MonadTrans (Happstack.Server.ClientSession.ClientSessionT sessionData) instance Control.Monad.Trans.Control.MonadTransControl (Happstack.Server.ClientSession.ClientSessionT s) instance Control.Monad.Trans.Control.MonadBaseControl b m => Control.Monad.Trans.Control.MonadBaseControl b (Happstack.Server.ClientSession.ClientSessionT s m) instance Control.Monad.Reader.Class.MonadReader r m => Control.Monad.Reader.Class.MonadReader r (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.Writer.Class.MonadWriter w m => Control.Monad.Writer.Class.MonadWriter w (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.State.Class.MonadState s m => Control.Monad.State.Class.MonadState s (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Control.Monad.RWS.Class.MonadRWS r w s m => Control.Monad.RWS.Class.MonadRWS r w s (Happstack.Server.ClientSession.ClientSessionT sessionData m) instance Happstack.Server.Monads.Happstack m => Happstack.Server.Monads.Happstack (Happstack.Server.ClientSession.SessionStateT sessionData m) instance GHC.Base.MonadPlus m => GHC.Base.Semigroup (Happstack.Server.ClientSession.SessionStateT sessionData m a) instance GHC.Base.MonadPlus m => GHC.Base.Monoid (Happstack.Server.ClientSession.SessionStateT sessionData m a) instance (GHC.Base.Monad m, Happstack.Server.ClientSession.ClientSession sessionData) => Control.Monad.State.Class.MonadState sessionData (Happstack.Server.ClientSession.SessionStateT sessionData m) instance Control.Monad.Trans.Control.MonadTransControl (Happstack.Server.ClientSession.SessionStateT s) instance Control.Monad.Trans.Control.MonadBaseControl b m => Control.Monad.Trans.Control.MonadBaseControl b (Happstack.Server.ClientSession.SessionStateT s m) instance Data.SafeCopy.SafeCopy.SafeCopy Web.ClientSession.Key