@@ -36,15 +36,15 @@ import Data.Function (on)
3636import System.Directory (removeFile, getTemporaryDirectory)
3737import System.IO (hClose, openBinaryTempFile)
3838import Network.Wai
39−import qualified Data.Conduit as C
39+import Data.Conduit
4040import qualified Data.Conduit.List as CL
4141import qualified Data.Conduit.Binary as CB
4242import Control.Monad.IO.Class (liftIO)
4343import qualified Network.HTTP.Types as H
4444import Data.Either (partitionEithers)
45+import Control.Monad (when, unless)
4546import Control.Monad.Trans.Class (lift)
4647import Control.Monad.Trans.Resource (allocate, release, register)
47−import Data.Void (absurd)
4848
4949uncons :: S.ByteString -> Maybe (Word8, S.ByteString)
5050uncons s
@@ -97,11 +97,11 @@ parseHttpAccept = map fst
9797 trimWhite = S.dropWhile (== 32) -- space
9898
9999-- | Store uploaded files in memory
100−lbsSink :: Monad m => C.Sink S.ByteString m L.ByteString
100+lbsSink :: Monad m => Sink S.ByteString m L.ByteString
101101lbsSink = fmap L.fromChunks CL.consume
102102
103103-- | Save uploaded files on disk as temporary files
104−tempFileSink :: C.MonadResource m => C.Sink S.ByteString m FilePath
104+tempFileSink :: MonadResource m => Sink S.ByteString m FilePath
105105tempFileSink = do
106106 (key, (fp, h)) <- lift $ allocate (do
107107 tempDir <- getTemporaryDirectory
@@ -145,45 +145,47 @@ getRequestBodyType req = do
145145 else Nothing
146146 else Nothing
147147
148−parseRequestBody :: C.Sink S.ByteString (C.ResourceT IO) y
148+parseRequestBody :: Sink S.ByteString (ResourceT IO) y
149149 -> Request
150− -> C.ResourceT IO ([Param], [File y])
150+ -> ResourceT IO ([Param], [File y])
151151parseRequestBody s r =
152152 case getRequestBodyType r of
153153 Nothing -> return ([], [])
154− Just rbt -> fmap partitionEithers $ requestBody r C.$$ conduitRequestBody s rbt C.=$ CL.consume
154+ Just rbt -> fmap partitionEithers $ requestBody r $$ conduitRequestBody s rbt =$ CL.consume
155155
156−sinkRequestBody :: C.Sink S.ByteString (C.ResourceT IO) y
156+sinkRequestBody :: Sink S.ByteString (ResourceT IO) y
157157 -> RequestBodyType
158− -> C.Sink S.ByteString (C.ResourceT IO) ([Param], [File y])
159−sinkRequestBody s r = fmap partitionEithers $ conduitRequestBody s r C.=$ CL.consume
158+ -> Sink S.ByteString (ResourceT IO) ([Param], [File y])
159+sinkRequestBody s r = fmap partitionEithers $ conduitRequestBody s r =$ CL.consume
160160
161−conduitRequestBody :: C.Sink S.ByteString (C.ResourceT IO) y
161+conduitRequestBody :: Sink S.ByteString (ResourceT IO) y
162162 -> RequestBodyType
163− -> C.Conduit S.ByteString (C.ResourceT IO) (Either Param (File y))
164−conduitRequestBody _ UrlEncoded = C.sequenceSink () $ \() -> do -- url-encoded
163+ -> Conduit S.ByteString (ResourceT IO) (Either Param (File y))
164+conduitRequestBody _ UrlEncoded = do
165165 -- NOTE: in general, url-encoded data will be in a single chunk.
166166 -- Therefore, I'm optimizing for the usual case by sticking with
167167 -- strict byte strings here.
168168 bs <- CL.consume
169− return $ C.Emit () $ map Left $ H.parseSimpleQuery $ S.concat bs
169+ mapM_ yield $ map Left $ H.parseSimpleQuery $ S.concat bs
170170conduitRequestBody backend (Multipart bound) =
171171 parsePieces backend $ S8.pack "--" `S.append` bound
172172
173−takeLine :: C.Sink S.ByteString (C.ResourceT IO) (Maybe S.ByteString)
173+takeLine :: Monad m => Pipe S.ByteString S.ByteString o u m (Maybe S.ByteString)
174174takeLine =
175− C.sinkState id push close'
175+ go id
176176 where
177− close' _ = return Nothing
177+ go front = await >>= maybe (close front) (push front)
178+
179+ close front = leftover (front S.empty) >> return Nothing
178180 push front bs = do
179181 let (x, y) = S.break (== 10) $ front bs -- LF
180182 in if S.null y
181− then return $ C.StateProcessing $ S.append x
183+ then go $ S.append x
182184 else do
183− let lo = if S.length y > 1 then Just (S.drop 1 y) else Nothing
184− return $ C.StateDone lo $ Just $ killCR x
185+ when (S.length y > 1) $ leftover $ S.drop 1 y
186+ return $ Just $ killCR x
185187
186−takeLines :: C.Sink S.ByteString (C.ResourceT IO) [S.ByteString]
188+takeLines :: Pipe S.ByteString S.ByteString o u (ResourceT IO) [S.ByteString]
187189takeLines = do
188190 res <- takeLine
189191 case res of
@@ -194,20 +196,16 @@ takeLines = do
194196 ls <- takeLines
195197 return $ l : ls
196198
197−parsePieces :: C.Sink S.ByteString (C.ResourceT IO) y -> S.ByteString
198− -> C.Conduit S.ByteString (C.ResourceT IO) (Either Param (File y))
199−parsePieces sink bound = C.sequenceSink True (parsePiecesSink sink bound)
200−
201−parsePiecesSink :: C.Sink S.ByteString (C.ResourceT IO) y
202− -> S.ByteString
203− -> C.SequencedSink Bool S.ByteString (C.ResourceT IO) (Either Param (File y))
204−parsePiecesSink _ _ False = return C.Stop
205−parsePiecesSink sink bound True = do
206− _boundLine <- takeLine
207− res' <- takeLines
208− case res' of
209− [] -> return C.Stop
210− _ -> do
199+parsePieces :: Sink S.ByteString (ResourceT IO) y
200+ -> S.ByteString
201+ -> Pipe S.ByteString S.ByteString (Either Param (File y)) u (ResourceT IO) ()
202+parsePieces sink bound =
203+ loop
204+ where
205+ loop = do
206+ _boundLine <- takeLine
207+ res' <- takeLines
208+ unless (null res') $ do
211209 let ls' = map parsePair res'
212210 let x = do
213211 cd <- lookup contDisp ls'
@@ -218,30 +216,31 @@ parsePiecesSink sink bound True = do
218216 case x of
219217 Just (mct, name, Just filename) -> do
220218 let ct = fromMaybe "application/octet-stream" mct
221− (y, wasFound) <- sinkTillBound' Nothing bound sink
219+ (wasFound, y) <- sinkTillBound' bound sink
222220 let fi = FileInfo filename ct y
223221 let y' = (name, fi)
224− return $ C.Emit wasFound [Right y']
222+ yield $ Right y'
223+ when wasFound loop
225224 Just (_ct, name, Nothing) -> do
226225 let seed = id
227226 let iter front bs = return $ front . (:) bs
228− (front, wasFound) <-
229− sinkTillBound bound iter seed
227+ (wasFound, front) <- sinkTillBound bound iter seed
230228 let bs = S.concat $ front []
231229 let x' = (name, qsDecode bs)
232− return $ C.Emit wasFound [Left x']
230+ yield $ Left x'
231+ when wasFound loop
233232 _ -> do
234233 -- ignore this part
235234 let seed = ()
236235 iter () _ = return ()
237− ((), wasFound) <- sinkTillBound bound iter seed
238− return $ C.Emit wasFound []
239− where
240− contDisp = S8.pack "Content-Disposition"
241− contType = S8.pack "Content-Type"
242− parsePair s =
243− let (x, y) = breakDiscard 58 s -- colon
244− in (x, S.dropWhile (== 32) y) -- space
236+ (wasFound, ()) <- sinkTillBound bound iter seed
237+ when wasFound loop
238+ where
239+ contDisp = S8.pack "Content-Disposition"
240+ contType = S8.pack "Content-Type"
241+ parsePair s =
242+ let (x, y) = breakDiscard 58 s -- colon
243+ in (x, S.dropWhile (== 32) y) -- space
245244
246245data Bound = FoundBound S.ByteString S.ByteString
247246 | NoBound
@@ -265,60 +264,49 @@ findBound b bs = go [0..S.length bs - 1]
265264 | S.index b x == S.index bs y = mismatch xs ys
266265 | otherwise = True
267266
268−sinkTillBound' :: Maybe S.ByteString -> S.ByteString -> C.Sink S.ByteString (C.ResourceT IO) y -> C.Sink S.ByteString (C.ResourceT IO) (y, Bool)
269−sinkTillBound' mfront _ (C.Done _ y) = C.Done mfront (y, False)
270−sinkTillBound' _ _ (C.HaveOutput _ _ o) = absurd o
271−sinkTillBound' mfront bound (C.PipeM mp mc) = C.PipeM (fmap (sinkTillBound' mfront bound) mp) (fmap (\y -> (y, False)) mc)
272−sinkTillBound' mfront bound (C.NeedInput pushI closeI) =
273− C.NeedInput push close
267+sinkTillBound' :: S.ByteString
268+ -> Sink S.ByteString (ResourceT IO) y
269+ -> Pipe S.ByteString S.ByteString o u (ResourceT IO) (Bool, y)
270+sinkTillBound' bound sink = conduitTillBound bound >+> withUpstream (sinkToPipe sink)
271+
272+conduitTillBound :: Monad m
273+ => S.ByteString -- bound
274+ -> Pipe S.ByteString S.ByteString S.ByteString u m Bool
275+conduitTillBound bound =
276+ go id
274277 where
275− push bs' = do
276− let bs = maybe id S.append mfront $ bs'
278+ go front = await >>= maybe (close front) (push front)
279+ close front = do
280+ let bs = front S.empty
281+ unless (S.null bs) $ yield bs
282+ return False
283+ push front bs' = do
284+ let bs = front bs'
277285 case findBound bound bs of
278286 FoundBound before after -> do
279287 let before' = killCRLF before
280− res <- lift $ return () C.$$ pushI before'
281− C.Done (Just after) (res, True)
288+ yield before'
289+ leftover after
290+ return True
282291 NoBound -> do
283292 -- don't emit newlines, in case it's part of a bound
284293 let (toEmit, front') =
285294 if not (S8.null bs) && S8.last bs `elem` "\r\n"
286295 then let (x, y) = S.splitAt (S.length bs - 2) bs
287− in (x, Just y)
288− else (bs, Nothing)
289− sinkTillBound' front' bound (pushI toEmit)
290− PartialBound -> sinkTillBound' (Just bs) bound (C.NeedInput pushI closeI)
291− close = fmap (\y -> (y, False)) closeI
296+ in (x, S.append y)
297+ else (bs, id)
298+ yield toEmit
299+ go front'
300+ PartialBound -> go $ S.append bs
292301
293302sinkTillBound :: S.ByteString
294303 -> (x -> S.ByteString -> IO x)
295304 -> x
296− -> C.Sink S.ByteString (C.ResourceT IO) (x, Bool)
297−sinkTillBound bound iter seed0 = C.sinkState
298− (id, seed0)
299− push
300− close'
305+ -> Pipe S.ByteString S.ByteString o u (ResourceT IO) (Bool, x)
306+sinkTillBound bound iter seed0 =
307+ conduitTillBound bound >+> withUpstream (CL.foldM iter' seed0)
301308 where
302− close' (front, seed) = do
303− seed' <- liftIO $ iter seed $ front S.empty
304− return (seed', False)
305− push (front, seed) bs' = do
306− let bs = front bs'
307− case findBound bound bs of
308− FoundBound before after -> do
309− let before' = killCRLF before
310− seed' <- liftIO $ iter seed before'
311− return $ C.StateDone (Just after) (seed', True)
312− NoBound -> do
313− -- don't emit newlines, in case it's part of a bound
314− let (toEmit, front') =
315− if not (S8.null bs) && S8.last bs `elem` "\r\n"
316− then let (x, y) = S.splitAt (S.length bs - 2) bs
317− in (x, S.append y)
318− else (bs, id)
319− seed' <- liftIO $ iter seed toEmit
320− return $ C.StateProcessing (front', seed')
321− PartialBound -> return $ C.StateProcessing (S.append bs, seed)
309+ iter' a b = liftIO $ iter a b
322310
323311parseAttrs :: S.ByteString -> [(S.ByteString, S.ByteString)]
324312parseAttrs = map go . S.split 59 -- semicolon