-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Marco Zocca
committed
Feb 27, 2024
1 parent
85417b2
commit 4ba17f6
Showing
1 changed file
with
65 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
-- | This should be in 'hspec-wai', PR pending as of Feb 2024 : https://github.com/hspec/hspec-wai/pull/77 | ||
|
||
{-# language OverloadedStrings #-} | ||
module Test.Hspec.Wai.Extra (postMultipartForm, FileMeta(..)) where | ||
|
||
import qualified Data.Char as Char | ||
import Data.List (intersperse) | ||
|
||
import Data.ByteString (ByteString) | ||
import qualified Data.ByteString.Builder as Builder | ||
import qualified Data.ByteString.Lazy as LB | ||
|
||
import Data.Word (Word8) | ||
|
||
import Network.HTTP.Types (methodPost, hContentType) | ||
import Network.Wai.Test (SResponse) | ||
|
||
import Test.Hspec.Wai (request) | ||
import Test.Hspec.Wai.Internal (WaiSession) | ||
|
||
-- | @POST@ a @multipart/form-data@ form which might include files. | ||
-- | ||
-- The @Content-Type@ is set to @multipart/form-data; boundary=<bd>@ where @bd@ is the part separator without the @--@ prefix. | ||
postMultipartForm :: ByteString -- ^ path | ||
-> ByteString -- ^ part separator without any dashes | ||
-> [(FileMeta, ByteString, ByteString, ByteString)] -- ^ (file metadata, field MIME type, field name, field contents) | ||
-> WaiSession st SResponse | ||
postMultipartForm path sbs = | ||
request methodPost path [(hContentType, "multipart/form-data; boundary=" <> sbs)] . formMultipartQuery sbs | ||
|
||
-- | Encode the body of a multipart form post | ||
-- | ||
-- schema from : https://swagger.io/docs/specification/describing-request-body/multipart-requests/ | ||
formMultipartQuery :: ByteString -- ^ part separator without any dashes | ||
-> [(FileMeta, ByteString, ByteString, ByteString)] -- ^ (file metadata, field MIME type, field name, field contents) | ||
-> LB.ByteString | ||
formMultipartQuery sbs = Builder.toLazyByteString . mconcat . (preamble :) . intersperse newline . encodeAll | ||
where | ||
preamble = | ||
kv "Content-Type" ("multipart/form-data; boundary=" <> Builder.byteString sbs <> newline <> newline) | ||
encodeAll fs = map encodeFile fs <> [sepEnd] | ||
encodeFile (fieldMeta, ty, n, payload) = mconcat $ [ | ||
sep | ||
, newline | ||
, kv "Content-Disposition" ("form-data;" <> " name=" <> quoted n <> encodeMPField fieldMeta) | ||
, newline | ||
, kv "Content-Type" (Builder.byteString ty) | ||
, newline, newline | ||
, Builder.byteString payload | ||
] | ||
sep = Builder.byteString ("--" <> sbs) | ||
sepEnd = Builder.byteString ("--" <> sbs <> "--") | ||
encodeMPField FMFormField = mempty | ||
encodeMPField (FMFile fname) = "; filename=" <> quoted fname | ||
quoted x = Builder.byteString ("\"" <> x <> "\"") | ||
kv k v = k <> ": " <> v | ||
newline = Builder.word8 (ord '\n') | ||
|
||
|
||
data FileMeta = FMFormField -- ^ any form field except a file | ||
| FMFile ByteString -- ^ file name | ||
|
||
|
||
ord :: Char -> Word8 | ||
ord = fromIntegral . Char.ord |