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 }
where
oldReq = handlerRequest oldHandlerData
oldWaiReq = reqWaiRequest oldReq oldWaiReq = reqWaiRequest oldReq
newWaiReq = oldWaiReq { W.requestBody = mempty newWaiReq = oldWaiReq { W.requestBody = mempty
, W.requestBodyLength = W.KnownLength 0 , W.requestBodyLength = W.KnownLength 0
} }
newReq = oldReq { reqWaiRequest = newWaiReq } oldEnv = handlerEnv oldHandlerData
clearedOldHandlerData =
oldHandlerData { handlerRequest = err "handlerRequest never here"
, 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) ->
liftIO $
runResourceT $ withInternalState $ \resState -> do
-- The state IORef needs to be created here, otherwise it -- The state IORef needs to be created here, otherwise it
-- will be shared by different invocations of this function. -- will be shared by different invocations of this function.
newStateIORef <- I.newIORef newState newStateIORef <- liftIO (I.newIORef newState)
-- FIXME previously runResourceT was used here, but that could mean resources might vanish... let newHandlerData =
-- Check if this new behavior is correct. HandlerData
f clearedOldHandlerData
{ handlerRequest = newReq { handlerRequest = newReq
, handlerState = newStateIORef } , 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>