@@ -1,22 +1,23 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE CPP #-}
2{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE DeriveDataTypeable #-}
5{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE InstanceSigs #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE OverloadedStrings #-}
8{-# LANGUAGE OverloadedStrings #-}
10{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE QuantifiedConstraints #-}
12{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE TupleSections #-}
12{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
16{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
17{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
18{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
17+#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ >= 802
18+#define HAS_TYPE_ERROR
20module Servant.Server.Internal
21module Servant.Server.Internal
21 ( module Servant.Server.Internal
22 ( module Servant.Server.Internal
22 , module Servant.Server.Internal.BasicAuth
23 , module Servant.Server.Internal.BasicAuth
@@ -41,11 +42,12 @@ import qualified Data.ByteString as B
41import qualified Data.ByteString.Builder as BB
42import qualified Data.ByteString.Builder as BB
42import qualified Data.ByteString.Char8 as BC8
43import qualified Data.ByteString.Char8 as BC8
43import qualified Data.ByteString.Lazy as BL
44import qualified Data.ByteString.Lazy as BL
44−import Data.Constraint (Constraint, Dict(..))
45import Data.Either
45import Data.Either
46 (partitionEithers)
46 (partitionEithers)
47import Data.Maybe
47import Data.Maybe
48 (fromMaybe, isNothing, mapMaybe, maybeToList)
48 (fromMaybe, isNothing, mapMaybe, maybeToList)
49import Data.String
51import Data.String
50 (IsString (..))
52 (IsString (..))
51import Data.String.Conversions
53import Data.String.Conversions
@@ -54,9 +56,8 @@ import Data.Tagged
54 (Tagged (..), retag, untag)
56 (Tagged (..), retag, untag)
55import qualified Data.Text as T
57import qualified Data.Text as T
56import Data.Typeable
58import Data.Typeable
58import GHC.TypeLits
59import GHC.TypeLits
59− (KnownNat, KnownSymbol, TypeError, symbolVal)
60+ (KnownNat, KnownSymbol, natVal, symbolVal)
60import qualified Network.HTTP.Media as NHM
61import qualified Network.HTTP.Media as NHM
61import Network.HTTP.Types hiding
62import Network.HTTP.Types hiding
62 (Header, ResponseHeaders)
63 (Header, ResponseHeaders)
@@ -76,8 +77,7 @@ import Servant.API
76 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
77 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
77 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
78 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
78 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
79 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
79− WithNamedContext, NamedRoutes)
80+ WithNamedContext)
80−import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
81import Servant.API.ContentTypes
81import Servant.API.ContentTypes
82 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
82 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
83 AllMime, MimeRender (..), MimeUnrender (..), NoContent,
83 AllMime, MimeRender (..), MimeUnrender (..), NoContent,
@@ -87,10 +87,7 @@ import Servant.API.Modifiers
87 unfoldRequestArgument)
87 unfoldRequestArgument)
88import Servant.API.ResponseHeaders
88import Servant.API.ResponseHeaders
89 (GetHeaders, Headers, getHeaders, getResponse)
89 (GetHeaders, Headers, getHeaders, getResponse)
90−import Servant.API.Status
92import qualified Servant.Types.SourceT as S
90import qualified Servant.Types.SourceT as S
93−import Servant.API.TypeErrors
94import Web.HttpApiData
91import Web.HttpApiData
95 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
92 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
96 parseUrlPieces)
93 parseUrlPieces)
@@ -106,10 +103,12 @@ import Servant.Server.Internal.RouteResult
106import Servant.Server.Internal.RoutingApplication
103import Servant.Server.Internal.RoutingApplication
107import Servant.Server.Internal.ServerError
104import Servant.Server.Internal.ServerError
106+#ifdef HAS_TYPE_ERROR
109import GHC.TypeLits
107import GHC.TypeLits
110 (ErrorMessage (..), TypeError)
108 (ErrorMessage (..), TypeError)
111import Servant.API.TypeLevel
109import Servant.API.TypeLevel
112 (AtLeastOneFragment, FragmentUnique)
110 (AtLeastOneFragment, FragmentUnique)
114class HasServer api context where
113class HasServer api context where
115 type ServerT api (m :: * -> *) :: *
114 type ServerT api (m :: * -> *) :: *
@@ -173,7 +172,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
173-- > server = getBook
172-- > server = getBook
174-- > where getBook :: Text -> Handler Book
173-- > where getBook :: Text -> Handler Book
175-- > getBook isbn = ...
174-- > getBook isbn = ...
176−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
175+instance (KnownSymbol capture, FromHttpApiData a
177 , HasServer api context, SBoolI (FoldLenient mods)
176 , HasServer api context, SBoolI (FoldLenient mods)
178 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
177 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -185,7 +184,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
185 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
184 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
187 route Proxy context d =
186 route Proxy context d =
188− CaptureRouter [hint] $
187+ CaptureRouter $
189 route (Proxy :: Proxy api)
188 route (Proxy :: Proxy api)
191 (addCapture d $ \ txt -> withRequest $ \ request ->
190 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -197,7 +196,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
198 rep = typeRep (Proxy :: Proxy Capture')
197 rep = typeRep (Proxy :: Proxy Capture')
199 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
198 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
200− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
202-- | If you use 'CaptureAll' in one of the endpoints for your API,
200-- | If you use 'CaptureAll' in one of the endpoints for your API,
203-- this automatically requires your server-side handler to be a
201-- this automatically requires your server-side handler to be a
@@ -216,7 +214,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
216-- > server = getSourceFile
214-- > server = getSourceFile
217-- > where getSourceFile :: [Text] -> Handler Book
215-- > where getSourceFile :: [Text] -> Handler Book
218-- > getSourceFile pathSegments = ...
216-- > getSourceFile pathSegments = ...
219−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
217+instance (KnownSymbol capture, FromHttpApiData a
220 , HasServer api context
218 , HasServer api context
221 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
219 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -228,7 +226,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
228 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
226 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
230 route Proxy context d =
228 route Proxy context d =
231− CaptureAllRouter [hint] $
229+ CaptureAllRouter $
232 route (Proxy :: Proxy api)
230 route (Proxy :: Proxy api)
234 (addCapture d $ \ txts -> withRequest $ \ request ->
232 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -239,7 +237,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
240 rep = typeRep (Proxy :: Proxy CaptureAll)
238 rep = typeRep (Proxy :: Proxy CaptureAll)
241 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
239 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
242− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
244allowedMethodHead :: Method -> Request -> Bool
241allowedMethodHead :: Method -> Request -> Bool
245allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
242allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -303,7 +300,7 @@ instance {-# OVERLAPPABLE #-}
304 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
301 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
305 where method = reflectMethod (Proxy :: Proxy method)
302 where method = reflectMethod (Proxy :: Proxy method)
306− status = statusFromNat (Proxy :: Proxy status)
303+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
308instance {-# OVERLAPPING #-}
305instance {-# OVERLAPPING #-}
309 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
306 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
@@ -315,7 +312,7 @@ instance {-# OVERLAPPING #-}
316 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
313 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
317 where method = reflectMethod (Proxy :: Proxy method)
314 where method = reflectMethod (Proxy :: Proxy method)
318− status = statusFromNat (Proxy :: Proxy status)
315+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
320instance (ReflectMethod method) =>
317instance (ReflectMethod method) =>
321 HasServer (NoContentVerb method) context where
318 HasServer (NoContentVerb method) context where
@@ -336,7 +333,7 @@ instance {-# OVERLAPPABLE #-}
337 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
334 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
338 where method = reflectMethod (Proxy :: Proxy method)
335 where method = reflectMethod (Proxy :: Proxy method)
339− status = statusFromNat (Proxy :: Proxy status)
336+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
342instance {-# OVERLAPPING #-}
339instance {-# OVERLAPPING #-}
@@ -350,7 +347,7 @@ instance {-# OVERLAPPING #-}
351 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
348 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
352 where method = reflectMethod (Proxy :: Proxy method)
349 where method = reflectMethod (Proxy :: Proxy method)
353− status = statusFromNat (Proxy :: Proxy status)
350+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
356streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
353streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
@@ -789,7 +786,7 @@ instance ( KnownSymbol realm
789-- * helpers
786-- * helpers
791ct_wildcard :: B.ByteString
788ct_wildcard :: B.ByteString
792−ct_wildcard = "*" <> "/" <> "*"
789+ct_wildcard = "*" <> "/" <> "*" -- Because CPP
794getAcceptHeader :: Request -> AcceptHeader
791getAcceptHeader :: Request -> AcceptHeader
795getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders
792getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders
@@ -817,15 +814,39 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
817 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
814 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
819-------------------------------------------------------------------------------
816-------------------------------------------------------------------------------
820−-- Custom type errors
817+-- TypeError helpers
821-------------------------------------------------------------------------------
818-------------------------------------------------------------------------------
823−-- Erroring instance for 'HasServer' when a combinator is not fully applied
820+#ifdef HAS_TYPE_ERROR
821+-- | This instance catches mistakes when there are non-saturated
822+-- type applications on LHS of ':>'.
824+-- >>> serve (Proxy :: Proxy (Capture "foo" :> Get '[JSON] Int)) (error "...")
826+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
827+-- ...Maybe you haven't applied enough arguments to
828+-- ...Capture' '[] "foo"
831+-- >>> undefined :: Server (Capture "foo" :> Get '[JSON] Int)
833+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
834+-- ...Maybe you haven't applied enough arguments to
835+-- ...Capture' '[] "foo"
824−instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
838+instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
826− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
840+ type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
827− route = error "unreachable"
841+ -- it doesn't really matter what sub route we peak
842+ route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
828− hoistServerWithContext _ _ _ _ = error "unreachable"
843+ hoistServerWithContext _ _ _ = id
845+-- Cannot have TypeError here, otherwise use of this symbol will error :)
846+type HasServerArrowKindError arr =
847+ 'Text "Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'."
848+ ':$$: 'Text "Maybe you haven't applied enough arguments to"
830-- | This instance prevents from accidentally using '->' instead of ':>'
851-- | This instance prevents from accidentally using '->' instead of ':>'
@@ -859,15 +880,7 @@ type HasServerArrowTypeError a b =
859 ':$$: 'ShowType a
880 ':$$: 'ShowType a
860 ':$$: 'Text "and"
881 ':$$: 'Text "and"
861 ':$$: 'ShowType b
882 ':$$: 'ShowType b
863−-- Erroring instances for 'HasServer' for unknown API combinators
865−-- XXX: This omits the @context@ parameter, e.g.:
867−-- "There is no instance for HasServer (Bool :> …)". Do we care ?
868−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub HasServer ty) => HasServer (ty :> sub) context
870−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
872-- | Ignore @'Fragment'@ in server handlers.
885-- | Ignore @'Fragment'@ in server handlers.
873-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
886-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
@@ -880,7 +893,11 @@ instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context))
880-- > server = getBooks
893-- > server = getBooks
881-- > where getBooks :: Handler [Book]
894-- > where getBooks :: Handler [Book]
882-- > getBooks = ...return all books...
895-- > getBooks = ...return all books...
896+#ifdef HAS_TYPE_ERROR
883instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context)
897instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context)
899+instance (HasServer api context)
884 => HasServer (Fragment a1 :> api) context where
901 => HasServer (Fragment a1 :> api) context where
885 type ServerT (Fragment a1 :> api) m = ServerT api m
902 type ServerT (Fragment a1 :> api) m = ServerT api m
@@ -890,72 +907,3 @@ instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer
892-- >>> import Servant
909-- >>> import Servant
894−-- | A type that specifies that an API record contains a server implementation.
895−data AsServerT (m :: * -> *)
896−instance GenericMode (AsServerT m) where
897− type AsServerT m :- api = ServerT api m
899−type AsServer = AsServerT Handler
902−-- | Set of constraints required to convert to / from vanilla server types.
903−type GServerConstraints api m =
904− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
905− , GServantProduct (Rep (api (AsServerT m)))
908−-- | This class is a necessary evil: in the implementation of 'HasServer' for
909−-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
910−-- m. 'GServerConstraints' m@ to hold.
912−-- We cannot require do that directly as the definition of 'GServerConstraints'
913−-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
914−-- those type family applications behind a typeclass providing evidence for
915−-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
916−-- @forall m. 'GServer' api m@ instead.
918−-- Users shouldn't have to worry about this class, as the only possible instance
919−-- is provided in this module for all record APIs.
921−class GServer (api :: * -> *) (m :: * -> *) where
922− gServerProof :: Dict (GServerConstraints api m)
925− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
926− , GServantProduct (Rep (api (AsServerT m)))
927− ) => GServer api m where
931− ( HasServer (ToServantApi api) context
932− , forall m. Generic (api (AsServerT m))
933− , forall m. GServer api m
934− ) => HasServer (NamedRoutes api) context where
936− type ServerT (NamedRoutes api) m = api (AsServerT m)
939− :: Proxy (NamedRoutes api)
941− -> Delayed env (api (AsServerT Handler))
943− route _ ctx delayed =
944− case gServerProof @api @Handler of
945− Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
947− hoistServerWithContext
948− :: forall m n. Proxy (NamedRoutes api)
950− -> (forall x. m x -> n x)
951− -> api (AsServerT m)
952− -> api (AsServerT n)
953− hoistServerWithContext _ pctx nat server =
954− case (gServerProof @api @m, gServerProof @api @n) of
956− fromServant servantSrvN
958− servantSrvM :: ServerT (ToServantApi api) m =
960− servantSrvN :: ServerT (ToServantApi api) n =
961− hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM