on master help sign in
Better parseRequestBody
commit0d7d5dbdf120a4c9b545d31d8fd56cba8866ff3f authorMichael Snoyman <michael@snoyman.com> dateThu 2 Feb 2012, 12:23 (15 years ago) parent60c2fb6
3 files changed+51−35
wai-extra/Network/Wai/Middleware/RequestLogger.hs+6−3 wai-extra/Network/Wai/Parse.hs+37−29 wai-extra/test/WaiExtraTest.hs+8−3
wai-extra/Network/Wai/Middleware/RequestLogger.hs+6−3
@@ -17,7 +17,7 @@ import Network.Wai (Request(..), Middleware)
1717import System.Log.FastLogger
1818import Network.HTTP.Types as H
1919
20−import Network.Wai.Parse (parseRequestBody, lbsBackEnd, fileName, Param, File)
20+import Network.Wai.Parse (sinkRequestBody, lbsBackEnd, fileName, Param, File, getRequestBodyType)
2121import qualified Data.ByteString.Lazy as LBS
2222
2323import qualified Data.Conduit as C
@@ -77,7 +77,7 @@ logCallbackDev cb app req = do
7777 body <- requestBody req C.$$ CL.consume
7878 postParams <- if any (requestMethod req ==) ["GET", "HEAD"]
7979 then return []
80− else do postParams <- liftIO $ allPostParams req body
80+ else do postParams <- liftIO $ allPostParams body
8181 return $ collectPostParams postParams
8282 let getParams = map emptyGetParam $ queryString req
8383
@@ -99,7 +99,10 @@ logCallbackDev cb app req = do
9999 if null params then ""
100100 else BS.concat ["\n", prefix, pack (show params)]
101101
102− allPostParams req' body = C.runResourceT $ CL.sourceList body C.$$ parseRequestBody lbsBackEnd req'
102+ allPostParams body =
103+ case getRequestBodyType req of
104+ Nothing -> return ([], [])
105+ Just rbt -> C.runResourceT $ CL.sourceList body C.$$ sinkRequestBody lbsBackEnd rbt
103106
104107 emptyGetParam :: (BS.ByteString, Maybe BS.ByteString) -> (BS.ByteString, BS.ByteString)
105108 emptyGetParam (k, Just v) = (k,v)
wai-extra/Network/Wai/Parse.hs+37−29
@@ -6,6 +6,9 @@
66module Network.Wai.Parse
77 ( parseHttpAccept
88 , parseRequestBody
9+ , RequestBodyType (..)
10+ , getRequestBodyType
11+ , sinkRequestBody
912 , conduitRequestBody
1013 , lbsBackEnd
1114 , tempFileBackEnd
@@ -146,29 +149,16 @@ data FileInfo c = FileInfo
146149type Param = (S.ByteString, S.ByteString)
147150type File y = (S.ByteString, FileInfo y)
148151
149−parseRequestBody :: BackEnd y
150− -> Request
151− -> C.Sink S.ByteString IO ([Param], [File y])
152−parseRequestBody s r = fmap partitionEithers $ conduitRequestBody s r C.=$ CL.consume
152+data RequestBodyType = UrlEncoded | Multipart S.ByteString
153153
154−conduitRequestBody :: BackEnd y
155− -> Request
156− -> C.Conduit S.ByteString IO (Either Param (File y))
157−conduitRequestBody sink req = do
158− case ctype of
159− Nothing -> C.Conduit
160− { C.conduitPush = \bs -> return $ C.Finished (Just bs) []
161− , C.conduitClose = return []
162− }
163− Just Nothing -> C.sequenceSink () $ \() -> do -- url-encoded
164− -- NOTE: in general, url-encoded data will be in a single chunk.
165− -- Therefore, I'm optimizing for the usual case by sticking with
166− -- strict byte strings here.
167− bs <- CL.consume
168− return $ C.Emit () $ map Left $ H.parseSimpleQuery $ S.concat bs
169− Just (Just bound) -> -- multi-part
170− let bound'' = S8.pack "--" `S.append` bound
171− in parsePieces sink bound''
154+getRequestBodyType :: Request -> Maybe RequestBodyType
155+getRequestBodyType req = do
156+ ctype <- lookup "Content-Type" $ requestHeaders req
157+ if urlenc `S.isPrefixOf` ctype
158+ then Just UrlEncoded
159+ else case boundary ctype of
160+ Just x -> Just $ Multipart x
161+ Nothing -> Nothing
172162 where
173163 urlenc = S8.pack "application/x-www-form-urlencoded"
174164 formBound = S8.pack "multipart/form-data;"
@@ -181,13 +171,31 @@ conduitRequestBody sink req = do
181171 then Just $ S.drop (S.length bound') s'
182172 else Nothing
183173 else Nothing
184− ctype = do
185− ctype' <- lookup "Content-Type" $ requestHeaders req
186− if urlenc `S.isPrefixOf` ctype'
187− then Just Nothing
188− else case boundary ctype' of
189− Just x -> Just $ Just x
190− Nothing -> Nothing
174+
175+parseRequestBody :: BackEnd y
176+ -> Request
177+ -> C.ResourceT IO ([Param], [File y])
178+parseRequestBody s r =
179+ case getRequestBodyType r of
180+ Nothing -> return ([], [])
181+ Just rbt -> fmap partitionEithers $ requestBody r C.$$ conduitRequestBody s rbt C.=$ CL.consume
182+
183+sinkRequestBody :: BackEnd y
184+ -> RequestBodyType
185+ -> C.Sink S.ByteString IO ([Param], [File y])
186+sinkRequestBody s r = fmap partitionEithers $ conduitRequestBody s r C.=$ CL.consume
187+
188+conduitRequestBody :: BackEnd y
189+ -> RequestBodyType
190+ -> C.Conduit S.ByteString IO (Either Param (File y))
191+conduitRequestBody _ UrlEncoded = C.sequenceSink () $ \() -> do -- url-encoded
192+ -- NOTE: in general, url-encoded data will be in a single chunk.
193+ -- Therefore, I'm optimizing for the usual case by sticking with
194+ -- strict byte strings here.
195+ bs <- CL.consume
196+ return $ C.Emit () $ map Left $ H.parseSimpleQuery $ S.concat bs
197+conduitRequestBody backend (Multipart bound) =
198+ parsePieces backend $ S8.pack "--" `S.append` bound
191199
192200takeLine :: C.Sink S.ByteString IO (Maybe S.ByteString)
193201takeLine =
wai-extra/test/WaiExtraTest.hs+8−3
@@ -100,7 +100,9 @@ parseRequestBody' :: BackEnd L.ByteString
100100 -> SRequest
101101 -> C.ResourceT IO ([(S.ByteString, S.ByteString)], [(S.ByteString, FileInfo L.ByteString)])
102102parseRequestBody' sink (SRequest req bod) =
103− CL.sourceList (L.toChunks bod) C.$$ parseRequestBody sink req
103+ case getRequestBodyType req of
104+ Nothing -> return ([], [])
105+ Just rbt -> CL.sourceList (L.toChunks bod) C.$$ sinkRequestBody sink rbt
104106
105107caseParseRequestBody :: Assertion
106108caseParseRequestBody =
@@ -399,8 +401,11 @@ caseDalvikMultipart = do
399401 let request' = defaultRequest
400402 { requestHeaders = headers
401403 }
402− (params, files) <- C.runResourceT $ sourceFile "test/requests/dalvik-request"
403− C.$$ parseRequestBody lbsBackEnd request'
404+ (params, files) <-
405+ case getRequestBodyType request' of
406+ Nothing -> return ([], [])
407+ Just rbt -> C.runResourceT $ sourceFile "test/requests/dalvik-request"
408+ C.$$ sinkRequestBody lbsBackEnd rbt
404409 lookup "scannedTime" params @?= Just "1.298590056748E9"
405410 lookup "geoLong" params @?= Just "0"
406411 lookup "geoLat" params @?= Just "0"