@@ -22,8 +22,8 @@ module Servant.Multipart
22
22
, lookupFile
23
23
, MultipartOptions (.. )
24
24
, defaultMultipartOptions
25
- , TmpBackendOptions ( .. )
26
- , LbsBackendOptions ( .. )
25
+ , Tmp
26
+ , Mem
27
27
, defaultTmpBackendOptions
28
28
, Input (.. )
29
29
, FileData (.. )
@@ -128,7 +128,7 @@ import qualified Data.ByteString.Lazy as LBS
128
128
-- after your handler has run, if they are still there. It is
129
129
-- therefore recommended to move or copy them somewhere in your
130
130
-- handler code if you need to keep the content around.
131
- data MultipartForm options a
131
+ data MultipartForm tag a
132
132
133
133
-- | What servant gets out of a @multipart/form-data@ form submission.
134
134
--
@@ -142,9 +142,9 @@ data MultipartForm options a
142
142
-- 'FileData' which among other things contains the path to the temporary file
143
143
-- (to be removed when your handler is done running) with a given uploaded
144
144
-- file's content. See haddocks for 'FileData'.
145
- data MultipartData options = MultipartData
145
+ data MultipartData tag = MultipartData
146
146
{ inputs :: [Input ]
147
- , files :: [FileData options ]
147
+ , files :: [FileData tag ]
148
148
}
149
149
150
150
@@ -169,24 +169,24 @@ fromRaw (inputs, files) = MultipartData is fs
169
169
-- | Representation for an uploaded file, usually resulting from
170
170
-- picking a local file for an HTML input that looks like
171
171
-- @\<input type="file" name="somefile" /\>@.
172
- data FileData options = FileData
172
+ data FileData tag = FileData
173
173
{ fdInputName :: Text -- ^ @name@ attribute of the corresponding
174
174
-- HTML @\<input\>@
175
175
, fdFileName :: Text -- ^ name of the file on the client's disk
176
176
, fdFileCType :: Text -- ^ MIME type for the file
177
- , fdPayload :: MultipartResult options
177
+ , fdPayload :: MultipartResult tag
178
178
-- ^ path to the temporary file that has the
179
179
-- content of the user's original file. Only
180
180
-- valid during the execution of your handler as
181
181
-- it gets removed right after, which means you
182
182
-- really want to move or copy it in your handler.
183
183
}
184
184
185
- deriving instance Eq (MultipartResult options ) => Eq (FileData options )
186
- deriving instance Show (MultipartResult options ) => Show (FileData options )
185
+ deriving instance Eq (MultipartResult tag ) => Eq (FileData tag )
186
+ deriving instance Show (MultipartResult tag ) => Show (FileData tag )
187
187
188
188
-- | Lookup a file input with the given @name@ attribute.
189
- lookupFile :: Text -> MultipartData options -> Maybe (FileData options )
189
+ lookupFile :: Text -> MultipartData tag -> Maybe (FileData tag )
190
190
lookupFile iname = find ((== iname) . fdInputName) . files
191
191
192
192
-- | Representation for a textual input (any @\<input\>@ type but @file@).
@@ -198,7 +198,7 @@ data Input = Input
198
198
} deriving (Eq , Show )
199
199
200
200
-- | Lookup a textual input with the given @name@ attribute.
201
- lookupInput :: Text -> MultipartData options -> Maybe Text
201
+ lookupInput :: Text -> MultipartData tag -> Maybe Text
202
202
lookupInput iname = fmap iValue . find ((== iname) . iName) . inputs
203
203
204
204
-- | 'MultipartData' is the type representing
@@ -218,63 +218,64 @@ lookupInput iname = fmap iValue . find ((==iname) . iName) . inputs
218
218
-- User \<$\> lookupInput "username" (inputs form)
219
219
-- \<*\> fmap fdFilePath (lookupFile "pic" $ files form)
220
220
-- @
221
- class FromMultipart options a where
221
+ class FromMultipart tag a where
222
222
-- | Given a value of type 'MultipartData', which consists
223
223
-- in a list of textual inputs and another list for
224
224
-- files, try to extract a value of type @a@. When
225
225
-- extraction fails, servant errors out with status code 400.
226
- fromMultipart :: MultipartData options -> Maybe a
226
+ fromMultipart :: MultipartData tag -> Maybe a
227
227
228
- instance FromMultipart options (MultipartData options ) where
228
+ instance FromMultipart tag (MultipartData tag ) where
229
229
fromMultipart = Just
230
230
231
231
-- | Upon seeing @MultipartForm a :> ...@ in an API type,
232
232
--- servant-server will hand a value of type @a@ to your handler
233
233
-- assuming the request body's content type is
234
234
-- @multipart/form-data@ and the call to 'fromMultipart' succeeds.
235
- instance ( FromMultipart options a
236
- , MultipartBackend options
237
- , LookupContext config (MultipartOptions options )
235
+ instance ( FromMultipart tag a
236
+ , MultipartBackend tag
237
+ , LookupContext config (MultipartOptions tag )
238
238
, HasServer sublayout config )
239
- => HasServer (MultipartForm options a :> sublayout ) config where
239
+ => HasServer (MultipartForm tag a :> sublayout ) config where
240
240
241
- type ServerT (MultipartForm options a :> sublayout ) m =
241
+ type ServerT (MultipartForm tag a :> sublayout ) m =
242
242
a -> ServerT sublayout m
243
243
244
244
route Proxy config subserver =
245
245
route psub config subserver'
246
246
where
247
247
psub = Proxy :: Proxy sublayout
248
248
pbak = Proxy :: Proxy b
249
- popts = Proxy :: Proxy (MultipartOptions options )
250
- multipartOpts = fromMaybe defaultMultipartOptions
249
+ popts = Proxy :: Proxy (MultipartOptions tag )
250
+ multipartOpts = fromMaybe ( defaultMultipartOptions pbak)
251
251
$ lookupContext popts config
252
- subserver' = addMultipartHandling multipartOpts subserver
252
+ subserver' = addMultipartHandling pbak multipartOpts subserver
253
253
254
254
-- Try and extract the request body as multipart/form-data,
255
255
-- returning the data as well as the resourcet InternalState
256
256
-- that allows us to properly clean up the temporary files
257
257
-- later on.
258
- check :: MultipartBackend options => MultipartOptions options -> DelayedIO (MultipartData options )
259
- check opts = withRequest $ \ request -> do
258
+ check :: MultipartBackend tag => Proxy tag -> MultipartOptions tag -> DelayedIO (MultipartData tag )
259
+ check prx tag = withRequest $ \ request -> do
260
260
st <- liftResourceT getInternalState
261
- rawData <- liftIO $ parseRequestBodyEx parseOpts (backend (options opts ) st) request
261
+ rawData <- liftIO $ parseRequestBodyEx parseOpts (backend prx (options tag ) st) request
262
262
return (fromRaw rawData)
263
- where parseOpts = generalOptions opts
263
+ where parseOpts = generalOptions tag
264
264
265
265
-- Add multipart extraction support to a Delayed.
266
- addMultipartHandling :: forall options multipart env a . (FromMultipart options multipart , MultipartBackend options )
267
- => MultipartOptions options
266
+ addMultipartHandling :: forall tag multipart env a . (FromMultipart tag multipart , MultipartBackend tag )
267
+ => Proxy tag
268
+ -> MultipartOptions tag
268
269
-> Delayed env (multipart -> a )
269
270
-> Delayed env a
270
- addMultipartHandling opts subserver =
271
+ addMultipartHandling prx opts subserver =
271
272
addBodyCheck subserver contentCheck bodyCheck
272
273
where
273
274
contentCheck = withRequest $ \ request ->
274
275
fuzzyMultipartCTCheck (contentTypeH request)
275
276
276
277
bodyCheck () = do
277
- mpd <- check opts :: DelayedIO (MultipartData options )
278
+ mpd <- check prx opts :: DelayedIO (MultipartData tag )
278
279
case fromMultipart mpd of
279
280
Nothing -> liftRouteResult $ FailFatal
280
281
err400 { errBody = " fromMultipart returned Nothing" }
@@ -308,36 +309,46 @@ fuzzyMultipartCTCheck ct
308
309
-- the temporary file backend. See haddocks for
309
310
-- 'ParseRequestBodyOptions' and 'TmpBackendOptions' respectively
310
311
-- for more information on what you can tweak.
311
- data MultipartOptions options = MultipartOptions
312
+ data MultipartOptions tag = MultipartOptions
312
313
{ generalOptions :: ParseRequestBodyOptions
313
- , options :: options
314
+ , options :: MultipartBackendOptions tag
314
315
}
315
316
316
- class MultipartBackend options where
317
- type MultipartResult options :: *
317
+ class MultipartBackend tag where
318
+ type MultipartResult tag :: *
319
+ type MultipartBackendOptions tag :: *
318
320
319
- backend :: options
321
+ backend :: Proxy tag
322
+ -> MultipartBackendOptions tag
320
323
-> InternalState
321
324
-> ignored1
322
325
-> ignored2
323
326
-> IO SBS. ByteString
324
- -> IO (MultipartResult options )
327
+ -> IO (MultipartResult tag )
325
328
326
- defaultBackendOptions :: options
329
+ defaultBackendOptions :: Proxy tag -> MultipartBackendOptions tag
327
330
328
- instance MultipartBackend TmpBackendOptions where
329
- type MultipartResult TmpBackendOptions = FilePath
331
+ -- | Tag for temporary files
332
+ data Tmp
330
333
331
- defaultBackendOptions = defaultTmpBackendOptions
332
- backend opts = tmpBackend
334
+ -- | Tag for items in memory
335
+ data Mem
336
+
337
+ instance MultipartBackend Tmp where
338
+ type MultipartResult Tmp = FilePath
339
+ type MultipartBackendOptions Tmp = TmpBackendOptions
340
+
341
+ defaultBackendOptions _ = defaultTmpBackendOptions
342
+ backend _ opts = tmpBackend
333
343
where
334
344
tmpBackend = tempFileBackEndOpts (getTmpDir opts) (filenamePat opts)
335
345
336
- instance MultipartBackend LbsBackendOptions where
337
- type MultipartResult LbsBackendOptions = LBS. ByteString
346
+ instance MultipartBackend Mem where
347
+ type MultipartResult Mem = LBS. ByteString
348
+ type MultipartBackendOptions Mem = LbsBackendOptions
338
349
339
- defaultBackendOptions = LbsBackendOptions
340
- backend opts _ = lbsBackEnd
350
+ defaultBackendOptions _ = LbsBackendOptions
351
+ backend _ opts _ = lbsBackEnd
341
352
342
353
-- | TODO: write me
343
354
data LbsBackendOptions = LbsBackendOptions
@@ -366,10 +377,10 @@ defaultTmpBackendOptions = TmpBackendOptions
366
377
--
367
378
-- Uses 'defaultParseRequestBodyOptions' and
368
379
-- 'defaultTmpBackendOptions' respectively.
369
- defaultMultipartOptions :: MultipartBackend options => MultipartOptions options
370
- defaultMultipartOptions = MultipartOptions
380
+ defaultMultipartOptions :: MultipartBackend tag => Proxy tag -> MultipartOptions tag
381
+ defaultMultipartOptions prx = MultipartOptions
371
382
{ generalOptions = defaultParseRequestBodyOptions
372
- , options = defaultBackendOptions
383
+ , options = defaultBackendOptions prx
373
384
}
374
385
375
386
-- Utility class that's like HasContextEntry
0 commit comments