@@ -1,12 +1,10 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE ConstraintKinds #-}
4{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE DeriveDataTypeable #-}
6{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
7{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
12{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
@@ -36,15 +34,14 @@ module Servant.Server.Internal
36import Control.Monad
34import Control.Monad
37 (join, when)
35 (join, when)
38import Control.Monad.Trans
36import Control.Monad.Trans
39− (liftIO, lift)
37+ (liftIO)
40import Control.Monad.Trans.Resource
38import Control.Monad.Trans.Resource
41− (runResourceT, ReleaseKey)
39+ (runResourceT)
43import qualified Data.ByteString as B
40import qualified Data.ByteString as B
44import qualified Data.ByteString.Builder as BB
41import qualified Data.ByteString.Builder as BB
45import qualified Data.ByteString.Char8 as BC8
42import qualified Data.ByteString.Char8 as BC8
46import qualified Data.ByteString.Lazy as BL
43import qualified Data.ByteString.Lazy as BL
47−import Data.Constraint (Constraint, Dict(..))
44+import Data.Constraint (Dict(..))
48import Data.Either
45import Data.Either
49 (partitionEithers)
46 (partitionEithers)
50import Data.Maybe
47import Data.Maybe
@@ -59,14 +56,14 @@ import qualified Data.Text as T
59import Data.Typeable
56import Data.Typeable
60import GHC.Generics
57import GHC.Generics
61import GHC.TypeLits
58import GHC.TypeLits
62− (KnownNat, KnownSymbol, TypeError, symbolVal)
59+ (KnownNat, KnownSymbol, TypeError, natVal, symbolVal)
63import qualified Network.HTTP.Media as NHM
60import qualified Network.HTTP.Media as NHM
64import Network.HTTP.Types hiding
61import Network.HTTP.Types hiding
65 (Header, ResponseHeaders)
62 (Header, ResponseHeaders)
66import Network.Socket
63import Network.Socket
67 (SockAddr)
64 (SockAddr)
68import Network.Wai
65import Network.Wai
69− (Application, Request, Response, ResponseReceived, httpVersion, isSecure, lazyRequestBody,
66+ (Application, Request, httpVersion, isSecure, lazyRequestBody,
70 queryString, remoteHost, getRequestBodyChunk, requestHeaders,
67 queryString, remoteHost, getRequestBodyChunk, requestHeaders,
71 requestMethod, responseLBS, responseStream, vault)
68 requestMethod, responseLBS, responseStream, vault)
72import Prelude ()
69import Prelude ()
@@ -76,10 +73,10 @@ import Servant.API
76 CaptureAll, Description, EmptyAPI, Fragment,
73 CaptureAll, Description, EmptyAPI, Fragment,
77 FramingRender (..), FramingUnrender (..), FromSourceIO (..),
74 FramingRender (..), FramingUnrender (..), FromSourceIO (..),
78 Header', If, IsSecure (..), NoContentVerb, QueryFlag,
75 Header', If, IsSecure (..), NoContentVerb, QueryFlag,
79− QueryParam', QueryParams, Raw, RawM, ReflectMethod (reflectMethod),
76+ QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
80 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
77 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
81 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
78 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
82− WithNamedContext, WithResource, NamedRoutes)
79+ WithNamedContext, NamedRoutes)
83import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
80import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
84import Servant.API.ContentTypes
81import Servant.API.ContentTypes
85 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
82 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
@@ -90,15 +87,11 @@ import Servant.API.Modifiers
90 unfoldRequestArgument)
87 unfoldRequestArgument)
91import Servant.API.ResponseHeaders
88import Servant.API.ResponseHeaders
92 (GetHeaders, Headers, getHeaders, getResponse)
89 (GetHeaders, Headers, getHeaders, getResponse)
93−import Servant.API.Status
95import qualified Servant.Types.SourceT as S
90import qualified Servant.Types.SourceT as S
96import Servant.API.TypeErrors
91import Servant.API.TypeErrors
97import Web.HttpApiData
92import Web.HttpApiData
98 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
93 (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece,
99 parseUrlPieces)
94 parseUrlPieces)
103import Servant.Server.Internal.BasicAuth
96import Servant.Server.Internal.BasicAuth
104import Servant.Server.Internal.Context
97import Servant.Server.Internal.Context
@@ -117,10 +110,6 @@ import Servant.API.TypeLevel
117 (AtLeastOneFragment, FragmentUnique)
110 (AtLeastOneFragment, FragmentUnique)
119class HasServer api context where
112class HasServer api context where
120− -- | The type of a server for this API, given a monad to run effects in.
122− -- Note that the result kind is @*@, so it is /not/ a monad transformer, unlike
123− -- what the @T@ in the name might suggest.
124 type ServerT api (m :: * -> *) :: *
113 type ServerT api (m :: * -> *) :: *
@@ -182,7 +171,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
182-- > server = getBook
171-- > server = getBook
183-- > where getBook :: Text -> Handler Book
172-- > where getBook :: Text -> Handler Book
184-- > getBook isbn = ...
173-- > getBook isbn = ...
185−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
174+instance (KnownSymbol capture, FromHttpApiData a
186 , HasServer api context, SBoolI (FoldLenient mods)
175 , HasServer api context, SBoolI (FoldLenient mods)
187 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
176 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -194,7 +183,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
194 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
183 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
196 route Proxy context d =
185 route Proxy context d =
197− CaptureRouter [hint] $
186+ CaptureRouter $
198 route (Proxy :: Proxy api)
187 route (Proxy :: Proxy api)
200 (addCapture d $ \ txt -> withRequest $ \ request ->
189 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -206,7 +195,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
207 rep = typeRep (Proxy :: Proxy Capture')
196 rep = typeRep (Proxy :: Proxy Capture')
208 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
197 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
209− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
211-- | If you use 'CaptureAll' in one of the endpoints for your API,
199-- | If you use 'CaptureAll' in one of the endpoints for your API,
212-- this automatically requires your server-side handler to be a
200-- this automatically requires your server-side handler to be a
@@ -225,7 +213,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
225-- > server = getSourceFile
213-- > server = getSourceFile
226-- > where getSourceFile :: [Text] -> Handler Book
214-- > where getSourceFile :: [Text] -> Handler Book
227-- > getSourceFile pathSegments = ...
215-- > getSourceFile pathSegments = ...
228−instance (KnownSymbol capture, FromHttpApiData a, Typeable a
216+instance (KnownSymbol capture, FromHttpApiData a
229 , HasServer api context
217 , HasServer api context
230 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
218 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -237,7 +225,7 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
237 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
225 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
239 route Proxy context d =
227 route Proxy context d =
240− CaptureAllRouter [hint] $
228+ CaptureAllRouter $
241 route (Proxy :: Proxy api)
229 route (Proxy :: Proxy api)
243 (addCapture d $ \ txts -> withRequest $ \ request ->
231 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -248,43 +236,6 @@ instance (KnownSymbol capture, FromHttpApiData a, Typeable a
249 rep = typeRep (Proxy :: Proxy CaptureAll)
237 rep = typeRep (Proxy :: Proxy CaptureAll)
250 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
238 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
251− hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
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.
259−-- The creation and destruction are done using a 'Data.Acquire.Acquire'
260−-- provided via server 'Context'.
264−-- > type MyApi = WithResource Handle :> "writeToFile" :> Post '[JSON] NoContent
266−-- > server :: Server MyApi
267−-- > server = writeToFile
268−-- > where writeToFile :: (ReleaseKey, Handle) -> Handler NoContent
269−-- > writeToFile (_, h) = hPutStrLn h "message"
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
275−instance (HasServer api ctx, HasContextEntry ctx (Acquire a))
276− => HasServer (WithResource a :> api) ctx where
278− type ServerT (WithResource a :> api) m = (ReleaseKey, a) -> ServerT api m
280− hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy @api) pc nt . s
282− route Proxy context d = route (Proxy @api) context (d `addParameterCheck` allocateResource)
284− allocateResource :: DelayedIO (ReleaseKey, a)
285− allocateResource = DelayedIO $ lift $ allocateAcquire (getContextEntry context)
289allowedMethodHead :: Method -> Request -> Bool
240allowedMethodHead :: Method -> Request -> Bool
290allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
241allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -348,7 +299,7 @@ instance {-# OVERLAPPABLE #-}
349 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
300 route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
350 where method = reflectMethod (Proxy :: Proxy method)
301 where method = reflectMethod (Proxy :: Proxy method)
351− status = statusFromNat (Proxy :: Proxy status)
302+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
353instance {-# OVERLAPPING #-}
304instance {-# OVERLAPPING #-}
354 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
305 ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
@@ -360,7 +311,7 @@ instance {-# OVERLAPPING #-}
361 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
312 route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
362 where method = reflectMethod (Proxy :: Proxy method)
313 where method = reflectMethod (Proxy :: Proxy method)
363− status = statusFromNat (Proxy :: Proxy status)
314+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
365instance (ReflectMethod method) =>
316instance (ReflectMethod method) =>
366 HasServer (NoContentVerb method) context where
317 HasServer (NoContentVerb method) context where
@@ -381,7 +332,7 @@ instance {-# OVERLAPPABLE #-}
382 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
333 route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
383 where method = reflectMethod (Proxy :: Proxy method)
334 where method = reflectMethod (Proxy :: Proxy method)
384− status = statusFromNat (Proxy :: Proxy status)
335+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
387instance {-# OVERLAPPING #-}
338instance {-# OVERLAPPING #-}
@@ -395,7 +346,7 @@ instance {-# OVERLAPPING #-}
396 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
347 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
397 where method = reflectMethod (Proxy :: Proxy method)
348 where method = reflectMethod (Proxy :: Proxy method)
398− status = statusFromNat (Proxy :: Proxy status)
349+ status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
401streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
352streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, FramingRender framing, ToSourceIO chunk a) =>
@@ -653,35 +604,6 @@ instance HasServer Raw context where
653 Fail a -> respond $ Fail a
604 Fail a -> respond $ Fail a
654 FailFatal e -> respond $ FailFatal e
605 FailFatal e -> respond $ FailFatal e
656−-- | Just pass the request to the underlying application and serve its response.
660−-- > type MyApi = "images" :> Raw
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
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)) >>=
677− Left e -> respond' $ FailFatal e
679− Fail e -> respond' $ Fail e
680− FailFatal e -> respond' $ FailFatal e
682− hoistServerWithContext _ _ f srvM = \req respond -> f (srvM req respond)
685-- | If you use 'ReqBody' in one of the endpoints for your API,
607-- | If you use 'ReqBody' in one of the endpoints for your API,
686-- this automatically requires your server-side handler to be a function
608-- this automatically requires your server-side handler to be a function
687-- that takes an argument of the type specified by 'ReqBody'.
609-- that takes an argument of the type specified by 'ReqBody'.
@@ -895,13 +817,9 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
895-------------------------------------------------------------------------------
817-------------------------------------------------------------------------------
897-- Erroring instance for 'HasServer' when a combinator is not fully applied
819-- Erroring instance for 'HasServer' when a combinator is not fully applied
898−instance TypeError (PartialApplication
899−#if __GLASGOW_HASKELL__ >= 904
900− @(Type -> [Type] -> Constraint)
902− HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
820+instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
904− type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
822+ type ServerT (arr :> sub) _ = TypeError (PartialApplication HasServer arr)
905 route = error "unreachable"
823 route = error "unreachable"
906 hoistServerWithContext _ _ _ _ = error "unreachable"
824 hoistServerWithContext _ _ _ _ = error "unreachable"
@@ -943,11 +861,7 @@ type HasServerArrowTypeError a b =
943-- XXX: This omits the @context@ parameter, e.g.:
861-- XXX: This omits the @context@ parameter, e.g.:
945-- "There is no instance for HasServer (Bool :> …)". Do we care ?
863-- "There is no instance for HasServer (Bool :> …)". Do we care ?
946−instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub
864+instance {-# OVERLAPPABLE #-} TypeError (NoInstanceForSub HasServer ty) => HasServer (ty :> sub) context
947−#if __GLASGOW_HASKELL__ >= 904
948− @(Type -> [Type] -> Constraint)
950− HasServer ty) => HasServer (ty :> sub) context
952instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context
866instance {-# OVERLAPPABLE #-} TypeError (NoInstanceFor (HasServer api context)) => HasServer api context