Full support for ResponseEnumerator

This commit is contained in:
Michael Snoyman 2010-12-16 21:38:01 +02:00
parent 68e5969ecf
commit b2e95911d8
2 changed files with 68 additions and 41 deletions

View File

@ -313,30 +313,33 @@ toWaiApp' y key' segments env = do
let ya = runHandler h render eurl' id y id let ya = runHandler h render eurl' id y id
let sessionMap = Map.fromList let sessionMap = Map.fromList
$ filter (\(x, _) -> x /= nonceKey) session' $ filter (\(x, _) -> x /= nonceKey) session'
(s, hs, ct, c, sessionFinal) <- unYesodApp ya eh rr types sessionMap yar <- unYesodApp ya eh rr types sessionMap
let sessionVal = case yar of
case key' of YARPlain s hs ct c sessionFinal -> do
Nothing -> B.empty let sessionVal =
Just key'' -> case key' of
encodeSession key'' exp' host Nothing -> B.empty
$ Map.toList Just key'' ->
$ Map.insert nonceKey (reqNonce rr) sessionFinal encodeSession key'' exp' host
let hs' = $ Map.toList
case key' of $ Map.insert nonceKey (reqNonce rr) sessionFinal
Nothing -> hs let hs' =
Just _ -> AddCookie case key' of
(clientSessionDuration y) Nothing -> hs
sessionName Just _ -> AddCookie
(bsToChars sessionVal) (clientSessionDuration y)
: hs sessionName
hs'' = map (headerToPair getExpires) hs' (bsToChars sessionVal)
hs''' = ("Content-Type", charsToBs ct) : hs'' : hs
return $ hs'' = map (headerToPair getExpires) hs'
case c of hs''' = ("Content-Type", charsToBs ct) : hs''
ContentLBS lbs -> W.ResponseLBS s hs''' lbs return $
ContentFile fp -> W.ResponseFile s hs''' fp case c of
ContentEnum e -> W.ResponseEnumerator $ \iter -> ContentLBS lbs -> W.ResponseLBS s hs''' lbs
run_ $ e $$ iter s hs''' ContentFile fp -> W.ResponseFile s hs''' fp
ContentEnum e -> W.ResponseEnumerator $ \iter ->
run_ $ e $$ iter s hs'''
YAREnum e -> return $ W.ResponseEnumerator e
httpAccept :: W.Request -> [ContentType] httpAccept :: W.Request -> [ContentType]
httpAccept = map B.unpack httpAccept = map B.unpack

View File

@ -51,6 +51,7 @@ module Yesod.Handler
, sendResponse , sendResponse
, sendResponseStatus , sendResponseStatus
, sendResponseCreated , sendResponseCreated
, sendResponseEnumerator
-- * Setting headers -- * Setting headers
, setCookie , setCookie
, deleteCookie , deleteCookie
@ -85,6 +86,7 @@ module Yesod.Handler
, localNoCurrent , localNoCurrent
, HandlerData , HandlerData
, ErrorResponse (..) , ErrorResponse (..)
, YesodAppResult (..)
#if TEST #if TEST
, testSuite , testSuite
#endif #endif
@ -232,15 +234,20 @@ newtype YesodApp = YesodApp
-> Request -> Request
-> [ContentType] -> [ContentType]
-> SessionMap -> SessionMap
-> IO (W.Status, [Header], ContentType, Content, SessionMap) -> IO YesodAppResult
} }
data YesodAppResult
= YAREnum (forall a. W.ResponseEnumerator a)
| YARPlain W.Status [Header] ContentType Content SessionMap
data HandlerContents = data HandlerContents =
HCContent W.Status ChooseRep HCContent W.Status ChooseRep
| HCError ErrorResponse | HCError ErrorResponse
| HCSendFile ContentType FilePath | HCSendFile ContentType FilePath
| HCRedirect RedirectType String | HCRedirect RedirectType String
| HCCreated String | HCCreated String
| HCEnum (forall a. W.ResponseEnumerator a)
instance Failure ErrorResponse (GHandler sub master) where instance Failure ErrorResponse (GHandler sub master) where
failure = GHandler . lift . throwMEither . HCError failure = GHandler . lift . throwMEither . HCError
@ -307,34 +314,46 @@ runHandler handler mrender sroute tomr ma tosa =
) (\e -> return ((MLeft $ HCError $ toErrorHandler e, id), initSession)) ) (\e -> return ((MLeft $ HCError $ toErrorHandler e, id), initSession))
let contents = meither id (HCContent W.status200 . chooseRep) contents' let contents = meither id (HCContent W.status200 . chooseRep) contents'
let handleError e = do let handleError e = do
(_, hs, ct, c, sess) <- unYesodApp (eh e) safeEh rr cts finalSession yar <- unYesodApp (eh e) safeEh rr cts finalSession
let hs' = headers hs case yar of
return (getStatus e, hs', ct, c, sess) YARPlain _ hs ct c sess ->
let hs' = headers hs
in return $ YARPlain (getStatus e) hs' ct c sess
YAREnum _ -> return yar
let sendFile' ct fp = let sendFile' ct fp =
return (W.status200, headers [], ct, ContentFile fp, finalSession) return $ YARPlain W.status200 (headers []) ct (ContentFile fp) finalSession
case contents of case contents of
HCContent status a -> do HCContent status a -> do
(ct, c) <- chooseRep a cts (ct, c) <- chooseRep a cts
return (status, headers [], ct, c, finalSession) return $ YARPlain status (headers []) ct c finalSession
HCError e -> handleError e HCError e -> handleError e
HCRedirect rt loc -> do HCRedirect rt loc -> do
let hs = Header "Location" loc : headers [] let hs = Header "Location" loc : headers []
return (getRedirectStatus rt, hs, typePlain, emptyContent, return $ YARPlain
finalSession) (getRedirectStatus rt) hs typePlain emptyContent
finalSession
HCSendFile ct fp -> E.catch HCSendFile ct fp -> E.catch
(sendFile' ct fp) (sendFile' ct fp)
(handleError . toErrorHandler) (handleError . toErrorHandler)
HCCreated loc -> do HCCreated loc -> do -- FIXME add status201 to WAI
let hs = Header "Location" loc : headers [] let hs = Header "Location" loc : headers []
return (W.Status 201 (S8.pack "Created"), hs, typePlain, return $ YARPlain
emptyContent, (W.Status 201 (S8.pack "Created"))
finalSession) hs
typePlain
emptyContent
finalSession
HCEnum e -> return $ YAREnum e
safeEh :: ErrorResponse -> YesodApp safeEh :: ErrorResponse -> YesodApp
safeEh er = YesodApp $ \_ _ _ session -> do safeEh er = YesodApp $ \_ _ _ session -> do
liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er
return (W.status500, [], typePlain, toContent "Internal Server Error", return $ YARPlain
session) W.status500
[]
typePlain
(toContent "Internal Server Error")
session
-- | Redirect to the given route. -- | Redirect to the given route.
redirect :: RedirectType -> Route master -> GHandler sub master a redirect :: RedirectType -> Route master -> GHandler sub master a
@ -439,6 +458,14 @@ sendResponseCreated url = do
r <- getUrlRender r <- getUrlRender
GHandler $ lift $ throwMEither $ HCCreated $ r url GHandler $ lift $ throwMEither $ HCCreated $ r url
-- | Send a 'W.ResponseEnumerator'. Please note: this function is rarely
-- necessary, and will /disregard/ any changes to response headers and session
-- that you have already specified. This function short-circuits. It should be
-- considered only for they specific needs. If you are not sure if you need it,
-- you don't.
sendResponseEnumerator :: (forall a. W.ResponseEnumerator a) -> GHandler s m b
sendResponseEnumerator = GHandler . lift . throwMEither . HCEnum
-- | Return a 404 not found page. Also denotes no handler available. -- | Return a 404 not found page. Also denotes no handler available.
notFound :: Failure ErrorResponse m => m a notFound :: Failure ErrorResponse m => m a
notFound = failure NotFound notFound = failure NotFound
@ -559,6 +586,3 @@ testSuite = testGroup "Yesod.Handler"
] ]
#endif #endif
-- FIXME add a sendEnum that uses a ResponseEnumerator and bypasses all status
-- and header stuff