@@ -32,19 +32,25 @@ module Servant.Server.Internal
32
32
, module Servant.Server.Internal.ServerError
33
33
) where
34
34
35
+ import Control.Applicative
36
+ ((<|>) )
35
37
import Control.Monad
36
38
(join , when )
37
39
import Control.Monad.Trans
38
40
(liftIO )
39
41
import Control.Monad.Trans.Resource
40
42
(runResourceT )
43
+ import Data.Bifunctor
44
+ (bimap )
41
45
import qualified Data.ByteString as B
42
46
import qualified Data.ByteString.Builder as BB
43
47
import qualified Data.ByteString.Char8 as BC8
44
48
import qualified Data.ByteString.Lazy as BL
45
49
import Data.Constraint (Constraint , Dict (.. ))
46
50
import Data.Either
47
51
(partitionEithers )
52
+ import Data.Function
53
+ ((&) )
48
54
import Data.Maybe
49
55
(fromMaybe , isNothing , mapMaybe , maybeToList )
50
56
import Data.String
@@ -64,9 +70,10 @@ import Network.HTTP.Types hiding
64
70
import Network.Socket
65
71
(SockAddr )
66
72
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 )
70
77
import Prelude ()
71
78
import Prelude.Compat
72
79
import Servant.API
@@ -632,12 +639,13 @@ instance HasServer Raw context where
632
639
-- > server = postBook
633
640
-- > where postBook :: Book -> Handler Book
634
641
-- > 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 )
636
644
, HasContextEntry (MkContextWithErrorFormatter context ) ErrorFormatters
637
645
) => HasServer (ReqBody' mods list a :> api ) context where
638
646
639
647
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
641
649
642
650
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api ) pc nt . s
643
651
@@ -649,25 +657,48 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
649
657
formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
650
658
651
659
-- 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
662
682
663
683
-- Body check, we get a body parsing functions as the first argument.
664
684
bodyCheck f = withRequest $ \ request -> do
665
685
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 )
671
702
672
703
instance
673
704
( FramingUnrender framing , FromSourceIO chunk a , MimeUnrender ctype chunk
0 commit comments