Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f873add

Browse files
unclechudalpdRelex
authored andcommittedNov 29, 2022
Fix Optional ReqBody'
See haskell-servant#1346
1 parent 8f081bd commit f873add

File tree

1 file changed

+51
-20
lines changed

1 file changed

+51
-20
lines changed
 

‎servant-server/src/Servant/Server/Internal.hs

+51-20
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,25 @@ module Servant.Server.Internal
3232
, module Servant.Server.Internal.ServerError
3333
) where
3434

35+
import Control.Applicative
36+
((<|>))
3537
import Control.Monad
3638
(join, when)
3739
import Control.Monad.Trans
3840
(liftIO)
3941
import Control.Monad.Trans.Resource
4042
(runResourceT)
43+
import Data.Bifunctor
44+
(bimap)
4145
import qualified Data.ByteString as B
4246
import qualified Data.ByteString.Builder as BB
4347
import qualified Data.ByteString.Char8 as BC8
4448
import qualified Data.ByteString.Lazy as BL
4549
import Data.Constraint (Constraint, Dict(..))
4650
import Data.Either
4751
(partitionEithers)
52+
import Data.Function
53+
((&))
4854
import Data.Maybe
4955
(fromMaybe, isNothing, mapMaybe, maybeToList)
5056
import Data.String
@@ -64,9 +70,10 @@ import Network.HTTP.Types hiding
6470
import Network.Socket
6571
(SockAddr)
6672
import Network.Wai
67-
(Application, Request, httpVersion, isSecure, lazyRequestBody,
68-
queryString, remoteHost, getRequestBodyChunk, requestHeaders,
69-
requestMethod, responseLBS, responseStream, vault)
73+
(Application, Request, RequestBodyLength (KnownLength), httpVersion,
74+
isSecure, lazyRequestBody, queryString, remoteHost, requestBodyLength,
75+
getRequestBodyChunk, requestHeaders, requestMethod, responseLBS,
76+
responseStream, vault)
7077
import Prelude ()
7178
import Prelude.Compat
7279
import Servant.API
@@ -632,12 +639,13 @@ instance HasServer Raw context where
632639
-- > server = postBook
633640
-- > where postBook :: Book -> Handler Book
634641
-- > postBook book = ...insert into your db...
635-
instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods)
642+
instance ( AllCTUnrender list a, HasServer api context
643+
, SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)
636644
, HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
637645
) => HasServer (ReqBody' mods list a :> api) context where
638646

639647
type ServerT (ReqBody' mods list a :> api) m =
640-
If (FoldLenient mods) (Either String a) a -> ServerT api m
648+
RequestArgument mods a -> ServerT api m
641649

642650
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
643651

@@ -649,25 +657,48 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
649657
formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
650658

651659
-- Content-Type check, we only lookup we can try to parse the request body
652-
ctCheck = withRequest $ \ request -> do
653-
-- See HTTP RFC 2616, section 7.2.1
654-
-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
655-
-- See also "W3C Internet Media Type registration, consistency of use"
656-
-- http://www.w3.org/2001/tag/2002/0129-mime
657-
let contentTypeH = fromMaybe "application/octet-stream"
658-
$ lookup hContentType $ requestHeaders request
659-
case canHandleCTypeH (Proxy :: Proxy list) (cs contentTypeH) :: Maybe (BL.ByteString -> Either String a) of
660-
Nothing -> delayedFail err415
661-
Just f -> return f
660+
ctCheck = withRequest $ \ request ->
661+
let
662+
contentTypeH = lookup hContentType $ requestHeaders request
663+
664+
-- See HTTP RFC 2616, section 7.2.1
665+
-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
666+
-- See also "W3C Internet Media Type registration, consistency of use"
667+
-- http://www.w3.org/2001/tag/2002/0129-mime
668+
contentTypeH' = fromMaybe "application/octet-stream" contentTypeH
669+
670+
canHandleContentTypeH :: Maybe (BL.ByteString -> Either String a)
671+
canHandleContentTypeH = canHandleCTypeH (Proxy :: Proxy list) (cs contentTypeH')
672+
673+
-- In case ReqBody' is Optional and neither request body nor Content-Type header was provided.
674+
noOptionalReqBody =
675+
case (sbool :: SBool (FoldRequired mods), contentTypeH, requestBodyLength request) of
676+
(SFalse, Nothing, KnownLength 0) -> Just . const $ Left "This value does not matter (it is ignored)"
677+
_ -> Nothing
678+
in
679+
case canHandleContentTypeH <|> noOptionalReqBody of
680+
Nothing -> delayedFail err415
681+
Just f -> return f
662682

663683
-- Body check, we get a body parsing functions as the first argument.
664684
bodyCheck f = withRequest $ \ request -> do
665685
mrqbody <- f <$> liftIO (lazyRequestBody request)
666-
case sbool :: SBool (FoldLenient mods) of
667-
STrue -> return mrqbody
668-
SFalse -> case mrqbody of
669-
Left e -> delayedFailFatal $ formatError rep request e
670-
Right v -> return v
686+
687+
let
688+
hasReqBody =
689+
case requestBodyLength request of
690+
KnownLength 0 -> False
691+
_ -> True
692+
693+
serverErr :: String -> ServerError
694+
serverErr = formatError rep request . cs
695+
696+
mrqbody & case (sbool :: SBool (FoldRequired mods), sbool :: SBool (FoldLenient mods), hasReqBody) of
697+
(STrue, STrue, _) -> return . bimap cs id
698+
(STrue, SFalse, _) -> either (delayedFailFatal . serverErr) return
699+
(SFalse, _, False) -> return . const Nothing
700+
(SFalse, STrue, True) -> return . Just . bimap cs id
701+
(SFalse, SFalse, True) -> either (delayedFailFatal . serverErr) (return . Just)
671702

672703
instance
673704
( FramingUnrender framing, FromSourceIO chunk a, MimeUnrender ctype chunk

0 commit comments

Comments
 (0)
Please sign in to comment.