Merge pull request #1083 from DaveCTurner/issue-1083

Thread leak when calling neverExpires
This commit is contained in:
Greg Weber 2015-10-11 08:39:43 -07:00
commit 15d7bd458b
5 changed files with 30 additions and 17 deletions

View File

@ -59,6 +59,8 @@ import Yesod.Core.Class.Dispatch
import Yesod.Core.Internal.Run import Yesod.Core.Internal.Run
import Safe (readMay) import Safe (readMay)
import System.Environment (getEnvironment) import System.Environment (getEnvironment)
import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction, updateFreq)
import Yesod.Core.Internal.Util (getCurrentMaxExpiresRFC1123)
import Network.Wai.Middleware.Autohead import Network.Wai.Middleware.Autohead
import Network.Wai.Middleware.AcceptOverride import Network.Wai.Middleware.AcceptOverride
@ -82,11 +84,13 @@ toWaiAppPlain site = do
logger <- makeLogger site logger <- makeLogger site
sb <- makeSessionBackend site sb <- makeSessionBackend site
gen <- MWC.createSystemRandom gen <- MWC.createSystemRandom
getMaxExpires <- getGetMaxExpires
return $ toWaiAppYre $ YesodRunnerEnv return $ toWaiAppYre $ YesodRunnerEnv
{ yreLogger = logger { yreLogger = logger
, yreSite = site , yreSite = site
, yreSessionBackend = sb , yreSessionBackend = sb
, yreGen = gen , yreGen = gen
, yreGetMaxExpires = getMaxExpires
} }
toWaiAppYre :: YesodDispatch site => YesodRunnerEnv site -> W.Application toWaiAppYre :: YesodDispatch site => YesodRunnerEnv site -> W.Application
@ -139,11 +143,13 @@ toWaiAppLogger :: YesodDispatch site => Logger -> site -> IO W.Application
toWaiAppLogger logger site = do toWaiAppLogger logger site = do
sb <- makeSessionBackend site sb <- makeSessionBackend site
gen <- MWC.createSystemRandom gen <- MWC.createSystemRandom
getMaxExpires <- getGetMaxExpires
let yre = YesodRunnerEnv let yre = YesodRunnerEnv
{ yreLogger = logger { yreLogger = logger
, yreSite = site , yreSite = site
, yreSessionBackend = sb , yreSessionBackend = sb
, yreGen = gen , yreGen = gen
, yreGetMaxExpires = getMaxExpires
} }
messageLoggerSource messageLoggerSource
site site
@ -230,3 +236,9 @@ warpEnv site = do
case readMay portS of case readMay portS of
Nothing -> error $ "warpEnv: invalid PORT environment variable: " ++ show portS Nothing -> error $ "warpEnv: invalid PORT environment variable: " ++ show portS
Just port -> warp port site Just port -> warp port site
getGetMaxExpires :: IO (IO Text)
getGetMaxExpires = mkAutoUpdate defaultUpdateSettings
{ updateAction = getCurrentMaxExpiresRFC1123
, updateFreq = 24 * 60 * 60 * 1000000 -- Update once per day
}

View File

@ -733,7 +733,7 @@ cacheSeconds i = setHeader "Cache-Control" $ T.concat
-- is never (realistically) expired. -- is never (realistically) expired.
neverExpires :: MonadHandler m => m () neverExpires :: MonadHandler m => m ()
neverExpires = do neverExpires = do
askHandlerEnv >>= liftIO . rheGetMaxExpires >>= setHeader "Expires" setHeader "Expires" . rheMaxExpires =<< askHandlerEnv
cacheSeconds oneYear cacheSeconds oneYear
where where
oneYear :: Int oneYear :: Int

View File

@ -34,7 +34,6 @@ import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8) import Data.Text.Encoding (encodeUtf8)
import Data.Text.Encoding (decodeUtf8With) import Data.Text.Encoding (decodeUtf8With)
import Data.Text.Encoding.Error (lenientDecode) import Data.Text.Encoding.Error (lenientDecode)
import Data.Time (getCurrentTime, addUTCTime)
import Language.Haskell.TH.Syntax (Loc, qLocation) import Language.Haskell.TH.Syntax (Loc, qLocation)
import qualified Network.HTTP.Types as H import qualified Network.HTTP.Types as H
import Network.Wai import Network.Wai
@ -48,11 +47,9 @@ import Yesod.Core.Class.Yesod
import Yesod.Core.Types import Yesod.Core.Types
import Yesod.Core.Internal.Request (parseWaiRequest, import Yesod.Core.Internal.Request (parseWaiRequest,
tooLargeResponse) tooLargeResponse)
import Yesod.Core.Internal.Util (formatRFC1123) import Yesod.Core.Internal.Util (getCurrentMaxExpiresRFC1123)
import Yesod.Routes.Class (Route, renderRoute) import Yesod.Routes.Class (Route, renderRoute)
import Control.DeepSeq (($!!)) import Control.DeepSeq (($!!))
import Control.Monad (liftM)
import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction, updateFreq)
returnDeepSessionMap :: Monad m => SessionMap -> m SessionMap returnDeepSessionMap :: Monad m => SessionMap -> m SessionMap
#if MIN_VERSION_bytestring(0, 10, 0) #if MIN_VERSION_bytestring(0, 10, 0)
@ -199,7 +196,7 @@ runFakeHandler :: (Yesod site, MonadIO m) =>
-> m (Either ErrorResponse a) -> m (Either ErrorResponse a)
runFakeHandler fakeSessionMap logger site handler = liftIO $ do runFakeHandler fakeSessionMap logger site handler = liftIO $ do
ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result") ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result")
getMaxExpires <- getGetMaxExpires maxExpires <- getCurrentMaxExpiresRFC1123
let handler' = do liftIO . I.writeIORef ret . Right =<< handler let handler' = do liftIO . I.writeIORef ret . Right =<< handler
return () return ()
let yapp = runHandler let yapp = runHandler
@ -210,7 +207,7 @@ runFakeHandler fakeSessionMap logger site handler = liftIO $ do
, rheUpload = fileUpload site , rheUpload = fileUpload site
, rheLog = messageLoggerSource site $ logger site , rheLog = messageLoggerSource site $ logger site
, rheOnError = errHandler , rheOnError = errHandler
, rheGetMaxExpires = getMaxExpires , rheMaxExpires = maxExpires
} }
handler' handler'
errHandler err req = do errHandler err req = do
@ -261,7 +258,7 @@ yesodRunner handler' YesodRunnerEnv {..} route req sendResponse
let dontSaveSession _ = return [] let dontSaveSession _ = return []
(session, saveSession) <- liftIO $ do (session, saveSession) <- liftIO $ do
maybe (return (Map.empty, dontSaveSession)) (\sb -> sbLoadSession sb req) yreSessionBackend maybe (return (Map.empty, dontSaveSession)) (\sb -> sbLoadSession sb req) yreSessionBackend
getMaxExpires <- getGetMaxExpires maxExpires <- yreGetMaxExpires
let mkYesodReq = parseWaiRequest req session (isJust yreSessionBackend) mmaxLen let mkYesodReq = parseWaiRequest req session (isJust yreSessionBackend) mmaxLen
let yreq = let yreq =
case mkYesodReq of case mkYesodReq of
@ -280,7 +277,7 @@ yesodRunner handler' YesodRunnerEnv {..} route req sendResponse
, rheUpload = fileUpload yreSite , rheUpload = fileUpload yreSite
, rheLog = log' , rheLog = log'
, rheOnError = safeEh log' , rheOnError = safeEh log'
, rheGetMaxExpires = getMaxExpires , rheMaxExpires = maxExpires
} }
rhe = rheSafe rhe = rheSafe
{ rheOnError = runHandler rheSafe . errorHandler { rheOnError = runHandler rheSafe . errorHandler
@ -294,12 +291,6 @@ yesodRunner handler' YesodRunnerEnv {..} route req sendResponse
mmaxLen = maximumContentLength yreSite route mmaxLen = maximumContentLength yreSite route
handler = yesodMiddleware handler' handler = yesodMiddleware handler'
getGetMaxExpires :: MonadIO m => m (IO Text)
getGetMaxExpires = liftIO $ mkAutoUpdate defaultUpdateSettings
{ updateAction = liftM (formatRFC1123 . addUTCTime (60*60*24*365)) getCurrentTime
, updateFreq = 60 * 60 * 1000000 -- Update once per hour
}
yesodRender :: Yesod y yesodRender :: Yesod y
=> y => y
-> ResolvedApproot -> ResolvedApproot

View File

@ -5,13 +5,16 @@ module Yesod.Core.Internal.Util
, formatW3 , formatW3
, formatRFC1123 , formatRFC1123
, formatRFC822 , formatRFC822
, getCurrentMaxExpiresRFC1123
) where ) where
import Data.Int (Int64) import Data.Int (Int64)
import Data.Serialize (Get, Put, Serialize (..)) import Data.Serialize (Get, Put, Serialize (..))
import qualified Data.Text as T import qualified Data.Text as T
import Data.Time (Day (ModifiedJulianDay, toModifiedJulianDay), import Data.Time (Day (ModifiedJulianDay, toModifiedJulianDay),
DiffTime, UTCTime (..), formatTime) DiffTime, UTCTime (..), formatTime,
getCurrentTime, addUTCTime)
import Control.Monad (liftM)
#if MIN_VERSION_time(1,5,0) #if MIN_VERSION_time(1,5,0)
import Data.Time (defaultTimeLocale) import Data.Time (defaultTimeLocale)
@ -50,3 +53,9 @@ formatRFC1123 = T.pack . formatTime defaultTimeLocale "%a, %d %b %Y %X %Z"
-- | Format as per RFC 822. -- | Format as per RFC 822.
formatRFC822 :: UTCTime -> T.Text formatRFC822 :: UTCTime -> T.Text
formatRFC822 = T.pack . formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S %z" formatRFC822 = T.pack . formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S %z"
{- | Get the time 365 days from now in RFC 1123 format. For use as an expiry
date on a resource that never expires. See RFC 2616 section 14.21 for details.
-}
getCurrentMaxExpiresRFC1123 :: IO T.Text
getCurrentMaxExpiresRFC1123 = liftM (formatRFC1123 . addUTCTime (60*60*24*365)) getCurrentTime

View File

@ -183,10 +183,10 @@ data RunHandlerEnv site = RunHandlerEnv
, rheUpload :: !(RequestBodyLength -> FileUpload) , rheUpload :: !(RequestBodyLength -> FileUpload)
, rheLog :: !(Loc -> LogSource -> LogLevel -> LogStr -> IO ()) , rheLog :: !(Loc -> LogSource -> LogLevel -> LogStr -> IO ())
, rheOnError :: !(ErrorResponse -> YesodApp) , rheOnError :: !(ErrorResponse -> YesodApp)
, rheGetMaxExpires :: IO Text
-- ^ How to respond when an error is thrown internally. -- ^ How to respond when an error is thrown internally.
-- --
-- Since 1.2.0 -- Since 1.2.0
, rheMaxExpires :: !Text
} }
data HandlerData site parentRoute = HandlerData data HandlerData site parentRoute = HandlerData
@ -202,6 +202,7 @@ data YesodRunnerEnv site = YesodRunnerEnv
, yreSite :: !site , yreSite :: !site
, yreSessionBackend :: !(Maybe SessionBackend) , yreSessionBackend :: !(Maybe SessionBackend)
, yreGen :: !MWC.GenIO , yreGen :: !MWC.GenIO
, yreGetMaxExpires :: IO Text
} }
data YesodSubRunnerEnv sub parent parentMonad = YesodSubRunnerEnv data YesodSubRunnerEnv sub parent parentMonad = YesodSubRunnerEnv