@@ -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 e -> delayedFailFatal $ formatError rep request e
840- Right v -> return v
831+ let contentTypeHMaybe = lookup hContentType $ requestHeaders request
832+ contentTypeH = fromMaybe " application/octet-stream" contentTypeHMaybe
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