@@ -15,13 +15,16 @@ module Servant.Server.Internal
1515  , module  Servant.Server.Internal.ServerError 
1616  ) where 
1717
18+ import            Control.Applicative  ((<|>) )
1819import            Control.Monad 
1920                 (join , when , unless )
2021import            Control.Monad.Trans 
2122                 (liftIO , lift )
2223import            Control.Monad.Trans.Resource 
2324                 (runResourceT , ReleaseKey )
2425import            Data.Acquire 
26+ 
27+ import  Data.Bifunctor  (first )
2528import  qualified  Data.ByteString                             as  B 
2629import  qualified  Data.ByteString.Builder                     as  BB 
2730import  qualified  Data.ByteString.Char8                       as  BC8 
@@ -47,8 +50,8 @@ import           Network.HTTP.Types                         hiding
4750import            Network.Socket 
4851                 (SockAddr )
4952import            Network.Wai 
50-                  (Application , Request , Response , ResponseReceived , httpVersion , isSecure , lazyRequestBody ,
51-                  queryString , remoteHost , getRequestBodyChunk , requestHeaders , requestHeaderHost ,
53+                  (Application , Request , Response , ResponseReceived , RequestBodyLength  ( .. ),  httpVersion , isSecure , lazyRequestBody ,
54+                  queryString , remoteHost , getRequestBodyChunk , requestBodyLength ,  requestHeaders , requestHeaderHost ,
5255                 requestMethod , responseLBS , responseStream , vault )
5356import            Servant.API 
5457                 ((:<|>)  (.. ), (:>) , Accept  (.. ), BasicAuth , Capture' ,
@@ -802,12 +805,13 @@ instance HasServer RawM context where
802805--  > server = postBook 
803806--  >   where postBook :: Book -> Handler Book 
804807--  >         postBook book = ...insert into your db... 
805- instance  ( AllCTUnrender  list  a , HasServer  api  context , SBoolI  (FoldLenient  mods )
808+ instance  ( AllCTUnrender  list  a , HasServer  api  context 
809+          , SBoolI  (FoldRequired  mods ), SBoolI  (FoldLenient  mods )
806810         , HasContextEntry  (MkContextWithErrorFormatter  context ) ErrorFormatters 
807811         ) =>  HasServer  (ReqBody'  mods  list  a  :>  api ) context  where 
808812
809813  type  ServerT  (ReqBody'  mods  list  a  :>  api ) m  = 
810-     If  ( FoldLenient   mods ) ( Either   String   a )  a  ->  ServerT  api  m 
814+     RequestArgument   mods  a  ->  ServerT  api  m 
811815
812816  hoistServerWithContext _ pc nt s =  hoistServerWithContext (Proxy  ::  Proxy  api ) pc nt .  s
813817
@@ -819,25 +823,44 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
819823      formatError =  bodyParserErrorFormatter $  getContextEntry (mkContextWithErrorFormatter context)
820824
821825      --  Content-Type check, we only lookup we can try to parse the request body
822-       ctCheck =  withRequest $  \  request ->   do 
826+       ctCheck =  withRequest $  \  request -> 
823827        --  See HTTP RFC 2616, section 7.2.1
824828        --  http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
825829        --  See also "W3C Internet Media Type registration, consistency of use"
826830        --  http://www.w3.org/2001/tag/2002/0129-mime
827-         let  contentTypeH =  fromMaybe " application/octet-stream" 
828-                          $  lookup  hContentType $  requestHeaders request
829-         case  canHandleCTypeH (Proxy  ::  Proxy  list ) (BSL. fromStrict contentTypeH) ::  Maybe BSL. ByteString->  Either String a ) of 
830-           Nothing  ->  delayedFail err415
831-           Just  f  ->  return  f
832- 
833-       --  Body check, we get a body parsing functions as the first argument.
834-       bodyCheck f =  withRequest $  \  request ->  do 
835-         mrqbody <-  f <$>  liftIO (lazyRequestBody request)
836-         case  sbool ::  SBool  (FoldLenient  mods ) of 
837-           STrue  ->  return  mrqbody
838-           SFalse  ->  case  mrqbody of 
839-             Left ->  delayedFailFatal $  formatError rep request e
840-             Right ->  return  v
831+         let  contentTypeHMaybe =  lookup  hContentType $  requestHeaders request
832+             contentTypeH =  fromMaybe " application/octet-stream" 
833+             canHandleContentTypeH  ::  Maybe BSL. ByteString->  Either String a )
834+             canHandleContentTypeH =  canHandleCTypeH (Proxy  ::  Proxy  list ) (BSL. fromStrict contentTypeH)
835+ 
836+           --  In case ReqBody' is Optional and neither request body nor Content-Type header was provided.
837+             noOptionalReqBody = 
838+               case  (sbool ::  SBool  (FoldRequired  mods ), contentTypeHMaybe , requestBodyLength  request ) of 
839+                   (SFalse , Nothing , KnownLength  0 ) ->  Just  .  const  $  Left " This value does not matter (it is ignored)" 
840+                   _ ->  Nothing 
841+         in 
842+           case  canHandleContentTypeH <|>  noOptionalReqBody of 
843+             Nothing  ->  delayedFail err415
844+             Just  f  ->  return  f
845+ 
846+       bodyCheck f =  withRequest $  \  request -> 
847+         let 
848+           hasReqBody = 
849+             case  requestBodyLength request of 
850+               KnownLength  0  ->  False 
851+               _             ->  True 
852+ 
853+           serverErr  ::  String ->  ServerError 
854+           serverErr =  formatError rep request
855+         in 
856+           fmap  f (liftIO $  lazyRequestBody request) >>= 
857+             case  (sbool ::  SBool  (FoldRequired  mods ), sbool  ::  SBool  (FoldLenient  mods ), hasReqBody ) of 
858+               (STrue ,  STrue ,  _)     ->  return  .  first T. pack
859+               (STrue ,  SFalse , _)     ->  either  (delayedFailFatal .  serverErr) return 
860+               (SFalse , STrue ,  False ->  return  .  either  (const  Nothing ) (Just  .  Right 
861+               (SFalse , SFalse , False ->  return  .  either  (const  Nothing ) Just 
862+               (SFalse , STrue ,  True ->  return  .  Just  .  first T. pack
863+               (SFalse , SFalse , True ->  either  (delayedFailFatal .  serverErr) (return  .  Just )
841864
842865instance 
843866    ( FramingUnrender  framing , FromSourceIO  chunk  a , MimeUnrender  ctype  chunk 
0 commit comments