Rewrite handlerToIO so that it works with Yesod 1.2.

Since the new YesodRequest has strict fields, handlerToIO didn't
work at all.  Even if it did, it had a reference to its parent's
ResourceT's internal state, so its chances of blowing up were
quite high.

The new implementation takes a whitelist approach of taking what
we want instead of clearing what we do not want.  Also, it takes
care of using a new runResourceT.
This commit is contained in:
Felipe Lessa 2013-05-03 21:20:31 -03:00
parent c19501b1d8
commit 5c434b089a
2 changed files with 30 additions and 25 deletions

View File

@ -143,6 +143,7 @@ import Yesod.Core.Internal.Request (langKey, mkFileInfoFile,
mkFileInfoLBS, mkFileInfoSource) mkFileInfoLBS, mkFileInfoSource)
import Control.Applicative ((<$>), (<|>)) import Control.Applicative ((<$>), (<|>))
import Control.Exception (evaluate)
import Control.Monad (liftM) import Control.Monad (liftM)
import qualified Control.Monad.Trans.Writer as Writer import qualified Control.Monad.Trans.Writer as Writer
@ -179,7 +180,7 @@ import Yesod.Core.Content (ToTypedContent (..), simpleConte
import Yesod.Core.Internal.Util (formatRFC1123) import Yesod.Core.Internal.Util (formatRFC1123)
import Text.Blaze.Html (preEscapedToMarkup, toHtml) import Text.Blaze.Html (preEscapedToMarkup, toHtml)
import Control.Monad.Trans.Resource (ResourceT) import Control.Monad.Trans.Resource (ResourceT, runResourceT, withInternalState)
import Data.Dynamic (fromDynamic, toDyn) import Data.Dynamic (fromDynamic, toDyn)
import qualified Data.IORef.Lifted as I import qualified Data.IORef.Lifted as I
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
@ -318,20 +319,15 @@ getCurrentRoute = rheRoute `liftM` askHandlerEnv
handlerToIO :: (MonadIO m1, MonadIO m2) => HandlerT site m1 (HandlerT site IO a -> m2 a) handlerToIO :: (MonadIO m1, MonadIO m2) => HandlerT site m1 (HandlerT site IO a -> m2 a)
handlerToIO = handlerToIO =
HandlerT $ \oldHandlerData -> do HandlerT $ \oldHandlerData -> do
-- Let go of the request body, cache and response headers. -- Take just the bits we need from oldHandlerData.
let oldReq = handlerRequest oldHandlerData let newReq = oldReq { reqWaiRequest = newWaiReq }
oldWaiReq = reqWaiRequest oldReq where
newWaiReq = oldWaiReq { W.requestBody = mempty oldReq = handlerRequest oldHandlerData
, W.requestBodyLength = W.KnownLength 0 oldWaiReq = reqWaiRequest oldReq
} newWaiReq = oldWaiReq { W.requestBody = mempty
newReq = oldReq { reqWaiRequest = newWaiReq } , W.requestBodyLength = W.KnownLength 0
clearedOldHandlerData = }
oldHandlerData { handlerRequest = err "handlerRequest never here" oldEnv = handlerEnv oldHandlerData
, handlerState = err "handlerState never here"
, handlerToParent = const () }
where
err :: String -> a
err = error . ("handlerToIO: clearedOldHandlerData/" ++)
newState <- liftIO $ do newState <- liftIO $ do
oldState <- I.readIORef (handlerState oldHandlerData) oldState <- I.readIORef (handlerState oldHandlerData)
return $ oldState { ghsRBC = Nothing return $ oldState { ghsRBC = Nothing
@ -339,16 +335,25 @@ handlerToIO =
, ghsCache = mempty , ghsCache = mempty
, ghsHeaders = mempty } , ghsHeaders = mempty }
-- xx From this point onwards, no references to oldHandlerData xx
liftIO $ evaluate (newReq `seq` oldEnv `seq` newState `seq` ())
-- Return GHandler running function. -- Return GHandler running function.
return $ \(HandlerT f) -> liftIO $ do return $ \(HandlerT f) ->
-- The state IORef needs to be created here, otherwise it liftIO $
-- will be shared by different invocations of this function. runResourceT $ withInternalState $ \resState -> do
newStateIORef <- I.newIORef newState -- The state IORef needs to be created here, otherwise it
-- FIXME previously runResourceT was used here, but that could mean resources might vanish... -- will be shared by different invocations of this function.
-- Check if this new behavior is correct. newStateIORef <- liftIO (I.newIORef newState)
f clearedOldHandlerData let newHandlerData =
{ handlerRequest = newReq HandlerData
, handlerState = newStateIORef } { handlerRequest = newReq
, handlerEnv = oldEnv
, handlerState = newStateIORef
, handlerToParent = const ()
, handlerResource = resState
}
liftIO (f newHandlerData)
-- | Redirect to the given route. -- | Redirect to the given route.

View File

@ -1,5 +1,5 @@
name: yesod-core name: yesod-core
version: 1.2.0.3 version: 1.2.0.4
license: MIT license: MIT
license-file: LICENSE license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com> author: Michael Snoyman <michael@snoyman.com>