@@ -1,12 +1,10 @@
11{-# LANGUAGE AllowAmbiguousTypes #-}
22{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE CPP #-}
43{-# LANGUAGE DataKinds #-}
54{-# LANGUAGE DeriveDataTypeable #-}
65{-# LANGUAGE FlexibleContexts #-}
76{-# LANGUAGE FlexibleInstances #-}
87{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE LambdaCase #-}
108{-# LANGUAGE MultiParamTypeClasses #-}
119{-# LANGUAGE OverloadedStrings #-}
1210{-# LANGUAGE PolyKinds #-}
@@ -36,15 +34,14 @@ module Servant.Server.Internal
3634import Control.Monad
3735 (join, when)
3836import Control.Monad.Trans
39− (liftIO, lift)
37+ (liftIO)
4038import Control.Monad.Trans.Resource
41− (runResourceT, ReleaseKey)
42−import Data.Acquire
39+ (runResourceT)
4340import qualified Data.ByteString as B
4441import qualified Data.ByteString.Builder as BB
4542import qualified Data.ByteString.Char8 as BC8
4643import qualified Data.ByteString.Lazy as BL
47−import Data.Constraint (Constraint, Dict(..))
44+import Data.Constraint (Dict(..))
4845import Data.Either
4946 (partitionEithers)
5047import Data.Maybe
@@ -59,14 +56,14 @@ import qualified Data.Text as T
5956import Data.Typeable
6057import GHC.Generics
6158import GHC.TypeLits
62− (KnownNat, KnownSymbol, TypeError, symbolVal)
59+ (KnownNat, KnownSymbol, symbolVal)
6360import qualified Network.HTTP.Media as NHM
6461import Network.HTTP.Types hiding
6562 (Header, ResponseHeaders)
6663import Network.Socket
6764 (SockAddr)
6865import Network.Wai
69− (Application, Request, Response, ResponseReceived, httpVersion, isSecure, lazyRequestBody,
66+ (Application, Request, httpVersion, isSecure, lazyRequestBody,
7067 queryString, remoteHost, getRequestBodyChunk, requestHeaders,
7168 requestMethod, responseLBS, responseStream, vault)
7269import Prelude ()
@@ -76,10 +73,10 @@ import Servant.API
7673 CaptureAll, Description, EmptyAPI, Fragment,
7774 FramingRender (..), FramingUnrender (..), FromSourceIO (..),
7875 Header', If, IsSecure (..), NoContentVerb, QueryFlag,
79− QueryParam', QueryParams, Raw, RawM, ReflectMethod (reflectMethod),
76+ QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
8077 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
8178 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
82− WithNamedContext, WithResource, NamedRoutes)
79+ WithNamedContext, NamedRoutes)
8380import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
8481import Servant.API.ContentTypes
8582 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
@@ -93,12 +90,9 @@ import Servant.API.ResponseHeaders
9390import Servant.API.Status
9491 (statusFromNat)
9592import qualified Servant.Types.SourceT as S
96−import Servant.API.TypeErrors
9793import Web.HttpApiData
9894 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
9995 parseUrlPieces)
100−import Data.Kind
101− (Type)
10296
10397import Servant.Server.Internal.BasicAuth
10498import Servant.Server.Internal.Context
@@ -117,10 +111,6 @@ import Servant.API.TypeLevel
117111 (AtLeastOneFragment, FragmentUnique)
118112
119113class 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.
124114 type ServerT api (m :: * -> *) :: *
125115
126116 route ::
@@ -182,7 +172,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
182172-- > server = getBook
183173-- > where getBook :: Text -> Handler Book
184174-- > getBook isbn = ...
185−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
175+instance (KnownSymbol capture, FromHttpApiData a
186176 , HasServer api context, SBoolI (FoldLenient mods)
187177 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
188178 )
@@ -194,7 +184,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
194184 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
195185
196186 route Proxy context d =
197− CaptureRouter [hint] $
187+ CaptureRouter $
198188 route (Proxy :: Proxy api)
199189 context
200190 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -206,7 +196,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
206196 where
207197 rep = typeRep (Proxy :: Proxy Capture')
208198 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
209− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
210199
211200-- | If you use 'CaptureAll' in one of the endpoints for your API,
212201-- this automatically requires your server-side handler to be a
@@ -225,7 +214,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
225214-- > server = getSourceFile
226215-- > where getSourceFile :: [Text] -> Handler Book
227216-- > getSourceFile pathSegments = ...
228−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
217+instance (KnownSymbol capture, FromHttpApiData a
229218 , HasServer api context
230219 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
231220 )
@@ -237,7 +226,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
237226 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
238227
239228 route Proxy context d =
240− CaptureAllRouter [hint] $
229+ CaptureAllRouter $
241230 route (Proxy :: Proxy api)
242231 context
243232 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -248,43 +237,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
248237 where
249238 rep = typeRep (Proxy :: Proxy CaptureAll)
250239 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−
288240
289241allowedMethodHead :: Method -> Request -> Bool
290242allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -653,35 +605,6 @@ instance HasServer Raw context where
653605 Fail a -> respond $ Fail a
654606 FailFatal e -> respond $ FailFatal e
655607
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−
685608-- | If you use 'ReqBody' in one of the endpoints for your API,
686609-- this automatically requires your server-side handler to be a function
687610-- that takes an argument of the type specified by 'ReqBody'.
@@ -891,19 +814,38 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
891814 hoistServerWithContext _ _ nt s = hoistServerWithContext (Proxy :: Proxy subApi) (Proxy :: Proxy subContext) nt s
892815
893816-------------------------------------------------------------------------------
894−-- Custom type errors
817+-- TypeError helpers
895818-------------------------------------------------------------------------------
896819
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
820+-- | This instance catches mistakes when there are non-saturated
821+-- type applications on LHS of ':>'.
822+--
823+-- >>> serve (Proxy :: Proxy (Capture "foo" :> Get '[JSON] Int)) (error "...")
824+-- ...
825+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
826+-- ...Maybe you haven't applied enough arguments to
827+-- ...Capture' '[] "foo"
828+-- ...
829+--
830+-- >>> undefined :: Server (Capture "foo" :> Get '[JSON] Int)
831+-- ...
832+-- ...Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'.
833+-- ...Maybe you haven't applied enough arguments to
834+-- ...Capture' '[] "foo"
835+-- ...
836+--
837+instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context
903838 where
904− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
905− route = error "unreachable"
906− hoistServerWithContext _ _ _ _ = error "unreachable"
839+ type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr)
840+ -- it doesn't really matter what sub route we peak
841+ route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)"
842+ hoistServerWithContext _ _ _ = id
843+
844+-- Cannot have TypeError here, otherwise use of this symbol will error :)
845+type HasServerArrowKindError arr =
846+ 'Text "Expected something of kind Symbol or *, got: k -> l on the LHS of ':>'."
847+ ':$$: 'Text "Maybe you haven't applied enough arguments to"
848+ ':$$: 'ShowType arr
907849
908850-- | This instance prevents from accidentally using '->' instead of ':>'
909851--
@@ -938,19 +880,6 @@ type HasServerArrowTypeError a b =
938880 ':$$: 'Text "and"
939881 ':$$: 'ShowType b
940882
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)
949−#endif
950− HasServer ty) => HasServer (ty :> sub) context
951−
952−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
953−
954883-- | Ignore @'Fragment'@ in server handlers.
955884-- See <https://ietf.org/rfc/rfc2616.html#section-15.1.3> for more details.
956885--