@@ -1,24 +1,23 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE ConstraintKinds #-}
31{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
43{-# LANGUAGE DataKinds #-}
54{-# LANGUAGE DeriveDataTypeable #-}
65{-# LANGUAGE FlexibleContexts #-}
76{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE LambdaCase #-}
107{-# LANGUAGE MultiParamTypeClasses #-}
118{-# LANGUAGE OverloadedStrings #-}
129{-# LANGUAGE PolyKinds #-}
13−{-# LANGUAGE QuantifiedConstraints #-}
1410{-# LANGUAGE RankNTypes #-}
1511{-# LANGUAGE ScopedTypeVariables #-}
1612{-# LANGUAGE TupleSections #-}
17−{-# LANGUAGE TypeApplications #-}
1813{-# LANGUAGE TypeFamilies #-}
1914{-# LANGUAGE TypeOperators #-}
2015{-# LANGUAGE UndecidableInstances #-}
2116
17+#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ >= 802
18+#define HAS_TYPE_ERROR
19+#endif
20+
2221module Servant.Server.Internal
2322 ( module Servant.Server.Internal
2423 , module Servant.Server.Internal.BasicAuth
@@ -36,15 +35,13 @@ module Servant.Server.Internal
3635import Control.Monad
3736 (join, when)
3837import Control.Monad.Trans
39− (liftIO, lift)
38+ (liftIO)
4039import Control.Monad.Trans.Resource
41− (runResourceT, ReleaseKey)
42−import Data.Acquire
40+ (runResourceT)
4341import qualified Data.ByteString as B
4442import qualified Data.ByteString.Builder as BB
4543import qualified Data.ByteString.Char8 as BC8
4644import qualified Data.ByteString.Lazy as BL
47−import Data.Constraint (Constraint, Dict(..))
4845import Data.Either
4946 (partitionEithers)
5047import Data.Maybe
@@ -57,16 +54,15 @@ import Data.Tagged
5754 (Tagged (..), retag, untag)
5855import qualified Data.Text as T
5956import Data.Typeable
60−import GHC.Generics
6157import GHC.TypeLits
62− (KnownNat, KnownSymbol, TypeError, symbolVal)
58+ (KnownNat, KnownSymbol, natVal, symbolVal)
6359import qualified Network.HTTP.Media as NHM
6460import Network.HTTP.Types hiding
6561 (Header, ResponseHeaders)
6662import Network.Socket
6763 (SockAddr)
6864import Network.Wai
69− (Application, Request, Response, ResponseReceived, httpVersion, isSecure, lazyRequestBody,
65+ (Application, Request, httpVersion, isSecure, lazyRequestBody,
7066 queryString, remoteHost, getRequestBodyChunk, requestHeaders,
7167 requestMethod, responseLBS, responseStream, vault)
7268import Prelude ()
@@ -76,11 +72,10 @@ import Servant.API
7672 CaptureAll, Description, EmptyAPI, Fragment,
7773 FramingRender (..), FramingUnrender (..), FromSourceIO (..),
7874 Header', If, IsSecure (..), NoContentVerb, QueryFlag,
79− QueryParam', QueryParams, Raw, RawM, ReflectMethod (reflectMethod),
75+ QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
8076 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
8177 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
82− WithNamedContext, WithResource, NamedRoutes)
83−import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
78+ WithNamedContext)
8479import Servant.API.ContentTypes
8580 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
8681 AllMime, MimeRender (..), MimeUnrender (..), NoContent,
@@ -90,15 +85,10 @@ import Servant.API.Modifiers
9085 unfoldRequestArgument)
9186import Servant.API.ResponseHeaders
9287 (GetHeaders, Headers, getHeaders, getResponse)
93−import Servant.API.Status
94− (statusFromNat)
9588import qualified Servant.Types.SourceT as S
96−import Servant.API.TypeErrors
9789import Web.HttpApiData
9890 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
9991 parseUrlPieces)
100−import Data.Kind
101− (Type)
10292
10393import Servant.Server.Internal.BasicAuth
10494import Servant.Server.Internal.Context
@@ -111,16 +101,14 @@ import Servant.Server.Internal.RouteResult
111101import Servant.Server.Internal.RoutingApplication
112102import Servant.Server.Internal.ServerError
113103
104+#ifdef HAS_TYPE_ERROR
114105import GHC.TypeLits
115106 (ErrorMessage (..), TypeError)
116107import Servant.API.TypeLevel
117108 (AtLeastOneFragment, FragmentUnique)
109+#endif
118110
119111class HasServer api context where
120− -- | The type of a server for this API, given a monad to run effects in.
121− --
122− -- Note that the result kind is @*@, so it is /not/ a monad transformer, unlike
123− -- what the @T@ in the name might suggest.
124112 type ServerT api (m :: * -> *) :: *
125113
126114 route ::
@@ -182,7 +170,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
182170-- > server = getBook
183171-- > where getBook :: Text -> Handler Book
184172-- > getBook isbn = ...
185−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
173+instance (KnownSymbol capture, FromHttpApiData a
186174 , HasServer api context, SBoolI (FoldLenient mods)
187175 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
188176 )
@@ -194,7 +182,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
194182 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
195183
196184 route Proxy context d =
197− CaptureRouter [hint] $
185+ CaptureRouter $
198186 route (Proxy :: Proxy api)
199187 context
200188 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -206,7 +194,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
206194 where
207195 rep = typeRep (Proxy :: Proxy Capture')
208196 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
209− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
210197
211198-- | If you use 'CaptureAll' in one of the endpoints for your API,
212199-- this automatically requires your server-side handler to be a
@@ -225,7 +212,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
225212-- > server = getSourceFile
226213-- > where getSourceFile :: [Text] -> Handler Book
227214-- > getSourceFile pathSegments = ...
228−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
215+instance (KnownSymbol capture, FromHttpApiData a
229216 , HasServer api context
230217 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
231218 )
@@ -237,7 +224,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
237224 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
238225
239226 route Proxy context d =
240− CaptureAllRouter [hint] $
227+ CaptureAllRouter $
241228 route (Proxy :: Proxy api)
242229 context
243230 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -248,43 +235,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
248235 where
249236 rep = typeRep (Proxy :: Proxy CaptureAll)
250237 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
251− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
252−
253−-- | If you use 'WithResource' in one of the endpoints for your API Servant
254−-- will provide the handler for this endpoint an argument of the specified type.
255−-- The lifespan of this resource will be automatically managed by Servant. This
256−-- resource will be created before the handler starts and it will be destoyed
257−-- after it ends. A new resource is created for each request to the endpoint.
258−
259−-- The creation and destruction are done using a 'Data.Acquire.Acquire'
260−-- provided via server 'Context'.
261−--
262−-- Example
263−--
264−-- > type MyApi = WithResource Handle :> "writeToFile" :> Post '[JSON] NoContent
265−-- >
266−-- > server :: Server MyApi
267−-- > server = writeToFile
268−-- > where writeToFile :: (ReleaseKey, Handle) -> Handler NoContent
269−-- > writeToFile (_, h) = hPutStrLn h "message"
270−--
271−-- In addition to the resource, the handler will also receive a 'ReleaseKey'
272−-- which can be used to deallocate the resource before the end of the request
273−-- if desired.
274−
275−instance (HasServer api ctx, HasContextEntry ctx (Acquire a))
276− => HasServer (WithResource a :> api) ctx where
277−
278− type ServerT (WithResource a :> api) m = (ReleaseKey, a) -> ServerT api m
279−
280− hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy @api) pc nt . s
281−
282− route Proxy context d = route (Proxy @api) context (d `addParameterCheck` allocateResource)
283− where
284− allocateResource :: DelayedIO (ReleaseKey, a)
285− allocateResource = DelayedIO $ lift $ allocateAcquire (getContextEntry context)
286−
287−
288238
289239allowedMethodHead :: Method -> Request -> Bool
290240allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -348,7 +298,7 @@ instance {-# OVERLAPPABLE #-}
348298
349299 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
350300 where method = reflectMethod (Proxy :: Proxy method)
351− status = statusFromNat (Proxy :: Proxy status)
301+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
352302
353303instance {-# OVERLAPPING #-}
354304 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
@@ -360,7 +310,7 @@ instance {-# OVERLAPPING #-}
360310
361311 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
362312 where method = reflectMethod (Proxy :: Proxy method)
363− status = statusFromNat (Proxy :: Proxy status)
313+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
364314
365315instance (ReflectMethod method) =>
366316 HasServer (NoContentVerb method) context where
@@ -381,7 +331,7 @@ instance {-# OVERLAPPABLE #-}
381331
382332 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
383333 where method = reflectMethod (Proxy :: Proxy method)
384− status = statusFromNat (Proxy :: Proxy status)
334+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
385335
386336
387337instance {-# OVERLAPPING #-}
@@ -395,7 +345,7 @@ instance {-# OVERLAPPING #-}
395345
396346 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
397347 where method = reflectMethod (Proxy :: Proxy method)
398− status = statusFromNat (Proxy :: Proxy status)
348+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
399349
400350
401351streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
@@ -653,35 +603,6 @@ instance HasServer Raw context where
653603 Fail a -> respond $ Fail a
654604 FailFatal e -> respond $ FailFatal e
655605
656−-- | Just pass the request to the underlying application and serve its response.
657−--
658−-- Example:
659−--
660−-- > type MyApi = "images" :> Raw
661−-- >
662−-- > server :: Server MyApi
663−-- > server = serveDirectory "/var/www/images"
664−instance HasServer RawM context where
665− type ServerT RawM m = Request -> (Response -> IO ResponseReceived) -> m ResponseReceived
666−
667− route
668− :: Proxy RawM
669− -> Context context
670− -> Delayed env (Request -> (Response -> IO ResponseReceived) -> Handler ResponseReceived) -> Router env
671− route _ _ handleDelayed = RawRouter $ \env request respond -> runResourceT $ do
672− routeResult <- runDelayed handleDelayed env request
673− let respond' = liftIO . respond
674− liftIO $ case routeResult of
675− Route handler -> runHandler (handler request (respond . Route)) >>=
676− \case
677− Left e -> respond' $ FailFatal e
678− Right a -> pure a
679− Fail e -> respond' $ Fail e
680− FailFatal e -> respond' $ FailFatal e
681−
682− hoistServerWithContext _ _ f srvM = \req respond -> f (srvM req respond)
683−
684−
685606-- | If you use 'ReqBody' in one of the endpoints for your API,
686607-- this automatically requires your server-side handler to be a function
687608-- that takes an argument of the type specified by 'ReqBody'.
@@ -863,7 +784,7 @@ instance ( KnownSymbol realm
863784-- * helpers
864785
865786ct_wildcard :: B.ByteString
866−ct_wildcard = "*" <> "/" <> "*"
787+ct_wildcard = "*" <> "/" <> "*" -- Because CPP
867788
868789getAcceptHeader :: Request -> AcceptHeader
869790getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders
@@ -891,19 +812,39 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
891812 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
892813
893814-------------------------------------------------------------------------------
894−-- Custom type errors
815+-- TypeError helpers
895816-------------------------------------------------------------------------------
896817
897−-- Erroring instance for 'HasServer' when a combinator is not fully applied
898−instance TypeError (PartialApplication
899−#if __GLASGOW_HASKELL__ >= 904
900− @(Type -> [Type] -> Constraint)
901−#endif
902− HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
818+#ifdef HAS_TYPE_ERROR
819+-- | This instance catches mistakes when there are non-saturated
820+-- type applications on LHS of ':>'.
821+--
822+-- >>> serve (Proxy :: Proxy (Capture "foo" :> Get '[JSON] Int)) (error "...")
823+-- ...
824+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
825+-- ...Maybe you haven't applied enough arguments to
826+-- ...Capture' '[] "foo"
827+-- ...
828+--
829+-- >>> undefined :: Server (Capture "foo" :> Get '[JSON] Int)
830+-- ...
831+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
832+-- ...Maybe you haven't applied enough arguments to
833+-- ...Capture' '[] "foo"
834+-- ...
835+--
836+instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
903837 where
904− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
905− route = error "unreachable"
906− hoistServerWithContext _ _ _ _ = error "unreachable"
838+ type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
839+ -- it doesn't really matter what sub route we peak
840+ route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
841+ hoistServerWithContext _ _ _ = id
842+
843+-- Cannot have TypeError here, otherwise use of this symbol will error :)
844+type HasServerArrowKindError arr =
845+ 'Text "Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'."
846+ ':$$: 'Text "Maybe you haven't applied enough arguments to"
847+ ':$$: 'ShowType arr
907848
908849-- | This instance prevents from accidentally using '->' instead of ':>'
909850--
@@ -937,19 +878,7 @@ type HasServerArrowTypeError a b =
937878 ':$$: 'ShowType a
938879 ':$$: 'Text "and"
939880 ':$$: 'ShowType b
940−
941−-- Erroring instances for 'HasServer' for unknown API combinators
942−
943−-- XXX: This omits the @context@ parameter, e.g.:
944−--
945−-- "There is no instance for HasServer (Bool :> …)". Do we care ?
946−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub
947−#if __GLASGOW_HASKELL__ >= 904
948− @(Type -> [Type] -> Constraint)
949881#endif
950− HasServer ty) => HasServer (ty :> sub) context
951−
952−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
953882
954883-- | Ignore @'Fragment'@ in server handlers.
955884-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
@@ -962,7 +891,11 @@ instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context))
962891-- > server = getBooks
963892-- > where getBooks :: Handler [Book]
964893-- > getBooks = ...return all books...
894+#ifdef HAS_TYPE_ERROR
965895instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context)
896+#else
897+instance (HasServer api context)
898+#endif
966899 => HasServer (Fragment a1 :> api) context where
967900 type ServerT (Fragment a1 :> api) m = ServerT api m
968901
@@ -972,72 +905,3 @@ instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer
972905
973906-- $setup
974907-- >>> import Servant
975−
976−-- | A type that specifies that an API record contains a server implementation.
977−data AsServerT (m :: * -> *)
978−instance GenericMode (AsServerT m) where
979− type AsServerT m :- api = ServerT api m
980−
981−type AsServer = AsServerT Handler
982−
983−
984−-- | Set of constraints required to convert to / from vanilla server types.
985−type GServerConstraints api m =
986− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
987− , GServantProduct (Rep (api (AsServerT m)))
988− )
989−
990−-- | This class is a necessary evil: in the implementation of 'HasServer' for
991−-- @'NamedRoutes' api@, we essentially need the quantified constraint @forall
992−-- m. 'GServerConstraints' m@ to hold.
993−--
994−-- We cannot require do that directly as the definition of 'GServerConstraints'
995−-- contains type family applications ('Rep' and 'ServerT'). The trick is to hide
996−-- those type family applications behind a typeclass providing evidence for
997−-- @'GServerConstraints' api m@ in the form of a dictionary, and require that
998−-- @forall m. 'GServer' api m@ instead.
999−--
1000−-- Users shouldn't have to worry about this class, as the only possible instance
1001−-- is provided in this module for all record APIs.
1002−
1003−class GServer (api :: * -> *) (m :: * -> *) where
1004− gServerProof :: Dict (GServerConstraints api m)
1005−
1006−instance
1007− ( ToServant api (AsServerT m) ~ ServerT (ToServantApi api) m
1008− , GServantProduct (Rep (api (AsServerT m)))
1009− ) => GServer api m where
1010− gServerProof = Dict
1011−
1012−instance
1013− ( HasServer (ToServantApi api) context
1014− , forall m. Generic (api (AsServerT m))
1015− , forall m. GServer api m
1016− ) => HasServer (NamedRoutes api) context where
1017−
1018− type ServerT (NamedRoutes api) m = api (AsServerT m)
1019−
1020− route
1021− :: Proxy (NamedRoutes api)
1022− -> Context context
1023− -> Delayed env (api (AsServerT Handler))
1024− -> Router env
1025− route _ ctx delayed =
1026− case gServerProof @api @Handler of
1027− Dict -> route (Proxy @(ToServantApi api)) ctx (toServant <$> delayed)
1028−
1029− hoistServerWithContext
1030− :: forall m n. Proxy (NamedRoutes api)
1031− -> Proxy context
1032− -> (forall x. m x -> n x)
1033− -> api (AsServerT m)
1034− -> api (AsServerT n)
1035− hoistServerWithContext _ pctx nat server =
1036− case (gServerProof @api @m, gServerProof @api @n) of
1037− (Dict, Dict) ->
1038− fromServant servantSrvN
1039− where
1040− servantSrvM :: ServerT (ToServantApi api) m =
1041− toServant server
1042− servantSrvN :: ServerT (ToServantApi api) n =
1043− hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM