Initial YesodRequest/YesodRespnse change
This commit is contained in:
parent
4f1a6b461e
commit
1bd193f642
@ -86,17 +86,37 @@ data ClientSessionDateCache =
|
|||||||
, csdcExpiresSerialized :: !ByteString
|
, csdcExpiresSerialized :: !ByteString
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
-- | The parsed request information.
|
-- | The parsed request information. This type augments the standard WAI
|
||||||
data Request = Request
|
-- 'W.Request' with additional information.
|
||||||
{ reqGetParams :: [(Text, Text)]
|
data YesodRequest = YesodRequest
|
||||||
, reqCookies :: [(Text, Text)]
|
{ reqGetParams :: ![(Text, Text)]
|
||||||
, reqWaiRequest :: W.Request
|
-- ^ Same as 'W.queryString', but decoded to @Text@.
|
||||||
-- | Languages which the client supports.
|
, reqCookies :: ![(Text, Text)]
|
||||||
, reqLangs :: [Text]
|
, reqWaiRequest :: !W.Request
|
||||||
-- | A random, session-specific token used to prevent CSRF attacks.
|
, reqLangs :: ![Text]
|
||||||
, reqToken :: Maybe Text
|
-- ^ Languages which the client supports. This is an ordered list by preference.
|
||||||
|
, reqToken :: !(Maybe Text)
|
||||||
|
-- ^ A random, session-specific token used to prevent CSRF attacks.
|
||||||
|
, reqSession :: !SessionMap
|
||||||
|
-- ^ Initial session sent from the client.
|
||||||
|
--
|
||||||
|
-- Since 1.2.0
|
||||||
|
, reqAccept :: ![ContentType]
|
||||||
|
-- ^ An ordered list of the accepted content types.
|
||||||
|
--
|
||||||
|
-- Since 1.2.0
|
||||||
|
, reqOnError :: !(ErrorResponse -> YesodApp)
|
||||||
|
-- ^ How to respond when an error is thrown internally.
|
||||||
|
--
|
||||||
|
-- Since 1.2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | An augmented WAI 'W.Response'. This can either be a standard @Response@,
|
||||||
|
-- or a higher-level data structure which Yesod will turn into a @Response@.
|
||||||
|
data YesodResponse
|
||||||
|
= YRWai W.Response
|
||||||
|
| YRPlain H.Status [Header] ContentType Content SessionMap
|
||||||
|
|
||||||
-- | A tuple containing both the POST parameters and submitted files.
|
-- | A tuple containing both the POST parameters and submitted files.
|
||||||
type RequestBodyContents =
|
type RequestBodyContents =
|
||||||
( [(Text, Text)]
|
( [(Text, Text)]
|
||||||
@ -150,7 +170,7 @@ type Texts = [Text]
|
|||||||
newtype WaiSubsite = WaiSubsite { runWaiSubsite :: W.Application }
|
newtype WaiSubsite = WaiSubsite { runWaiSubsite :: W.Application }
|
||||||
|
|
||||||
data HandlerData sub master = HandlerData
|
data HandlerData sub master = HandlerData
|
||||||
{ handlerRequest :: Request
|
{ handlerRequest :: YesodRequest
|
||||||
, handlerSub :: sub
|
, handlerSub :: sub
|
||||||
, handlerMaster :: master
|
, handlerMaster :: master
|
||||||
, handlerRoute :: Maybe (Route sub)
|
, handlerRoute :: Maybe (Route sub)
|
||||||
@ -178,18 +198,7 @@ data GHState = GHState
|
|||||||
-- | 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.
|
||||||
newtype YesodApp = YesodApp
|
type YesodApp = YesodRequest -> ResourceT IO YesodResponse
|
||||||
{ unYesodApp
|
|
||||||
:: (ErrorResponse -> YesodApp)
|
|
||||||
-> Request
|
|
||||||
-> [ContentType]
|
|
||||||
-> SessionMap
|
|
||||||
-> ResourceT IO YesodAppResult
|
|
||||||
}
|
|
||||||
|
|
||||||
data YesodAppResult
|
|
||||||
= YARWai W.Response
|
|
||||||
| YARPlain H.Status [Header] ContentType Content SessionMap
|
|
||||||
|
|
||||||
-- | A generic widget, allowing specification of both the subsite and master
|
-- | A generic widget, allowing specification of both the subsite and master
|
||||||
-- site datatypes. While this is simply a @WriterT@, we define a newtype for
|
-- site datatypes. While this is simply a @WriterT@, we define a newtype for
|
||||||
|
|||||||
@ -106,7 +106,7 @@ module Yesod.Handler
|
|||||||
, cacheDelete
|
, cacheDelete
|
||||||
-- * Internal Yesod
|
-- * Internal Yesod
|
||||||
, runHandler
|
, runHandler
|
||||||
, YesodApp (..)
|
, YesodApp
|
||||||
, runSubsiteGetter
|
, runSubsiteGetter
|
||||||
, toMasterHandler
|
, toMasterHandler
|
||||||
, toMasterHandlerDyn
|
, toMasterHandlerDyn
|
||||||
@ -114,7 +114,6 @@ module Yesod.Handler
|
|||||||
, localNoCurrent
|
, localNoCurrent
|
||||||
, HandlerData
|
, HandlerData
|
||||||
, ErrorResponse (..)
|
, ErrorResponse (..)
|
||||||
, YesodAppResult (..)
|
|
||||||
, handlerToYAR
|
, handlerToYAR
|
||||||
, yarToResponse
|
, yarToResponse
|
||||||
, headerToPair
|
, headerToPair
|
||||||
@ -146,10 +145,9 @@ import qualified Data.Text.Lazy as TL
|
|||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import Network.Wai.Parse (parseHttpAccept)
|
|
||||||
|
|
||||||
import Yesod.Content
|
import Yesod.Content
|
||||||
import Data.Maybe (fromMaybe, mapMaybe)
|
import Data.Maybe (mapMaybe)
|
||||||
import Web.Cookie (SetCookie (..), renderSetCookie)
|
import Web.Cookie (SetCookie (..), renderSetCookie)
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import qualified Network.Wai.Parse as NWP
|
import qualified Network.Wai.Parse as NWP
|
||||||
@ -255,7 +253,7 @@ toMasterHandlerMaybe :: (Route sub -> Route master)
|
|||||||
-> GHandler sub' master a
|
-> GHandler sub' master a
|
||||||
toMasterHandlerMaybe tm ts route = local (handlerSubDataMaybe tm ts route)
|
toMasterHandlerMaybe tm ts route = local (handlerSubDataMaybe tm ts route)
|
||||||
|
|
||||||
getRequest :: GHandler s m Request
|
getRequest :: GHandler s m YesodRequest
|
||||||
getRequest = handlerRequest `liftM` ask
|
getRequest = handlerRequest `liftM` ask
|
||||||
|
|
||||||
hcError :: ErrorResponse -> GHandler sub master a
|
hcError :: ErrorResponse -> GHandler sub master a
|
||||||
@ -415,8 +413,7 @@ runHandler :: HasReps c
|
|||||||
-> (W.RequestBodyLength -> FileUpload)
|
-> (W.RequestBodyLength -> FileUpload)
|
||||||
-> (Loc -> LogSource -> LogLevel -> LogStr -> IO ())
|
-> (Loc -> LogSource -> LogLevel -> LogStr -> IO ())
|
||||||
-> YesodApp
|
-> YesodApp
|
||||||
runHandler handler mrender sroute tomr master sub upload log' =
|
runHandler handler mrender sroute tomr master sub upload log' req = do
|
||||||
YesodApp $ \eh rr cts initSession -> do
|
|
||||||
let toErrorHandler e =
|
let toErrorHandler e =
|
||||||
case fromException e of
|
case fromException e of
|
||||||
Just (HCError x) -> x
|
Just (HCError x) -> x
|
||||||
@ -429,7 +426,7 @@ runHandler handler mrender sroute tomr master sub upload log' =
|
|||||||
, ghsHeaders = mempty
|
, ghsHeaders = mempty
|
||||||
}
|
}
|
||||||
let hd = HandlerData
|
let hd = HandlerData
|
||||||
{ handlerRequest = rr
|
{ handlerRequest = req
|
||||||
, handlerSub = sub
|
, handlerSub = sub
|
||||||
, handlerMaster = master
|
, handlerMaster = master
|
||||||
, handlerRoute = sroute
|
, handlerRoute = sroute
|
||||||
@ -447,21 +444,24 @@ runHandler handler mrender sroute tomr master sub upload log' =
|
|||||||
let headers = ghsHeaders state
|
let headers = ghsHeaders state
|
||||||
let contents = either id (HCContent H.status200 . chooseRep) contents'
|
let contents = either id (HCContent H.status200 . chooseRep) contents'
|
||||||
let handleError e = do
|
let handleError e = do
|
||||||
yar <- unYesodApp (eh e) safeEh rr cts finalSession
|
yar <- eh e req
|
||||||
|
{ reqOnError = safeEh
|
||||||
|
, reqSession = finalSession
|
||||||
|
}
|
||||||
case yar of
|
case yar of
|
||||||
YARPlain _ hs ct c sess ->
|
YRPlain _ hs ct c sess ->
|
||||||
let hs' = appEndo headers hs
|
let hs' = appEndo headers hs
|
||||||
in return $ YARPlain (getStatus e) hs' ct c sess
|
in return $ YRPlain (getStatus e) hs' ct c sess
|
||||||
YARWai _ -> return yar
|
YRWai _ -> return yar
|
||||||
let sendFile' ct fp p =
|
let sendFile' ct fp p =
|
||||||
return $ YARPlain H.status200 (appEndo headers []) ct (ContentFile fp p) finalSession
|
return $ YRPlain H.status200 (appEndo headers []) ct (ContentFile fp p) finalSession
|
||||||
case contents of
|
case contents of
|
||||||
HCContent status a -> do
|
HCContent status a -> do
|
||||||
(ct, c) <- liftIO $ a cts
|
(ct, c) <- liftIO $ a cts
|
||||||
ec' <- liftIO $ evaluateContent c
|
ec' <- liftIO $ evaluateContent c
|
||||||
case ec' of
|
case ec' of
|
||||||
Left e -> handleError e
|
Left e -> handleError e
|
||||||
Right c' -> return $ YARPlain status (appEndo headers []) ct c' finalSession
|
Right c' -> return $ YRPlain status (appEndo headers []) ct c' finalSession
|
||||||
HCError e -> handleError e
|
HCError e -> handleError e
|
||||||
HCRedirect status loc -> do
|
HCRedirect status loc -> do
|
||||||
let disable_caching x =
|
let disable_caching x =
|
||||||
@ -470,7 +470,7 @@ runHandler handler mrender sroute tomr master sub upload log' =
|
|||||||
: x
|
: x
|
||||||
hs = (if status /= H.movedPermanently301 then disable_caching else id)
|
hs = (if status /= H.movedPermanently301 then disable_caching else id)
|
||||||
$ Header "Location" (encodeUtf8 loc) : appEndo headers []
|
$ Header "Location" (encodeUtf8 loc) : appEndo headers []
|
||||||
return $ YARPlain
|
return $ YRPlain
|
||||||
status hs typePlain emptyContent
|
status hs typePlain emptyContent
|
||||||
finalSession
|
finalSession
|
||||||
HCSendFile ct fp p -> catch
|
HCSendFile ct fp p -> catch
|
||||||
@ -478,13 +478,17 @@ runHandler handler mrender sroute tomr master sub upload log' =
|
|||||||
(handleError . toErrorHandler)
|
(handleError . toErrorHandler)
|
||||||
HCCreated loc -> do
|
HCCreated loc -> do
|
||||||
let hs = Header "Location" (encodeUtf8 loc) : appEndo headers []
|
let hs = Header "Location" (encodeUtf8 loc) : appEndo headers []
|
||||||
return $ YARPlain
|
return $ YRPlain
|
||||||
H.status201
|
H.status201
|
||||||
hs
|
hs
|
||||||
typePlain
|
typePlain
|
||||||
emptyContent
|
emptyContent
|
||||||
finalSession
|
finalSession
|
||||||
HCWai r -> return $ YARWai r
|
HCWai r -> return $ YRWai r
|
||||||
|
where
|
||||||
|
eh = reqOnError req
|
||||||
|
cts = reqAccept req
|
||||||
|
initSession = reqSession req
|
||||||
|
|
||||||
evaluateContent :: Content -> IO (Either ErrorResponse Content)
|
evaluateContent :: Content -> IO (Either ErrorResponse Content)
|
||||||
evaluateContent (ContentBuilder b mlen) = Control.Exception.handle f $ do
|
evaluateContent (ContentBuilder b mlen) = Control.Exception.handle f $ do
|
||||||
@ -496,14 +500,14 @@ evaluateContent (ContentBuilder b mlen) = Control.Exception.handle f $ do
|
|||||||
evaluateContent c = return (Right c)
|
evaluateContent c = return (Right c)
|
||||||
|
|
||||||
safeEh :: ErrorResponse -> YesodApp
|
safeEh :: ErrorResponse -> YesodApp
|
||||||
safeEh er = YesodApp $ \_ _ _ session -> do
|
safeEh er req = do
|
||||||
liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er
|
liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er
|
||||||
return $ YARPlain
|
return $ YRPlain
|
||||||
H.status500
|
H.status500
|
||||||
[]
|
[]
|
||||||
typePlain
|
typePlain
|
||||||
(toContent ("Internal Server Error" :: S.ByteString))
|
(toContent ("Internal Server Error" :: S.ByteString))
|
||||||
session
|
(reqSession req)
|
||||||
|
|
||||||
-- | Redirect to the given route.
|
-- | Redirect to the given route.
|
||||||
-- HTTP status code 303 for HTTP 1.1 clients and 302 for HTTP 1.0
|
-- HTTP status code 303 for HTTP 1.1 clients and 302 for HTTP 1.0
|
||||||
@ -806,6 +810,9 @@ instance (key ~ Text, val ~ Text) => RedirectUrl master (Route master, [(key, va
|
|||||||
r <- getUrlRenderParams
|
r <- getUrlRenderParams
|
||||||
return $ r url params
|
return $ r url params
|
||||||
|
|
||||||
|
instance (key ~ Text, val ~ Text) => RedirectUrl master (Route master, Map.Map key val) where
|
||||||
|
toTextUrl (url, params) = toTextUrl (url, Map.toList params)
|
||||||
|
|
||||||
localNoCurrent :: GHandler s m a -> GHandler s m a
|
localNoCurrent :: GHandler s m a -> GHandler s m a
|
||||||
localNoCurrent =
|
localNoCurrent =
|
||||||
local (\hd -> hd { handlerRoute = Nothing })
|
local (\hd -> hd { handlerRoute = Nothing })
|
||||||
@ -832,22 +839,21 @@ handlerToYAR :: (HasReps a, HasReps b)
|
|||||||
-> (Route sub -> Route master)
|
-> (Route sub -> Route master)
|
||||||
-> (Route master -> [(Text, Text)] -> Text) -- route renderer
|
-> (Route master -> [(Text, Text)] -> Text) -- route renderer
|
||||||
-> (ErrorResponse -> GHandler sub master a)
|
-> (ErrorResponse -> GHandler sub master a)
|
||||||
-> Request
|
-> YesodRequest
|
||||||
-> Maybe (Route sub)
|
-> Maybe (Route sub)
|
||||||
-> SessionMap
|
-> SessionMap
|
||||||
-> GHandler sub master b
|
-> GHandler sub master b
|
||||||
-> ResourceT IO YesodAppResult
|
-> ResourceT IO YesodResponse
|
||||||
handlerToYAR y s upload log' toMasterRoute render errorHandler rr murl sessionMap h =
|
handlerToYAR y s upload log' toMasterRoute render errorHandler rr murl sessionMap h =
|
||||||
unYesodApp ya eh' rr types sessionMap
|
ya rr { reqOnError = eh', reqSession = sessionMap }
|
||||||
where
|
where
|
||||||
ya = runHandler h render murl toMasterRoute y s upload log'
|
ya = runHandler h render murl toMasterRoute y s upload log'
|
||||||
eh' er = runHandler (errorHandler' er) render murl toMasterRoute y s upload log'
|
eh' er = runHandler (errorHandler' er) render murl toMasterRoute y s upload log'
|
||||||
types = httpAccept $ reqWaiRequest rr
|
|
||||||
errorHandler' = localNoCurrent . errorHandler
|
errorHandler' = localNoCurrent . errorHandler
|
||||||
|
|
||||||
yarToResponse :: YesodAppResult -> [(CI ByteString, ByteString)] -> W.Response
|
yarToResponse :: YesodResponse -> [(CI ByteString, ByteString)] -> W.Response
|
||||||
yarToResponse (YARWai a) _ = a
|
yarToResponse (YRWai a) _ = a
|
||||||
yarToResponse (YARPlain s hs _ c _) extraHeaders =
|
yarToResponse (YRPlain s hs _ c _) extraHeaders =
|
||||||
go c
|
go c
|
||||||
where
|
where
|
||||||
finalHeaders = extraHeaders ++ map headerToPair hs
|
finalHeaders = extraHeaders ++ map headerToPair hs
|
||||||
@ -862,12 +868,6 @@ yarToResponse (YARPlain s hs _ c _) extraHeaders =
|
|||||||
go (ContentSource body) = W.ResponseSource s finalHeaders body
|
go (ContentSource body) = W.ResponseSource s finalHeaders body
|
||||||
go (ContentDontEvaluate c') = go c'
|
go (ContentDontEvaluate c') = go c'
|
||||||
|
|
||||||
httpAccept :: W.Request -> [ContentType]
|
|
||||||
httpAccept = parseHttpAccept
|
|
||||||
. fromMaybe mempty
|
|
||||||
. lookup "Accept"
|
|
||||||
. W.requestHeaders
|
|
||||||
|
|
||||||
-- | Convert Header to a key/value pair.
|
-- | Convert Header to a key/value pair.
|
||||||
headerToPair :: Header
|
headerToPair :: Header
|
||||||
-> (CI ByteString, ByteString)
|
-> (CI ByteString, ByteString)
|
||||||
|
|||||||
@ -44,6 +44,8 @@ module Yesod.Internal.Core
|
|||||||
import Yesod.Content
|
import Yesod.Content
|
||||||
import Yesod.Handler hiding (lift, getExpires)
|
import Yesod.Handler hiding (lift, getExpires)
|
||||||
import Control.Monad.Logger (logErrorS)
|
import Control.Monad.Logger (logErrorS)
|
||||||
|
import Control.Applicative ((<$>))
|
||||||
|
import System.Random (newStdGen)
|
||||||
|
|
||||||
import Yesod.Routes.Class
|
import Yesod.Routes.Class
|
||||||
|
|
||||||
@ -422,9 +424,10 @@ defaultYesodRunner logger handler' master sub murl toMasterRoute msb req
|
|||||||
| W.KnownLength len <- W.requestBodyLength req, maxLen < len = return tooLargeResponse
|
| W.KnownLength len <- W.requestBodyLength req, maxLen < len = return tooLargeResponse
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
let dontSaveSession _ = return []
|
let dontSaveSession _ = return []
|
||||||
|
let onError _ = error "FIXME: Yesod.Internal.Core.defaultYesodRunner.onError"
|
||||||
(session, saveSession) <- liftIO $ do
|
(session, saveSession) <- liftIO $ do
|
||||||
maybe (return (Map.empty, 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 onError (isJust msb) maxLen <$> newStdGen
|
||||||
let h = {-# SCC "h" #-} do
|
let h = {-# SCC "h" #-} do
|
||||||
case murl of
|
case murl of
|
||||||
Nothing -> handler
|
Nothing -> handler
|
||||||
@ -448,7 +451,7 @@ defaultYesodRunner logger handler' master sub murl toMasterRoute msb req
|
|||||||
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
|
(YRPlain _ _ ct _ newSess) -> do
|
||||||
let nsToken = maybe
|
let nsToken = maybe
|
||||||
newSess
|
newSess
|
||||||
(\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess)
|
(\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess)
|
||||||
@ -800,7 +803,7 @@ runFakeHandler fakeSessionMap logger master handler = liftIO $ do
|
|||||||
ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result")
|
ret <- I.newIORef (Left $ InternalError "runFakeHandler: no result")
|
||||||
let handler' = do liftIO . I.writeIORef ret . Right =<< handler
|
let handler' = do liftIO . I.writeIORef ret . Right =<< handler
|
||||||
return ()
|
return ()
|
||||||
let YesodApp yapp =
|
let yapp =
|
||||||
runHandler
|
runHandler
|
||||||
handler'
|
handler'
|
||||||
(yesodRender master $ resolveApproot master fakeWaiRequest)
|
(yesodRender master $ resolveApproot master fakeWaiRequest)
|
||||||
@ -810,15 +813,14 @@ runFakeHandler fakeSessionMap logger master handler = liftIO $ do
|
|||||||
master
|
master
|
||||||
(fileUpload master)
|
(fileUpload master)
|
||||||
(messageLoggerSource master $ logger master)
|
(messageLoggerSource master $ logger master)
|
||||||
errHandler err =
|
errHandler err req = do
|
||||||
YesodApp $ \_ _ _ session -> do
|
|
||||||
liftIO $ I.writeIORef ret (Left err)
|
liftIO $ I.writeIORef ret (Left err)
|
||||||
return $ YARPlain
|
return $ YRPlain
|
||||||
H.status500
|
H.status500
|
||||||
[]
|
[]
|
||||||
typePlain
|
typePlain
|
||||||
(toContent ("runFakeHandler: errHandler" :: S8.ByteString))
|
(toContent ("runFakeHandler: errHandler" :: S8.ByteString))
|
||||||
session
|
(reqSession req)
|
||||||
fakeWaiRequest =
|
fakeWaiRequest =
|
||||||
W.Request
|
W.Request
|
||||||
{ W.requestMethod = "POST"
|
{ W.requestMethod = "POST"
|
||||||
@ -839,15 +841,17 @@ runFakeHandler fakeSessionMap logger master handler = liftIO $ do
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
fakeRequest =
|
fakeRequest =
|
||||||
Request
|
YesodRequest
|
||||||
{ reqGetParams = []
|
{ reqGetParams = []
|
||||||
, reqCookies = []
|
, reqCookies = []
|
||||||
, reqWaiRequest = fakeWaiRequest
|
, reqWaiRequest = fakeWaiRequest
|
||||||
, reqLangs = []
|
, reqLangs = []
|
||||||
, reqToken = Just "NaN" -- not a nonce =)
|
, reqToken = Just "NaN" -- not a nonce =)
|
||||||
|
, reqOnError = errHandler
|
||||||
|
, reqAccept = []
|
||||||
|
, reqSession = fakeSessionMap
|
||||||
}
|
}
|
||||||
fakeContentType = []
|
_ <- runResourceT $ yapp fakeRequest
|
||||||
_ <- runResourceT $ yapp errHandler fakeRequest fakeContentType fakeSessionMap
|
|
||||||
I.readIORef ret
|
I.readIORef ret
|
||||||
{-# WARNING runFakeHandler "Usually you should *not* use runFakeHandler unless you really understand how it works and why you need it." #-}
|
{-# WARNING runFakeHandler "Usually you should *not* use runFakeHandler unless you really understand how it works and why you need it." #-}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
module Yesod.Internal.Request
|
module Yesod.Internal.Request
|
||||||
( parseWaiRequest
|
( parseWaiRequest
|
||||||
, Request (..)
|
|
||||||
, RequestBodyContents
|
, RequestBodyContents
|
||||||
, FileInfo
|
, FileInfo
|
||||||
, fileName
|
, fileName
|
||||||
@ -16,21 +15,18 @@ module Yesod.Internal.Request
|
|||||||
, tooLargeResponse
|
, tooLargeResponse
|
||||||
-- The below are exported for testing.
|
-- The below are exported for testing.
|
||||||
, randomString
|
, randomString
|
||||||
, parseWaiRequest'
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative ((<$>))
|
|
||||||
import Control.Arrow (second)
|
import Control.Arrow (second)
|
||||||
import qualified Network.Wai.Parse as NWP
|
import qualified Network.Wai.Parse as NWP
|
||||||
import Yesod.Internal
|
import Yesod.Internal
|
||||||
import qualified Network.Wai as W
|
import qualified Network.Wai as W
|
||||||
import System.Random (RandomGen, newStdGen, randomRs)
|
import System.Random (RandomGen, randomRs)
|
||||||
import Web.Cookie (parseCookiesText)
|
import Web.Cookie (parseCookiesText)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import qualified Data.ByteString.Char8 as S8
|
import qualified Data.ByteString.Char8 as S8
|
||||||
import Data.Text (Text, pack)
|
import Data.Text (Text, pack)
|
||||||
import Network.HTTP.Types (queryToQueryText, Status (Status))
|
import Network.HTTP.Types (queryToQueryText, Status (Status))
|
||||||
import Control.Monad (join)
|
|
||||||
import Data.Maybe (fromMaybe, catMaybes)
|
import Data.Maybe (fromMaybe, catMaybes)
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
@ -46,14 +42,6 @@ import Control.Exception (throwIO)
|
|||||||
import Yesod.Core.Types
|
import Yesod.Core.Types
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
parseWaiRequest :: W.Request
|
|
||||||
-> SessionMap
|
|
||||||
-> Bool
|
|
||||||
-> Word64 -- ^ maximum allowed body size
|
|
||||||
-> IO Request
|
|
||||||
parseWaiRequest env session' useToken maxBodySize =
|
|
||||||
parseWaiRequest' env session' useToken maxBodySize <$> newStdGen
|
|
||||||
|
|
||||||
-- | Impose a limit on the size of the request body.
|
-- | Impose a limit on the size of the request body.
|
||||||
limitRequestBody :: Word64 -> W.Request -> W.Request
|
limitRequestBody :: Word64 -> W.Request -> W.Request
|
||||||
limitRequestBody maxLen req =
|
limitRequestBody maxLen req =
|
||||||
@ -79,29 +67,40 @@ tooLargeResponse = W.responseLBS
|
|||||||
[("Content-Type", "text/plain")]
|
[("Content-Type", "text/plain")]
|
||||||
"Request body too large to be processed."
|
"Request body too large to be processed."
|
||||||
|
|
||||||
parseWaiRequest' :: RandomGen g
|
parseWaiRequest :: RandomGen g
|
||||||
=> W.Request
|
=> W.Request
|
||||||
-> SessionMap
|
-> SessionMap
|
||||||
-> Bool
|
-> (ErrorResponse -> YesodApp)
|
||||||
-> Word64 -- ^ max body size
|
-> Bool
|
||||||
-> g
|
-> Word64 -- ^ max body size
|
||||||
-> Request
|
-> g
|
||||||
parseWaiRequest' env session' useToken maxBodySize gen =
|
-> YesodRequest
|
||||||
Request gets'' cookies' (limitRequestBody maxBodySize env) langs'' token
|
parseWaiRequest env session onError useToken maxBodySize gen =
|
||||||
|
YesodRequest
|
||||||
|
{ reqGetParams = gets
|
||||||
|
, reqCookies = cookies
|
||||||
|
, reqWaiRequest = limitRequestBody maxBodySize env
|
||||||
|
, reqLangs = langs''
|
||||||
|
, reqToken = token
|
||||||
|
, reqSession = session
|
||||||
|
, reqAccept = httpAccept env
|
||||||
|
, reqOnError = onError
|
||||||
|
}
|
||||||
where
|
where
|
||||||
gets' = queryToQueryText $ W.queryString env
|
gets = map (second $ fromMaybe "")
|
||||||
gets'' = map (second $ fromMaybe "") gets'
|
$ queryToQueryText
|
||||||
|
$ W.queryString env
|
||||||
reqCookie = lookup "Cookie" $ W.requestHeaders env
|
reqCookie = lookup "Cookie" $ W.requestHeaders env
|
||||||
cookies' = maybe [] parseCookiesText reqCookie
|
cookies = maybe [] parseCookiesText reqCookie
|
||||||
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) . Map.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 [ lookup langKey gets -- Query _LANG
|
||||||
, lookup langKey cookies' -- Cookie _LANG
|
, lookup langKey cookies -- Cookie _LANG
|
||||||
, lookupText langKey session' -- Session _LANG
|
, lookupText langKey session -- Session _LANG
|
||||||
] ++ langs -- Accept-Language(s)
|
] ++ langs -- Accept-Language(s)
|
||||||
|
|
||||||
-- Github issue #195. We want to add an extra two-letter version of any
|
-- Github issue #195. We want to add an extra two-letter version of any
|
||||||
@ -117,7 +116,17 @@ parseWaiRequest' env session' useToken maxBodySize gen =
|
|||||||
else Just $ maybe
|
else Just $ maybe
|
||||||
(pack $ randomString 10 gen)
|
(pack $ randomString 10 gen)
|
||||||
(decodeUtf8With lenientDecode)
|
(decodeUtf8With lenientDecode)
|
||||||
(Map.lookup tokenKey session')
|
(Map.lookup tokenKey session)
|
||||||
|
|
||||||
|
-- | Get the list of accepted content types from the WAI Request\'s Accept
|
||||||
|
-- header.
|
||||||
|
--
|
||||||
|
-- Since 1.2.0
|
||||||
|
httpAccept :: W.Request -> [ContentType]
|
||||||
|
httpAccept = NWP.parseHttpAccept
|
||||||
|
. fromMaybe S8.empty
|
||||||
|
. lookup "Accept"
|
||||||
|
. W.requestHeaders
|
||||||
|
|
||||||
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
||||||
addTwoLetters (toAdd, exist) [] =
|
addTwoLetters (toAdd, exist) [] =
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
-- imported by library users.
|
-- imported by library users.
|
||||||
--
|
--
|
||||||
module Yesod.Internal.TestApi
|
module Yesod.Internal.TestApi
|
||||||
( randomString, parseWaiRequest'
|
( randomString, parseWaiRequest
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Yesod.Internal.Request (randomString, parseWaiRequest')
|
import Yesod.Internal.Request (randomString, parseWaiRequest)
|
||||||
|
|||||||
@ -15,7 +15,7 @@ module Yesod.Request
|
|||||||
(
|
(
|
||||||
-- * Request datatype
|
-- * Request datatype
|
||||||
RequestBodyContents
|
RequestBodyContents
|
||||||
, Request (..)
|
, YesodRequest (..)
|
||||||
, FileInfo
|
, FileInfo
|
||||||
, fileName
|
, fileName
|
||||||
, fileContentType
|
, fileContentType
|
||||||
@ -41,6 +41,7 @@ import Control.Monad (liftM)
|
|||||||
import Control.Monad.Instances () -- I'm missing the instance Monad ((->) r
|
import Control.Monad.Instances () -- I'm missing the instance Monad ((->) r
|
||||||
import Data.Maybe (listToMaybe)
|
import Data.Maybe (listToMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Yesod.Core.Types
|
||||||
|
|
||||||
-- | Get the list of supported languages supplied by the user.
|
-- | Get the list of supported languages supplied by the user.
|
||||||
--
|
--
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module YesodCoreTest.CleanPath (cleanPathTest, Widget) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module YesodCoreTest.Exceptions (exceptionsTest, Widget) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
import Network.HTTP.Types (status301)
|
import Network.HTTP.Types (status301)
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import System.Random (StdGen, mkStdGen)
|
|||||||
|
|
||||||
import Network.Wai as W
|
import Network.Wai as W
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
import Yesod.Internal.TestApi (randomString, parseWaiRequest')
|
import Yesod.Internal.TestApi (randomString, parseWaiRequest)
|
||||||
import Yesod.Request (Request (..))
|
import Yesod.Request (YesodRequest (..))
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Data.Monoid (mempty)
|
import Data.Monoid (mempty)
|
||||||
import Data.Map (singleton)
|
import Data.Map (singleton)
|
||||||
@ -40,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 mempty False 1000 g
|
r = parseWaiRequest defaultRequest mempty onError False 1000 g
|
||||||
|
|
||||||
ignoreDisabledToken :: Bool
|
ignoreDisabledToken :: Bool
|
||||||
ignoreDisabledToken = reqToken r == Nothing where
|
ignoreDisabledToken = reqToken r == Nothing where
|
||||||
r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") False 1000 g
|
r = parseWaiRequest defaultRequest (singleton "_TOKEN" "old") onError False 1000 g
|
||||||
|
|
||||||
useOldToken :: Bool
|
useOldToken :: Bool
|
||||||
useOldToken = reqToken r == Just "old" where
|
useOldToken = reqToken r == Just "old" where
|
||||||
r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") True 1000 g
|
r = parseWaiRequest defaultRequest (singleton "_TOKEN" "old") onError True 1000 g
|
||||||
|
|
||||||
generateToken :: Bool
|
generateToken :: Bool
|
||||||
generateToken = reqToken r /= Nothing where
|
generateToken = reqToken r /= Nothing where
|
||||||
r = parseWaiRequest' defaultRequest (singleton "_TOKEN" "old") True 1000 g
|
r = parseWaiRequest defaultRequest (singleton "_TOKEN" "old") onError True 1000 g
|
||||||
|
|
||||||
|
|
||||||
langSpecs :: Spec
|
langSpecs :: Spec
|
||||||
@ -65,32 +65,34 @@ 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")] } mempty False 1000 g
|
{ requestHeaders = [("Accept-Language", "en-US, es")] } mempty onError False 1000 g
|
||||||
|
|
||||||
respectSessionLang :: Bool
|
respectSessionLang :: Bool
|
||||||
respectSessionLang = reqLangs r == ["en"] where
|
respectSessionLang = reqLangs r == ["en"] where
|
||||||
r = parseWaiRequest' defaultRequest (singleton "_LANG" "en") False 1000 g
|
r = parseWaiRequest defaultRequest (singleton "_LANG" "en") onError 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")]
|
||||||
} mempty False 1000 g
|
} mempty onError 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")] } mempty False 1000 g
|
r = parseWaiRequest defaultRequest { queryString = [("_LANG", Just "en-US")] } mempty onError 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
|
||||||
r = parseWaiRequest' defaultRequest
|
r = parseWaiRequest defaultRequest
|
||||||
{ requestHeaders = [ ("Accept-Language", "en, es")
|
{ requestHeaders = [ ("Accept-Language", "en, es")
|
||||||
, ("Cookie", "_LANG=en-COOKIE")
|
, ("Cookie", "_LANG=en-COOKIE")
|
||||||
]
|
]
|
||||||
, queryString = [("_LANG", Just "en-QUERY")]
|
, queryString = [("_LANG", Just "en-QUERY")]
|
||||||
} (singleton "_LANG" "en-SESSION") False 10000 g
|
} (singleton "_LANG" "en-SESSION") onError False 10000 g
|
||||||
|
|
||||||
|
onError :: a
|
||||||
|
onError = error "Yesod.InternalRequest.onError"
|
||||||
|
|
||||||
internalRequestTest :: Spec
|
internalRequestTest :: Spec
|
||||||
internalRequestTest = describe "Test.InternalRequestTest" $ do
|
internalRequestTest = describe "Test.InternalRequestTest" $ do
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import YesodCoreTest.JsLoaderSites.Bottom (B(..))
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
|
|
||||||
data H = H
|
data H = H
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module YesodCoreTest.Links (linksTest, Widget) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Text.Hamlet
|
import Text.Hamlet
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
module YesodCoreTest.Media (mediaTest, Widget) where
|
module YesodCoreTest.Media (mediaTest, Widget) where
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
import Text.Lucius
|
import Text.Lucius
|
||||||
|
|||||||
@ -4,7 +4,7 @@ module YesodCoreTest.NoOverloadedStrings (noOverloadedTest, Widget) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
import Data.Monoid (mempty)
|
import Data.Monoid (mempty)
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module YesodCoreTest.RequestBodySize (specs, Widget) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module YesodCoreTest.Widget (widgetTest) where
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Text.Julius
|
import Text.Julius
|
||||||
import Text.Lucius
|
import Text.Lucius
|
||||||
import Text.Hamlet
|
import Text.Hamlet
|
||||||
|
|||||||
@ -9,7 +9,7 @@ module YesodCoreTest.YesodTest
|
|||||||
, module Test.Hspec
|
, module Test.Hspec
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Yesod.Core hiding (Request)
|
import Yesod.Core
|
||||||
import Network.Wai.Test
|
import Network.Wai.Test
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user