43
43
import System.IO (hClose, openBinaryTempFile)
45
45
import Data.Conduit
46
import Data.Conduit.Internal (sinkToPipe)
46
import Data.Conduit.Internal ()
47
47
import qualified Data.Conduit.List as CL
48
48
import qualified Data.Conduit.Binary as CB
49
49
import Control.Monad.IO.Class (liftIO)
51
51
import Data.Either (partitionEithers)
52
52
import Control.Monad (when, unless)
53
53
import Control.Monad.Trans.Class (lift)
54
import Control.Monad.Trans.Resource (allocate, release, register)
55
#if MIN_VERSION_conduit(1, 0, 0)
56
import Data.Conduit.Internal (Pipe (NeedInput, HaveOutput), (>+>), withUpstream, Sink (..), injectLeftovers, ConduitM (..))
54
import Control.Monad.Trans.Resource (allocate, release, register, InternalState, runInternalState)
55
import Data.Conduit.Internal (Pipe (NeedInput, HaveOutput), (>+>), withUpstream, injectLeftovers, ConduitM (..))
57
56
import Data.Void (Void)
60
58
breakDiscard :: Word8 -> S.ByteString -> (S.ByteString, S.ByteString)
91
89
lbsBackEnd _ _ = fmap L.fromChunks CL.consume
93
91
-- | Save uploaded files on disk as temporary files
94
tempFileBackEnd :: MonadResource m => ignored1 -> ignored2 -> Sink S.ByteString m FilePath
93
-- Note: starting with version 2.0, it is the responsibility of the caller to
94
-- remove any temp files created by using this backend.
95
tempFileBackEnd :: InternalState -> ignored1 -> ignored2 -> Sink S.ByteString IO FilePath
95
96
tempFileBackEnd = tempFileBackEndOpts getTemporaryDirectory "webenc.buf"
97
98
-- | Same as 'tempFileSink', but use configurable temp folders and patterns.
98
tempFileBackEndOpts :: MonadResource m
99
=> IO FilePath -- ^ get temporary directory
99
tempFileBackEndOpts :: IO FilePath -- ^ get temporary directory
100
100
-> String -- ^ filename pattern
103
-> Sink S.ByteString m FilePath
104
tempFileBackEndOpts getTmpDir pattern _ _ = do
105
(key, (fp, h)) <- lift $ allocate (do
104
-> Sink S.ByteString IO FilePath
105
tempFileBackEndOpts getTmpDir pattern internalState _ _ = do
106
(key, (fp, h)) <- flip runInternalState internalState $ allocate (do
106
107
tempDir <- getTmpDir
107
108
openBinaryTempFile tempDir pattern) (\(_, h) -> hClose h)
108
_ <- lift $ register $ removeFile fp
109
_ <- runInternalState (register $ removeFile fp) internalState
110
111
lift $ release key
128
129
-- type, and returns a `Sink` for storing the contents.
129
130
type BackEnd a = S.ByteString -- ^ parameter name
131
-> Sink S.ByteString (ResourceT IO) a
132
-> Sink S.ByteString IO a
133
134
data RequestBodyType = UrlEncoded | Multipart S.ByteString
168
169
parseRequestBody :: BackEnd y
170
-> ResourceT IO ([Param], [File y])
171
-> IO ([Param], [File y])
171
172
parseRequestBody s r =
172
173
case getRequestBodyType r of
173
174
Nothing -> return ([], [])
176
177
sinkRequestBody :: BackEnd y
177
178
-> RequestBodyType
178
-> Sink S.ByteString (ResourceT IO) ([Param], [File y])
179
-> Sink S.ByteString IO ([Param], [File y])
179
180
sinkRequestBody s r = fmap partitionEithers $ conduitRequestBody s r =$ CL.consume
181
182
conduitRequestBody :: BackEnd y
182
183
-> RequestBodyType
183
-> Conduit S.ByteString (ResourceT IO) (Either Param (File y))
184
-> Conduit S.ByteString IO (Either Param (File y))
184
185
conduitRequestBody _ UrlEncoded = do
185
186
-- NOTE: in general, url-encoded data will be in a single chunk.
186
187
-- Therefore, I'm optimizing for the usual case by sticking with
190
191
conduitRequestBody backend (Multipart bound) =
191
192
parsePieces backend $ S8.pack "--" `S.append` bound
193
#if MIN_VERSION_conduit(1, 0, 0)
194
194
takeLine :: Monad m => Consumer S.ByteString m (Maybe S.ByteString)
196
takeLine :: Monad m => Pipe S.ByteString S.ByteString o u m (Maybe S.ByteString)
209
206
when (S.length y > 1) $ leftover $ S.drop 1 y
210
207
return $ Just $ killCR x
212
#if MIN_VERSION_conduit(1, 0, 0)
213
takeLines :: Consumer S.ByteString (ResourceT IO) [S.ByteString]
215
takeLines :: Pipe S.ByteString S.ByteString o u (ResourceT IO) [S.ByteString]
209
takeLines :: Consumer S.ByteString IO [S.ByteString]
227
220
parsePieces :: BackEnd y
229
#if MIN_VERSION_conduit(1, 0, 0)
230
-> ConduitM S.ByteString (Either Param (File y)) (ResourceT IO) ()
232
-> Pipe S.ByteString S.ByteString (Either Param (File y)) u (ResourceT IO) ()
222
-> ConduitM S.ByteString (Either Param (File y)) IO ()
234
223
parsePieces sink bound =
308
#if MIN_VERSION_conduit(1, 0, 0)
309
-> ConduitM S.ByteString o (ResourceT IO) (Bool, y)
311
-> Pipe S.ByteString S.ByteString o u (ResourceT IO) (Bool, y)
297
-> ConduitM S.ByteString o IO (Bool, y)
313
298
sinkTillBound' bound name fi sink =
314
#if MIN_VERSION_conduit(1, 0, 0)
315
299
ConduitM $ anyOutput $
317
300
conduitTillBound bound >+> withUpstream (fix $ sink name fi)
319
#if MIN_VERSION_conduit(1, 0, 0)
320
fix :: Sink S8.ByteString (ResourceT IO) y -> Pipe Void S8.ByteString Void Bool (ResourceT IO) y
321
fix (ConduitM p) = ignoreTerm >+> injectLeftovers p
302
fix :: Sink S8.ByteString IO y -> Pipe Void S8.ByteString Void Bool IO y
303
fix p = ignoreTerm >+> injectLeftovers (unConduitM p)
322
304
ignoreTerm = await' >>= maybe (return ()) (\x -> yield' x >> ignoreTerm)
323
305
await' = NeedInput (return . Just) (const $ return Nothing)
324
306
yield' = HaveOutput (return ()) (return ())
326
308
anyOutput p = p >+> dropInput
327
309
dropInput = NeedInput (const dropInput) return
332
311
conduitTillBound :: Monad m
333
312
=> S.ByteString -- bound
334
#if MIN_VERSION_conduit(1, 0, 0)
335
313
-> Pipe S.ByteString S.ByteString S.ByteString () m Bool
337
-> Pipe S.ByteString S.ByteString S.ByteString u m Bool
339
314
conduitTillBound bound =
340
#if MIN_VERSION_conduit(1, 0, 0)
345
318
go front = await >>= maybe (close front) (push front)
369
342
sinkTillBound :: S.ByteString
370
343
-> (x -> S.ByteString -> IO x)
372
#if MIN_VERSION_conduit(1, 0, 0)
373
-> Consumer S.ByteString (ResourceT IO) (Bool, x)
375
-> Pipe S.ByteString S.ByteString o u (ResourceT IO) (Bool, x)
345
-> Consumer S.ByteString IO (Bool, x)
377
346
sinkTillBound bound iter seed0 =
378
#if MIN_VERSION_conduit(1, 0, 0)
381
348
(conduitTillBound bound >+> (withUpstream $ ij $ CL.foldM iter' seed0))
383
350
iter' a b = liftIO $ iter a b
384
#if MIN_VERSION_conduit(1, 0, 0)
385
351
ij (ConduitM p) = ignoreTerm >+> injectLeftovers p
386
352
ignoreTerm = await' >>= maybe (return ()) (\x -> yield' x >> ignoreTerm)
387
353
await' = NeedInput (return . Just) (const $ return Nothing)
388
354
yield' = HaveOutput (return ()) (return ())
393
356
parseAttrs :: S.ByteString -> [(S.ByteString, S.ByteString)]
394
357
parseAttrs = map go . S.split 59 -- semicolon