Fix Optional ReqBody'
See https://github.com/haskell-servant/servant/issues/1346
This commit is contained in:
parent
8f081bd9ad
commit
a6be2ee728
|
@ -32,12 +32,16 @@ module Servant.Server.Internal
|
||||||
, module Servant.Server.Internal.ServerError
|
, module Servant.Server.Internal.ServerError
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
((<|>))
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
(join, when)
|
(join, when)
|
||||||
import Control.Monad.Trans
|
import Control.Monad.Trans
|
||||||
(liftIO)
|
(liftIO)
|
||||||
import Control.Monad.Trans.Resource
|
import Control.Monad.Trans.Resource
|
||||||
(runResourceT)
|
(runResourceT)
|
||||||
|
import Data.Bifunctor
|
||||||
|
(bimap)
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.ByteString.Builder as BB
|
import qualified Data.ByteString.Builder as BB
|
||||||
import qualified Data.ByteString.Char8 as BC8
|
import qualified Data.ByteString.Char8 as BC8
|
||||||
|
@ -45,6 +49,8 @@ import qualified Data.ByteString.Lazy as BL
|
||||||
import Data.Constraint (Constraint, Dict(..))
|
import Data.Constraint (Constraint, Dict(..))
|
||||||
import Data.Either
|
import Data.Either
|
||||||
(partitionEithers)
|
(partitionEithers)
|
||||||
|
import Data.Function
|
||||||
|
((&))
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
(fromMaybe, isNothing, mapMaybe, maybeToList)
|
(fromMaybe, isNothing, mapMaybe, maybeToList)
|
||||||
import Data.String
|
import Data.String
|
||||||
|
@ -64,7 +70,8 @@ import Network.HTTP.Types hiding
|
||||||
import Network.Socket
|
import Network.Socket
|
||||||
(SockAddr)
|
(SockAddr)
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
(Application, Request, httpVersion, isSecure, lazyRequestBody,
|
(Application, Request, RequestBodyLength (KnownLength),
|
||||||
|
httpVersion, isSecure, lazyRequestBody,
|
||||||
queryString, remoteHost, getRequestBodyChunk, requestHeaders,
|
queryString, remoteHost, getRequestBodyChunk, requestHeaders,
|
||||||
requestMethod, responseLBS, responseStream, vault)
|
requestMethod, responseLBS, responseStream, vault)
|
||||||
import Prelude ()
|
import Prelude ()
|
||||||
|
@ -632,12 +639,13 @@ instance HasServer Raw context where
|
||||||
-- > server = postBook
|
-- > server = postBook
|
||||||
-- > where postBook :: Book -> Handler Book
|
-- > where postBook :: Book -> Handler Book
|
||||||
-- > postBook book = ...insert into your db...
|
-- > postBook book = ...insert into your db...
|
||||||
instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods)
|
instance ( AllCTUnrender list a, HasServer api context
|
||||||
|
, SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)
|
||||||
, HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
|
, HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
|
||||||
) => HasServer (ReqBody' mods list a :> api) context where
|
) => HasServer (ReqBody' mods list a :> api) context where
|
||||||
|
|
||||||
type ServerT (ReqBody' mods list a :> api) m =
|
type ServerT (ReqBody' mods list a :> api) m =
|
||||||
If (FoldLenient mods) (Either String a) a -> ServerT api m
|
RequestArgument mods a -> ServerT api m
|
||||||
|
|
||||||
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
|
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
|
||||||
|
|
||||||
|
@ -649,25 +657,48 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods
|
||||||
formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
|
formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
|
||||||
|
|
||||||
-- Content-Type check, we only lookup we can try to parse the request body
|
-- Content-Type check, we only lookup we can try to parse the request body
|
||||||
ctCheck = withRequest $ \ request -> do
|
ctCheck = withRequest $ \ request ->
|
||||||
-- See HTTP RFC 2616, section 7.2.1
|
let
|
||||||
-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
|
contentTypeH = lookup hContentType $ requestHeaders request
|
||||||
-- See also "W3C Internet Media Type registration, consistency of use"
|
|
||||||
-- http://www.w3.org/2001/tag/2002/0129-mime
|
-- See HTTP RFC 2616, section 7.2.1
|
||||||
let contentTypeH = fromMaybe "application/octet-stream"
|
-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
|
||||||
$ lookup hContentType $ requestHeaders request
|
-- See also "W3C Internet Media Type registration, consistency of use"
|
||||||
case canHandleCTypeH (Proxy :: Proxy list) (cs contentTypeH) :: Maybe (BL.ByteString -> Either String a) of
|
-- http://www.w3.org/2001/tag/2002/0129-mime
|
||||||
Nothing -> delayedFail err415
|
contentTypeH' = fromMaybe "application/octet-stream" contentTypeH
|
||||||
Just f -> return f
|
|
||||||
|
canHandleContentTypeH :: Maybe (BL.ByteString -> Either String a)
|
||||||
|
canHandleContentTypeH = canHandleCTypeH (Proxy :: Proxy list) (cs contentTypeH')
|
||||||
|
|
||||||
|
-- In case ReqBody' is Optional and neither request body nor Content-Type header was provided.
|
||||||
|
noOptionalReqBody =
|
||||||
|
case (sbool :: SBool (FoldRequired mods), contentTypeH, requestBodyLength request) of
|
||||||
|
(SFalse, Nothing, KnownLength 0) -> Just . const $ Left "This value does not matter (it is ignored)"
|
||||||
|
_ -> Nothing
|
||||||
|
in
|
||||||
|
case canHandleContentTypeH <|> noOptionalReqBody of
|
||||||
|
Nothing -> delayedFail err415
|
||||||
|
Just f -> return f
|
||||||
|
|
||||||
-- Body check, we get a body parsing functions as the first argument.
|
-- Body check, we get a body parsing functions as the first argument.
|
||||||
bodyCheck f = withRequest $ \ request -> do
|
bodyCheck f = withRequest $ \ request -> do
|
||||||
mrqbody <- f <$> liftIO (lazyRequestBody request)
|
mrqbody <- f <$> liftIO (lazyRequestBody request)
|
||||||
case sbool :: SBool (FoldLenient mods) of
|
|
||||||
STrue -> return mrqbody
|
let
|
||||||
SFalse -> case mrqbody of
|
hasReqBody =
|
||||||
Left e -> delayedFailFatal $ formatError rep request e
|
case requestBodyLength request of
|
||||||
Right v -> return v
|
KnownLength 0 -> False
|
||||||
|
_ -> True
|
||||||
|
|
||||||
|
serverErr :: String -> ServerError
|
||||||
|
serverErr = formatError rep request . cs
|
||||||
|
|
||||||
|
mrqbody & case (sbool :: SBool (FoldRequired mods), sbool :: SBool (FoldLenient mods), hasReqBody) of
|
||||||
|
(STrue, STrue, _) -> return . bimap cs id
|
||||||
|
(STrue, SFalse, _) -> either (delayedFailFatal . serverErr) return
|
||||||
|
(SFalse, _, False) -> return . const Nothing
|
||||||
|
(SFalse, STrue, True) -> return . Just . bimap cs id
|
||||||
|
(SFalse, SFalse, True) -> either (delayedFailFatal . serverErr) (return . Just)
|
||||||
|
|
||||||
instance
|
instance
|
||||||
( FramingUnrender framing, FromSourceIO chunk a, MimeUnrender ctype chunk
|
( FramingUnrender framing, FromSourceIO chunk a, MimeUnrender ctype chunk
|
||||||
|
@ -824,9 +855,9 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- Erroring instance for 'HasServer' when a combinator is not fully applied
|
-- Erroring instance for 'HasServer' when a combinator is not fully applied
|
||||||
instance TypeError (PartialApplication
|
instance TypeError (PartialApplication
|
||||||
#if __GLASGOW_HASKELL__ >= 904
|
#if __GLASGOW_HASKELL__ >= 904
|
||||||
@(Type -> [Type] -> Constraint)
|
@(Type -> [Type] -> Constraint)
|
||||||
#endif
|
#endif
|
||||||
HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
|
HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
|
||||||
where
|
where
|
||||||
|
@ -872,9 +903,9 @@ type HasServerArrowTypeError a b =
|
||||||
-- XXX: This omits the @context@ parameter, e.g.:
|
-- XXX: This omits the @context@ parameter, e.g.:
|
||||||
--
|
--
|
||||||
-- "There is no instance for HasServer (Bool :> …)". Do we care ?
|
-- "There is no instance for HasServer (Bool :> …)". Do we care ?
|
||||||
instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub
|
instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub
|
||||||
#if __GLASGOW_HASKELL__ >= 904
|
#if __GLASGOW_HASKELL__ >= 904
|
||||||
@(Type -> [Type] -> Constraint)
|
@(Type -> [Type] -> Constraint)
|
||||||
#endif
|
#endif
|
||||||
HasServer ty) => HasServer (ty :> sub) context
|
HasServer ty) => HasServer (ty :> sub) context
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user