@@ -31,12 +31,6 @@ module Servant.Server.Internal
31 , module Servant.Server.Internal.ServerError
31 , module Servant.Server.Internal.ServerError
34−import Data.Proxy (Proxy (Proxy))
35−import Data.SOP (I (I))
36−import Data.SOP.Constraint (All, And)
37−import Data.String.Conversions (LBS, cs)
38−import Network.HTTP.Types (Status, HeaderName, hContentType)
40import Control.Monad
34import Control.Monad
41 (join, when)
35 (join, when)
42import Control.Monad.Trans
36import Control.Monad.Trans
@@ -47,7 +41,7 @@ import qualified Data.ByteString as B
47import qualified Data.ByteString.Builder as BB
41import qualified Data.ByteString.Builder as BB
48import qualified Data.ByteString.Char8 as BC8
42import qualified Data.ByteString.Char8 as BC8
49import qualified Data.ByteString.Lazy as BL
43import qualified Data.ByteString.Lazy as BL
50−import Data.Constraint (Dict(..))
44+import Data.Constraint (Constraint, Dict(..))
51import Data.Either
45import Data.Either
52 (partitionEithers)
46 (partitionEithers)
53import Data.Maybe
47import Data.Maybe
@@ -82,9 +76,7 @@ import Servant.API
82 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
76 QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod),
83 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
77 RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
84 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
78 Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
85− WithNamedContext, NamedRoutes, UVerb, WithStatus(..))
79+ WithNamedContext, NamedRoutes)
87−import Servant.API.UVerb (HasStatus, IsMember, Statuses, UVerb, Union, Unique, WithStatus (..), foldMapUnion, inject, statusOf)
88import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
80import Servant.API.Generic (GenericMode(..), ToServant, ToServantApi, GServantProduct, toServant, fromServant)
89import Servant.API.ContentTypes
81import Servant.API.ContentTypes
90 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
82 (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..),
@@ -96,7 +88,7 @@ import Servant.API.Modifiers
96import Servant.API.ResponseHeaders
88import Servant.API.ResponseHeaders
97 (GetHeaders, Headers, getHeaders, getResponse)
89 (GetHeaders, Headers, getHeaders, getResponse)
98import Servant.API.Status
90import Servant.API.Status
99− (statusFromNat, KnownStatus)
91+ (statusFromNat)
100import qualified Servant.Types.SourceT as S
92import qualified Servant.Types.SourceT as S
101import Servant.API.TypeErrors
93import Servant.API.TypeErrors
102import Web.HttpApiData
94import Web.HttpApiData
@@ -181,7 +173,7 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont
181-- > server = getBook
173-- > server = getBook
182-- > where getBook :: Text -> Handler Book
174-- > where getBook :: Text -> Handler Book
183-- > getBook isbn = ...
175-- > getBook isbn = ...
184−instance (KnownSymbol capture, FromHttpApiData a
176+instance (KnownSymbol capture, FromHttpApiData a, Typeable a
185 , HasServer api context, SBoolI (FoldLenient mods)
177 , HasServer api context, SBoolI (FoldLenient mods)
186 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
178 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -193,7 +185,7 @@ instance (KnownSymbol capture, FromHttpApiData a
193 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
185 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
195 route Proxy context d =
187 route Proxy context d =
196− CaptureRouter $
188+ CaptureRouter [hint] $
197 route (Proxy :: Proxy api)
189 route (Proxy :: Proxy api)
199 (addCapture d $ \ txt -> withRequest $ \ request ->
191 (addCapture d $ \ txt -> withRequest $ \ request ->
@@ -205,6 +197,7 @@ instance (KnownSymbol capture, FromHttpApiData a
206 rep = typeRep (Proxy :: Proxy Capture')
198 rep = typeRep (Proxy :: Proxy Capture')
207 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
199 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
200+ hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy a))
209-- | If you use 'CaptureAll' in one of the endpoints for your API,
202-- | If you use 'CaptureAll' in one of the endpoints for your API,
210-- this automatically requires your server-side handler to be a
203-- this automatically requires your server-side handler to be a
@@ -223,7 +216,7 @@ instance (KnownSymbol capture, FromHttpApiData a
223-- > server = getSourceFile
216-- > server = getSourceFile
224-- > where getSourceFile :: [Text] -> Handler Book
217-- > where getSourceFile :: [Text] -> Handler Book
225-- > getSourceFile pathSegments = ...
218-- > getSourceFile pathSegments = ...
226−instance (KnownSymbol capture, FromHttpApiData a
219+instance (KnownSymbol capture, FromHttpApiData a, Typeable a
227 , HasServer api context
220 , HasServer api context
228 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
221 , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
@@ -235,7 +228,7 @@ instance (KnownSymbol capture, FromHttpApiData a
235 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
228 hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
237 route Proxy context d =
230 route Proxy context d =
238− CaptureAllRouter $
231+ CaptureAllRouter [hint] $
239 route (Proxy :: Proxy api)
232 route (Proxy :: Proxy api)
241 (addCapture d $ \ txts -> withRequest $ \ request ->
234 (addCapture d $ \ txts -> withRequest $ \ request ->
@@ -246,6 +239,7 @@ instance (KnownSymbol capture, FromHttpApiData a
247 rep = typeRep (Proxy :: Proxy CaptureAll)
240 rep = typeRep (Proxy :: Proxy CaptureAll)
248 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
241 formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context)
242+ hint = CaptureHint (T.pack $ symbolVal $ Proxy @capture) (typeRep (Proxy :: Proxy [a]))
250allowedMethodHead :: Method -> Request -> Bool
244allowedMethodHead :: Method -> Request -> Bool
251allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
245allowedMethodHead method request = method == methodGet && requestMethod request == methodHead
@@ -300,36 +294,28 @@ noContentRouter method status action = leafRouter route'
300 env request respond $ \ _output ->
294 env request respond $ \ _output ->
301 Route $ responseLBS status [] ""
295 Route $ responseLBS status [] ""
303−-- instance {-# OVERLAPPABLE #-}
297+instance {-# OVERLAPPABLE #-}
304−-- ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
298+ ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
305−-- ) => HasServer (Verb method status ctypes a) context where
299+ ) => HasServer (Verb method status ctypes a) context where
307−-- type ServerT (Verb method status ctypes a) m = m a
308−-- hoistServerWithContext _ _ nt s = nt s
310−-- route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
311−-- where method = reflectMethod (Proxy :: Proxy method)
312−-- status = statusFromNat (Proxy :: Proxy status)
314−-- instance {-# OVERLAPPING #-}
315−-- ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
316−-- , GetHeaders (Headers h a)
317−-- ) => HasServer (Verb method status ctypes (Headers h a)) context where
301+ type ServerT (Verb method status ctypes a) m = m a
302+ hoistServerWithContext _ _ nt s = nt s
319−-- type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
304+ route Proxy _ = methodRouter ([],) method (Proxy :: Proxy ctypes) status
320−-- hoistServerWithContext _ _ nt s = nt s
305+ where method = reflectMethod (Proxy :: Proxy method)
306+ status = statusFromNat (Proxy :: Proxy status)
322−-- route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
308+instance {-# OVERLAPPING #-}
323−-- where method = reflectMethod (Proxy :: Proxy method)
309+ ( AllCTRender ctypes a, ReflectMethod method, KnownNat status
324−-- status = statusFromNat (Proxy :: Proxy status)
310+ , GetHeaders (Headers h a)
311+ ) => HasServer (Verb method status ctypes (Headers h a)) context where
326−instance
313+ type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
327− (KnownStatus statusCode, HasServer (UVerb method ctypes '[WithStatus statusCode a]) context) =>
314+ hoistServerWithContext _ _ nt s = nt s
328− HasServer (Verb method statusCode ctypes a) context where
330− type ServerT (Verb method statusCode ctypes a) m = m a
316+ route Proxy _ = methodRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy ctypes) status
331− route = undefined
317+ where method = reflectMethod (Proxy :: Proxy method)
332− hoistServerWithContext p1 p2 nat s = undefined
318+ status = statusFromNat (Proxy :: Proxy status)
334instance (ReflectMethod method) =>
320instance (ReflectMethod method) =>
335 HasServer (NoContentVerb method) context where
321 HasServer (NoContentVerb method) context where
@@ -837,7 +823,7 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA
837-- Erroring instance for 'HasServer' when a combinator is not fully applied
823-- Erroring instance for 'HasServer' when a combinator is not fully applied
838instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
824instance TypeError (PartialApplication HasServer arr) => HasServer ((arr :: a -> b) :> sub) context
840− type ServerT (arr :> sub) _ = TypeError (PartialApplication HasServer arr)
826+ type ServerT (arr :> sub) _ = TypeError (PartialApplication (HasServer :: * -> [*] -> Constraint) arr)
841 route = error "unreachable"
827 route = error "unreachable"
842 hoistServerWithContext _ _ _ _ = error "unreachable"
828 hoistServerWithContext _ _ _ _ = error "unreachable"
@@ -973,84 +959,3 @@ instance
973 toServant server
959 toServant server
974 servantSrvN :: ServerT (ToServantApi api) n =
960 servantSrvN :: ServerT (ToServantApi api) n =
975 hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM
961 hoistServerWithContext (Proxy @(ToServantApi api)) pctx nat servantSrvM
979−-- | 'return' for 'UVerb' handlers. Takes a value of any of the members of the open union,
980−-- and will construct a union value in an 'Applicative' (eg. 'Server').
982− forall (x :: *) (xs :: [*]) (f :: * -> *).
983− (Applicative f, HasStatus x, IsMember x xs) =>
986−respond = pure . inject . I
988−class IsServerResource (cts :: [*]) a where
989− resourceResponse :: Request -> Proxy cts -> a -> Maybe (LBS, LBS)
990− resourceHeaders :: Proxy cts -> a -> [(HeaderName, B.ByteString)]
992−instance {-# OVERLAPPABLE #-} AllCTRender cts a
993− => IsServerResource cts a where
994− resourceResponse request p res = handleAcceptH p (getAcceptHeader request) res
995− resourceHeaders _ _ = []
997−instance {-# OVERLAPPING #-} (IsServerResource cts a, GetHeaders (Headers h a))
998− => IsServerResource cts (Headers h a) where
999− resourceResponse request p res = resourceResponse request p (getResponse res)
1000− resourceHeaders cts res = getHeaders res ++ resourceHeaders cts (getResponse res)
1002−instance {-# OVERLAPPING #-} IsServerResource cts a
1003− => IsServerResource cts (WithStatus n a) where
1004− resourceResponse request p (WithStatus x) = resourceResponse request p x
1005− resourceHeaders cts (WithStatus x) = resourceHeaders cts x
1007−encodeResource :: forall a cts . (IsServerResource cts a, HasStatus a)
1008− => Request -> Proxy cts -> a
1009− -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)])
1010−encodeResource request cts res = (statusOf (Proxy @a),
1011− resourceResponse request cts res,
1012− resourceHeaders cts res)
1014−type IsServerResourceWithStatus cts = IsServerResource cts `And` HasStatus
1017− ( ReflectMethod method,
1018− AllMime contentTypes,
1019− All (IsServerResourceWithStatus contentTypes) as,
1020− Unique (Statuses as) -- for consistency with servant-swagger (server would work fine
1021− -- without; client is a bit of a corner case, because it dispatches
1022− -- the parser based on the status code. with this uniqueness
1023− -- constraint it won't have to run more than one parser in weird
1026− HasServer (UVerb method contentTypes as) context
1028− type ServerT (UVerb method contentTypes as) m = m (Union as)
1030− hoistServerWithContext _ _ nt s = nt s
1034− Proxy (UVerb method contentTypes as) ->
1036− Delayed env (Server (UVerb method contentTypes as)) ->
1038− route _proxy _ctx action = leafRouter route'
1040− method = reflectMethod (Proxy @method)
1041− route' env request cont = do
1042− let action' :: Delayed env (Handler (Union as))
1045− `addMethodCheck` methodCheck method request
1046− `addAcceptCheck` acceptCheck (Proxy @contentTypes) (getAcceptHeader request)
1048− runAction action' env request cont $ \(output :: Union as) -> do
1049− let cts = Proxy @contentTypes
1050− pickResource :: Union as -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)])
1051− pickResource = foldMapUnion (Proxy @(IsServerResourceWithStatus contentTypes)) (encodeResource request cts)
1052− case pickResource output of
1053− (_, Nothing, _) -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does
1054− (status, Just (contentT, body), headers) ->
1055− let bdy = if allowedMethodHead method request then "" else body
1056− in Route $ responseLBS status ((hContentType, cs contentT) : headers) bdy