Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Marco Zocca committed Dec 29, 2023
1 parent f646638 commit d0ab65b
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 11 deletions.
16 changes: 15 additions & 1 deletion Web/Scotty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ module Web.Scotty
, capture, regex, function, literal
-- ** Accessing the Request and its fields
, request, header, headers, body, bodyReader
, jsonData, files
, jsonData
-- ** Accessing Path, Form and Query Parameters
, param, params
, pathParam, captureParam, formParam, queryParam
, pathParamMaybe, captureParamMaybe, formParamMaybe, queryParamMaybe
, pathParams, captureParams, formParams, queryParams
-- *** Files
, files, filesOpts, Trans.ParseRequestBodyOptions, Trans.defaultParseRequestBodyOptions
-- ** Modifying the Response and Redirecting
, status, addHeader, setHeader, redirect
-- ** Setting Response Body
Expand Down Expand Up @@ -234,6 +236,14 @@ request = Trans.request
files :: ActionM [File ByteString]
files = Trans.files

-- | Get list of temp files and form parameters decoded from multipart payloads.
--
-- NB the temp files are deleted when the continuation exits
filesOpts :: Trans.ParseRequestBodyOptions
-> ([Param] -> [File FilePath] -> ActionM a) -- ^ temp files validation, storage etc
-> ActionM a
filesOpts = Trans.filesOpts

-- | Get a request header. Header name is case-insensitive.
header :: Text -> ActionM (Maybe Text)
header = Trans.header
Expand All @@ -243,6 +253,8 @@ headers :: ActionM [(Text, Text)]
headers = Trans.headers

-- | Get the request body.
--
-- NB: loads the entire request body in memory
body :: ActionM ByteString
body = Trans.body

Expand All @@ -253,6 +265,8 @@ bodyReader :: ActionM (IO BS.ByteString)
bodyReader = Trans.bodyReader

-- | Parse the request body as a JSON object and return it. Raises an exception if parse is unsuccessful.
--
-- NB: uses 'body' internally
jsonData :: FromJSON a => ActionM a
jsonData = Trans.jsonData

Expand Down
19 changes: 14 additions & 5 deletions Web/Scotty/Action.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ module Web.Scotty.Action
, file
, rawResponse
, files
-- , filesTemp
, filesOpts
, ParseRequestBodyOptions, defaultParseRequestBodyOptions
, finish
, header
, headers
Expand Down Expand Up @@ -72,6 +73,7 @@ import Control.Monad.Trans.Resource (InternalState, withInternalState, runResour
import Control.Concurrent.MVar

import qualified Data.Aeson as A
import Data.Bifunctor (bimap, first)
import Data.Bool (bool)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as BL
Expand All @@ -98,7 +100,7 @@ import qualified Network.Wai.Parse as W (File, Param, getRequestBodyType, BackEn
import Numeric.Natural

import Web.Scotty.Internal.Types
import Web.Scotty.Internal.WaiParseSafe (ParseRequestBodyOptions, RequestParseException(..), parseRequestBodyEx)
import Web.Scotty.Internal.WaiParseSafe (ParseRequestBodyOptions, defaultParseRequestBodyOptions, RequestParseException(..), parseRequestBodyEx)

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.6.3

The import of ‘RequestParseException’

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 8.10.7

The import of ‘RequestParseException’

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.2.8

The import of ‘RequestParseException’

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.0.2

The import of ‘RequestParseException’

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.4.6

The import of ‘RequestParseException’

Check warning on line 103 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.6.2

The import of ‘RequestParseException’
import Web.Scotty.Util (mkResponse, addIfNotPresent, add, replace, lazyTextToStrictByteString, decodeUtf8Lenient)

Check warning on line 104 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.6.3

The import of ‘decodeUtf8Lenient’

Check warning on line 104 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.4.6

The import of ‘decodeUtf8Lenient’

Check warning on line 104 in Web/Scotty/Action.hs

View workflow job for this annotation

GitHub Actions / ubuntu-latest / ghc 9.6.2

The import of ‘decodeUtf8Lenient’
import UnliftIO.Exception (Handler(..), catch, catches, throwIO)

Expand Down Expand Up @@ -284,13 +286,16 @@ files = ActionT $ envFiles <$> ask
-- NB the temp files are deleted when the continuation exits
filesOpts :: MonadUnliftIO m =>
ParseRequestBodyOptions
-> (([W.Param], [W.File FilePath]) -> ActionT m b) -- ^ temp files validation, storage etc
-> ([Param] -> [File FilePath] -> ActionT m b) -- ^ temp files validation, storage etc
-> ActionT m b
filesOpts prbo io = do
req <- getRequest
runResourceT $ withInternalState $ \istate -> do
out <- liftIO $ sinkTempFiles istate prbo req
io out
(ps, fs) <- liftIO $ sinkTempFiles istate prbo req
let
params' = bimap decodeUtf8Lenient decodeUtf8Lenient <$> ps
files' = first decodeUtf8Lenient <$> fs
io params' files'

sinkTempFiles :: InternalState
-> ParseRequestBodyOptions
Expand All @@ -315,6 +320,8 @@ headers = do
| (k,v) <- hs ]

-- | Get the request body.
--
-- NB This loads the whole request body in memory at once.
body :: (MonadIO m) => ActionT m BL.ByteString
body = ActionT ask >>= (liftIO . envBody)

Expand All @@ -333,6 +340,8 @@ bodyReader = ActionT $ envBodyChunk <$> ask
-- 422 Unprocessable Entity.
--
-- These status codes are as per https://www.restapitutorial.com/httpstatuscodes.html.
--
-- NB : Internally this uses 'body'.
jsonData :: (A.FromJSON a, MonadIO m) => ActionT m a
jsonData = do
b <- body
Expand Down
5 changes: 4 additions & 1 deletion Web/Scotty/Trans.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ module Web.Scotty.Trans
, capture, regex, function, literal
-- ** Accessing the Request and its fields
, request, Lazy.header, Lazy.headers, body, bodyReader
, jsonData, files
, jsonData

-- ** Accessing Path, Form and Query Parameters
, param, params
, pathParam, captureParam, formParam, queryParam
, pathParamMaybe, captureParamMaybe, formParamMaybe, queryParamMaybe
, pathParams, captureParams, formParams, queryParams
-- *** Files
, files, filesOpts, ParseRequestBodyOptions, defaultParseRequestBodyOptions
-- ** Modifying the Response and Redirecting
, status, Lazy.addHeader, Lazy.setHeader, Lazy.redirect
-- ** Setting Response Body
Expand Down
13 changes: 9 additions & 4 deletions examples/upload.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ module Main (main) where
import Web.Scotty

import Control.Monad.IO.Class
import Data.Foldable (for_)
import qualified Data.Text.Lazy as TL

import Network.Wai.Middleware.RequestLogger
import Network.Wai.Middleware.Static
import Network.Wai.Parse
import Network.Wai.Parse (fileName, fileContent)

import qualified Text.Blaze.Html5 as H
import Text.Blaze.Html5.Attributes
Expand All @@ -35,10 +36,14 @@ main = scotty 3000 $ do
H.input H.! type_ "submit"

post "/upload" $ do
fs <- files
let fs' = [ (fieldName, BS.unpack (fileName fi), fileContent fi) | (fieldName,fi) <- fs ]
filesOpts defaultParseRequestBodyOptions $ \_ fs -> do
let
-- fs' = [ (fieldName, BS.unpack (fileName fi), fileContent fi) | (fieldName,fi) <- fs ]
fpaths = [fileContent fi | (_, fi) <- fs]
-- write the files to disk, so they will be served by the static middleware
liftIO $ sequence_ [ B.writeFile ("uploads" </> fn) fc | (_,fn,fc) <- fs' ]
-- liftIO $ sequence_ [ B.writeFile ("uploads" </> fn) fc | (_,fn,fc) <- fs' ]
for_ fpaths $ \fpath -> do
fc <- B.readFile fpath
-- generate list of links to the files just uploaded
html $ mconcat [ mconcat [ TL.fromStrict fName
, ": "
Expand Down

0 comments on commit d0ab65b

Please sign in to comment.