@@ -15,13 +15,16 @@ module Servant.Server.Internal
15
15
, module Servant.Server.Internal.ServerError
16
16
) where
17
17
18
+ import Control.Applicative ((<|>) )
18
19
import Control.Monad
19
20
(join , when , unless )
20
21
import Control.Monad.Trans
21
22
(liftIO , lift )
22
23
import Control.Monad.Trans.Resource
23
24
(runResourceT , ReleaseKey )
24
25
import Data.Acquire
26
+
27
+ import Data.Bifunctor (first )
25
28
import qualified Data.ByteString as B
26
29
import qualified Data.ByteString.Builder as BB
27
30
import qualified Data.ByteString.Char8 as BC8
@@ -47,8 +50,8 @@ import Network.HTTP.Types hiding
47
50
import Network.Socket
48
51
(SockAddr )
49
52
import 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 ,
52
55
requestMethod , responseLBS , responseStream , vault )
53
56
import Servant.API
54
57
((:<|>) (.. ), (:>) , Accept (.. ), BasicAuth , Capture' ,
@@ -802,12 +805,13 @@ instance HasServer RawM context where
802
805
-- > server = postBook
803
806
-- > where postBook :: Book -> Handler Book
804
807
-- > 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 )
806
810
, HasContextEntry (MkContextWithErrorFormatter context ) ErrorFormatters
807
811
) => HasServer (ReqBody' mods list a :> api ) context where
808
812
809
813
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
811
815
812
816
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api ) pc nt . s
813
817
@@ -819,25 +823,44 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
819
823
formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
820
824
821
825
-- Content-Type check, we only lookup we can try to parse the request body
822
- ctCheck = withRequest $ \ request -> do
826
+ ctCheck = withRequest $ \ request ->
823
827
-- See HTTP RFC 2616, section 7.2.1
824
828
-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
825
829
-- See also "W3C Internet Media Type registration, consistency of use"
826
830
-- 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 )
841
864
842
865
instance
843
866
( FramingUnrender framing , FromSourceIO chunk a , MimeUnrender ctype chunk
0 commit comments