Only read request body when needed

This commit is contained in:
Michael Snoyman 2010-02-01 15:46:32 +02:00
parent 89f40e48d0
commit a95704d164
3 changed files with 42 additions and 27 deletions

View File

@ -53,7 +53,8 @@ runFormGeneric params (Form f) =
runFormPost :: Form x -> Handler y x runFormPost :: Form x -> Handler y x
runFormPost f = do runFormPost f = do
rr <- getRawRequest rr <- getRawRequest
runFormGeneric (postParams rr) f pp <- postParams rr
runFormGeneric pp f
-- | Run a form against GET parameters. -- | Run a form against GET parameters.
runFormGet :: Form x -> Handler y x runFormGet :: Form x -> Handler y x

View File

@ -170,10 +170,11 @@ rpxnowLogin = do
Just x -> return x Just x -> return x
Nothing -> notFound Nothing -> notFound
rr <- getRawRequest rr <- getRawRequest
let token = case getParams rr "token" ++ postParams rr "token" of pp <- postParams rr
let token = case getParams rr "token" ++ pp "token" of
[] -> failure MissingToken [] -> failure MissingToken
(x:_) -> x (x:_) -> x
let dest = case postParams rr "dest" of let dest = case pp "dest" of
[] -> case getParams rr "dest" of [] -> case getParams rr "dest" of
[] -> ar [] -> ar
("":_) -> ar ("":_) -> ar

View File

@ -48,6 +48,8 @@ import Data.Convertible.Text
import Control.Arrow ((***)) import Control.Arrow ((***))
import Control.Exception (SomeException (..)) import Control.Exception (SomeException (..))
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Control.Monad.Trans
import Control.Concurrent.MVar
#if TEST #if TEST
import Test.Framework (testGroup, Test) import Test.Framework (testGroup, Test)
@ -71,32 +73,36 @@ languages = rawLangs `fmap` getRawRequest
parseEnv :: (Functor m, RequestReader m) => m W.Request parseEnv :: (Functor m, RequestReader m) => m W.Request
parseEnv = rawRequest `fmap` getRawRequest parseEnv = rawRequest `fmap` getRawRequest
type RequestBodyContents =
( [(ParamName, ParamValue)]
, [(ParamName, FileInfo String BL.ByteString)]
)
-- | The raw information passed through W, cleaned up a bit. -- | The raw information passed through W, cleaned up a bit.
data RawRequest = RawRequest data RawRequest = RawRequest
{ rawGetParams :: [(ParamName, ParamValue)] { rawGetParams :: [(ParamName, ParamValue)]
, rawCookies :: [(ParamName, ParamValue)] , rawCookies :: [(ParamName, ParamValue)]
, rawSession :: [(B.ByteString, B.ByteString)] , rawSession :: [(B.ByteString, B.ByteString)]
-- when we switch to WAI, the following two should be combined and , rawRequestBody :: IO RequestBodyContents
-- wrapped in the IO monad
, rawPostParams :: [(ParamName, ParamValue)]
, rawFiles :: [(ParamName, FileInfo String BL.ByteString)]
, rawRequest :: W.Request , rawRequest :: W.Request
, rawLangs :: [Language] , rawLangs :: [Language]
} }
multiLookup :: [(ParamName, ParamValue)] -> ParamName -> [ParamValue]
multiLookup [] _ = []
multiLookup ((k, v):rest) pn
| k == pn = v : multiLookup rest pn
| otherwise = multiLookup rest pn
-- | All GET paramater values with the given name. -- | All GET paramater values with the given name.
getParams :: RawRequest -> ParamName -> [ParamValue] getParams :: RawRequest -> ParamName -> [ParamValue]
getParams rr name = map snd getParams rr = multiLookup $ rawGetParams rr
. filter (\x -> name == fst x)
. rawGetParams
$ rr
-- | All POST paramater values with the given name. -- | All POST paramater values with the given name.
postParams :: RawRequest -> ParamName -> [ParamValue] postParams :: MonadIO m => RawRequest -> m (ParamName -> [ParamValue])
postParams rr name = map snd postParams rr = do
. filter (\x -> name == fst x) (pp, _) <- liftIO $ rawRequestBody rr
. rawPostParams return $ multiLookup pp
$ rr
-- | All cookies with the given name. -- | All cookies with the given name.
cookies :: RawRequest -> ParamName -> [ParamValue] cookies :: RawRequest -> ParamName -> [ParamValue]
@ -105,16 +111,7 @@ cookies rr name = map snd . filter (fst `equals` name) . rawCookies $ rr
parseWaiRequest :: W.Request -> [(B.ByteString, B.ByteString)] -> IO RawRequest parseWaiRequest :: W.Request -> [(B.ByteString, B.ByteString)] -> IO RawRequest
parseWaiRequest env session = do parseWaiRequest env session = do
let gets' = map (cs *** cs) $ decodeUrlPairs $ W.queryString env let gets' = map (cs *** cs) $ decodeUrlPairs $ W.queryString env
let clength = maybe "0" cs $ lookup W.ReqContentLength let rawCookie = fromMaybe B.empty $ lookup W.Cookie $ W.httpHeaders env
$ W.httpHeaders env
let ctype = maybe "" cs $ lookup W.ReqContentType $ W.httpHeaders env
let convertFileInfo (FileInfo a b c) = FileInfo (cs a) (cs b) c
inputLBS <- WE.toLBS $ W.requestBody env -- FIXME
let (posts, files) = map (convertSuccess *** convertSuccess) ***
map (convertSuccess *** convertFileInfo)
$ parsePost ctype clength
inputLBS
rawCookie = fromMaybe B.empty $ lookup W.Cookie $ W.httpHeaders env
cookies' = map (cs *** cs) $ parseCookies rawCookie cookies' = map (cs *** cs) $ parseCookies rawCookie
acceptLang = lookup W.AcceptLanguage $ W.httpHeaders env acceptLang = lookup W.AcceptLanguage $ W.httpHeaders env
langs = map cs $ maybe [] parseHttpAccept acceptLang langs = map cs $ maybe [] parseHttpAccept acceptLang
@ -124,7 +121,23 @@ parseWaiRequest env session = do
langs'' = case lookup langKey gets' of langs'' = case lookup langKey gets' of
Nothing -> langs' Nothing -> langs'
Just x -> x : langs' Just x -> x : langs'
return $ RawRequest gets' cookies' session posts files env langs'' mrb <- newMVar $ Left env
return $ RawRequest gets' cookies' session (rbHelper mrb) env langs''
rbHelper :: MVar (Either W.Request RequestBodyContents)
-> IO RequestBodyContents
rbHelper mvar = modifyMVar mvar helper where
helper (Right bc) = return (Right bc, bc)
helper (Left env) = do
inputLBS <- WE.toLBS $ W.requestBody env -- FIXME
let clength = maybe "0" cs $ lookup W.ReqContentLength
$ W.httpHeaders env
let ctype = maybe "" cs $ lookup W.ReqContentType $ W.httpHeaders env
let convertFileInfo (FileInfo a b c) = FileInfo (cs a) (cs b) c
let ret = map (cs *** cs) ***
map (cs *** convertFileInfo)
$ parsePost ctype clength inputLBS
return (Right ret, ret)
#if TEST #if TEST
testSuite :: Test testSuite :: Test