@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
@@ -13,6 +14,7 @@
14module Servant.Server.Internal
15module Servant.Server.Internal
15 ( module Servant.Server.Internal
16 ( module Servant.Server.Internal
17+ , module Servant.Server.Internal.Config
16 , module Servant.Server.Internal.Router
18 , module Servant.Server.Internal.Router
17 , module Servant.Server.Internal.RoutingApplication
19 , module Servant.Server.Internal.RoutingApplication
18 , module Servant.Server.Internal.ServantErr
20 , module Servant.Server.Internal.ServantErr
@@ -30,6 +32,7 @@ import Data.String (fromString)
30import Data.String.Conversions (cs, (<>))
32import Data.String.Conversions (cs, (<>))
31import Data.Text (Text)
33import Data.Text (Text)
32import Data.Typeable
34import Data.Typeable
35+import GHC.Exts (Constraint)
33import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
36import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
34 symbolVal)
37 symbolVal)
35import Network.HTTP.Types hiding (Header, ResponseHeaders)
38import Network.HTTP.Types hiding (Header, ResponseHeaders)
@@ -49,7 +52,8 @@ import Servant.API ((:<|>) (..), (:>), Capture,
49 Verb, ReflectMethod(reflectMethod),
52 Verb, ReflectMethod(reflectMethod),
50 IsSecure(..), Header,
53 IsSecure(..), Header,
51 QueryFlag, QueryParam, QueryParams,
54 QueryFlag, QueryParam, QueryParams,
52− Raw, RemoteHost, ReqBody, Vault)
55+ Raw, RemoteHost, ReqBody, Vault,
53import Servant.API.ContentTypes (AcceptHeader (..),
57import Servant.API.ContentTypes (AcceptHeader (..),
54 AllCTRender (..),
58 AllCTRender (..),
55 AllCTUnrender (..),
59 AllCTUnrender (..),
@@ -58,6 +62,7 @@ import Servant.API.ContentTypes (AcceptHeader (..),
58import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
62import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
59 getResponse)
63 getResponse)
65+import Servant.Server.Internal.Config
61import Servant.Server.Internal.Router
66import Servant.Server.Internal.Router
62import Servant.Server.Internal.RoutingApplication
67import Servant.Server.Internal.RoutingApplication
63import Servant.Server.Internal.ServantErr
68import Servant.Server.Internal.ServantErr
@@ -65,8 +70,9 @@ import Servant.Server.Internal.ServantErr
66class HasServer layout where
71class HasServer layout where
67 type ServerT layout (m :: * -> *) :: *
72 type ServerT layout (m :: * -> *) :: *
73+ type HasConfig layout (c :: [*]) :: Constraint
69− route :: Proxy layout -> Delayed (Server layout) -> Router
75+ route :: HasConfig layout a => Proxy layout -> Config a -> Delayed (Server layout) -> Router
71type Server layout = ServerT layout (ExceptT ServantErr IO)
77type Server layout = ServerT layout (ExceptT ServantErr IO)
@@ -86,9 +92,10 @@ type Server layout = ServerT layout (ExceptT ServantErr IO)
86instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
92instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
88 type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m
94 type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m
95+ type HasConfig (a :<|> b) c = (HasConfig a c, HasConfig b c)
90− route Proxy server = choice (route pa ((\ (a :<|> _) -> a) <$> server))
97+ route Proxy config server = choice (route pa config ((\ (a :<|> _) -> a) <$> server))
91− (route pb ((\ (_ :<|> b) -> b) <$> server))
98+ (route pb config ((\ (_ :<|> b) -> b) <$> server))
92 where pa = Proxy :: Proxy a
99 where pa = Proxy :: Proxy a
93 pb = Proxy :: Proxy b
100 pb = Proxy :: Proxy b
@@ -117,10 +124,12 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
118 type ServerT (Capture capture a :> sublayout) m =
125 type ServerT (Capture capture a :> sublayout) m =
119 a -> ServerT sublayout m
126 a -> ServerT sublayout m
127+ type HasConfig (Capture capture a :> sublayout) c = (HasConfig sublayout c)
121− route Proxy d =
129+ route Proxy config d =
122 DynamicRouter $ \ first ->
130 DynamicRouter $ \ first ->
123 route (Proxy :: Proxy sublayout)
131 route (Proxy :: Proxy sublayout)
124 (addCapture d $ case captured captureProxy first of
133 (addCapture d $ case captured captureProxy first of
125 Nothing -> return $ Fail err404
134 Nothing -> return $ Fail err404
126 Just v -> return $ Route v
135 Just v -> return $ Route v
@@ -192,8 +201,9 @@ instance OVERLAPPABLE_
192 ) => HasServer (Verb method status ctypes a) where
201 ) => HasServer (Verb method status ctypes a) where
194 type ServerT (Verb method status ctypes a) m = m a
203 type ServerT (Verb method status ctypes a) m = m a
204+ type HasConfig (Verb method status ctypes a) c = ()
196− route Proxy = methodRouter method (Proxy :: Proxy ctypes) status
206+ route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status
197 where method = reflectMethod (Proxy :: Proxy method)
207 where method = reflectMethod (Proxy :: Proxy method)
198 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
208 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
@@ -203,8 +213,9 @@ instance OVERLAPPING_
203 ) => HasServer (Verb method status ctypes (Headers h a)) where
213 ) => HasServer (Verb method status ctypes (Headers h a)) where
205 type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
215 type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
216+ type HasConfig (Verb method status ctypes (Headers h a)) c = ()
207− route Proxy = methodRouterHeaders method (Proxy :: Proxy ctypes) status
218+ route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status
208 where method = reflectMethod (Proxy :: Proxy method)
219 where method = reflectMethod (Proxy :: Proxy method)
209 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
220 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
@@ -233,10 +244,11 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
234 type ServerT (Header sym a :> sublayout) m =
245 type ServerT (Header sym a :> sublayout) m =
235 Maybe a -> ServerT sublayout m
246 Maybe a -> ServerT sublayout m
247+ type HasConfig (Header sym a :> sublayout) c = HasConfig sublayout c
237− route Proxy subserver = WithRequest $ \ request ->
249+ route Proxy config subserver = WithRequest $ \ request ->
238 let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
250 let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
239− in route (Proxy :: Proxy sublayout) (passToServer subserver mheader)
251+ in route (Proxy :: Proxy sublayout) config (passToServer subserver mheader)
240 where str = fromString $ symbolVal (Proxy :: Proxy sym)
252 where str = fromString $ symbolVal (Proxy :: Proxy sym)
242-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
254-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
@@ -265,8 +277,9 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
266 type ServerT (QueryParam sym a :> sublayout) m =
278 type ServerT (QueryParam sym a :> sublayout) m =
267 Maybe a -> ServerT sublayout m
279 Maybe a -> ServerT sublayout m
280+ type HasConfig (QueryParam sym a :> sublayout) c = HasConfig sublayout c
269− route Proxy subserver = WithRequest $ \ request ->
282+ route Proxy config subserver = WithRequest $ \ request ->
270 let querytext = parseQueryText $ rawQueryString request
283 let querytext = parseQueryText $ rawQueryString request
272 case lookup paramname querytext of
285 case lookup paramname querytext of
@@ -274,7 +287,7 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
274 Just Nothing -> Nothing -- param present with no value -> Nothing
287 Just Nothing -> Nothing -- param present with no value -> Nothing
275 Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
288 Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
276 -- the right type
289 -- the right type
277− in route (Proxy :: Proxy sublayout) (passToServer subserver param)
290+ in route (Proxy :: Proxy sublayout) config (passToServer subserver param)
278 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
291 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
280-- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API,
293-- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API,
@@ -301,15 +314,16 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
302 type ServerT (QueryParams sym a :> sublayout) m =
315 type ServerT (QueryParams sym a :> sublayout) m =
303 [a] -> ServerT sublayout m
316 [a] -> ServerT sublayout m
317+ type HasConfig (QueryParams sym a :> sublayout) c = HasConfig sublayout c
305− route Proxy subserver = WithRequest $ \ request ->
319+ route Proxy config subserver = WithRequest $ \ request ->
306 let querytext = parseQueryText $ rawQueryString request
320 let querytext = parseQueryText $ rawQueryString request
307 -- if sym is "foo", we look for query string parameters
321 -- if sym is "foo", we look for query string parameters
308 -- named "foo" or "foo[]" and call parseQueryParam on the
322 -- named "foo" or "foo[]" and call parseQueryParam on the
309 -- corresponding values
323 -- corresponding values
310 parameters = filter looksLikeParam querytext
324 parameters = filter looksLikeParam querytext
311 values = mapMaybe (convert . snd) parameters
325 values = mapMaybe (convert . snd) parameters
312− in route (Proxy :: Proxy sublayout) (passToServer subserver values)
326+ in route (Proxy :: Proxy sublayout) config (passToServer subserver values)
313 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
327 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
314 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
328 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
315 convert Nothing = Nothing
329 convert Nothing = Nothing
@@ -332,14 +346,15 @@ instance (KnownSymbol sym, HasServer sublayout)
333 type ServerT (QueryFlag sym :> sublayout) m =
347 type ServerT (QueryFlag sym :> sublayout) m =
334 Bool -> ServerT sublayout m
348 Bool -> ServerT sublayout m
349+ type HasConfig (QueryFlag sym :> sublayout) c = HasConfig sublayout c
336− route Proxy subserver = WithRequest $ \ request ->
351+ route Proxy config subserver = WithRequest $ \ request ->
337 let querytext = parseQueryText $ rawQueryString request
352 let querytext = parseQueryText $ rawQueryString request
338 param = case lookup paramname querytext of
353 param = case lookup paramname querytext of
339 Just Nothing -> True -- param is there, with no value
354 Just Nothing -> True -- param is there, with no value
340 Just (Just v) -> examine v -- param with a value
355 Just (Just v) -> examine v -- param with a value
341 Nothing -> False -- param not in the query string
356 Nothing -> False -- param not in the query string
342− in route (Proxy :: Proxy sublayout) (passToServer subserver param)
357+ in route (Proxy :: Proxy sublayout) config (passToServer subserver param)
343 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
358 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
344 examine v | v == "true" || v == "1" || v == "" = True
359 examine v | v == "true" || v == "1" || v == "" = True
345 | otherwise = False
360 | otherwise = False
@@ -355,8 +370,9 @@ instance (KnownSymbol sym, HasServer sublayout)
355instance HasServer Raw where
370instance HasServer Raw where
357 type ServerT Raw m = Application
372 type ServerT Raw m = Application
373+ type HasConfig Raw c = ()
359− route Proxy rawApplication = LeafRouter $ \ request respond -> do
375+ route Proxy _ rawApplication = LeafRouter $ \ request respond -> do
360 r <- runDelayed rawApplication
376 r <- runDelayed rawApplication
361 case r of
377 case r of
362 Route app -> app request (respond . Route)
378 Route app -> app request (respond . Route)
@@ -389,9 +405,10 @@ instance ( AllCTUnrender list a, HasServer sublayout
390 type ServerT (ReqBody list a :> sublayout) m =
406 type ServerT (ReqBody list a :> sublayout) m =
391 a -> ServerT sublayout m
407 a -> ServerT sublayout m
408+ type HasConfig (ReqBody list a :> sublayout) c = HasConfig sublayout c
393− route Proxy subserver = WithRequest $ \ request ->
410+ route Proxy config subserver = WithRequest $ \ request ->
394− route (Proxy :: Proxy sublayout) (addBodyCheck subserver (bodyCheck request))
411+ route (Proxy :: Proxy sublayout) config (addBodyCheck subserver (bodyCheck request))
396 bodyCheck request = do
413 bodyCheck request = do
397 -- See HTTP RFC 2616, section 7.2.1
414 -- See HTTP RFC 2616, section 7.2.1
@@ -412,37 +429,42 @@ instance ( AllCTUnrender list a, HasServer sublayout
412instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where
429instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where
414 type ServerT (path :> sublayout) m = ServerT sublayout m
431 type ServerT (path :> sublayout) m = ServerT sublayout m
432+ type HasConfig (path :> sublayout) c = HasConfig sublayout c
416− route Proxy subserver = StaticRouter $
434+ route Proxy config subserver = StaticRouter $
417 M.singleton (cs (symbolVal proxyPath))
435 M.singleton (cs (symbolVal proxyPath))
418− (route (Proxy :: Proxy sublayout) subserver)
436+ (route (Proxy :: Proxy sublayout) config subserver)
419 where proxyPath = Proxy :: Proxy path
437 where proxyPath = Proxy :: Proxy path
421instance HasServer api => HasServer (RemoteHost :> api) where
439instance HasServer api => HasServer (RemoteHost :> api) where
422 type ServerT (RemoteHost :> api) m = SockAddr -> ServerT api m
440 type ServerT (RemoteHost :> api) m = SockAddr -> ServerT api m
441+ type HasConfig (RemoteHost :> api) c = HasConfig api c
424− route Proxy subserver = WithRequest $ \req ->
443+ route Proxy config subserver = WithRequest $ \req ->
425− route (Proxy :: Proxy api) (passToServer subserver $ remoteHost req)
444+ route (Proxy :: Proxy api) config (passToServer subserver $ remoteHost req)
427instance HasServer api => HasServer (IsSecure :> api) where
446instance HasServer api => HasServer (IsSecure :> api) where
428 type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m
447 type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m
448+ type HasConfig (IsSecure :> api) c = HasConfig api c
430− route Proxy subserver = WithRequest $ \req ->
450+ route Proxy config subserver = WithRequest $ \req ->
431− route (Proxy :: Proxy api) (passToServer subserver $ secure req)
451+ route (Proxy :: Proxy api) config (passToServer subserver $ secure req)
433 where secure req = if isSecure req then Secure else NotSecure
453 where secure req = if isSecure req then Secure else NotSecure
435instance HasServer api => HasServer (Vault :> api) where
455instance HasServer api => HasServer (Vault :> api) where
436 type ServerT (Vault :> api) m = Vault -> ServerT api m
456 type ServerT (Vault :> api) m = Vault -> ServerT api m
457+ type HasConfig (Vault :> api) c = HasConfig api c
438− route Proxy subserver = WithRequest $ \req ->
459+ route Proxy config subserver = WithRequest $ \req ->
439− route (Proxy :: Proxy api) (passToServer subserver $ vault req)
460+ route (Proxy :: Proxy api) config (passToServer subserver $ vault req)
441instance HasServer api => HasServer (HttpVersion :> api) where
462instance HasServer api => HasServer (HttpVersion :> api) where
442 type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m
463 type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m
464+ type HasConfig (HttpVersion :> api) c = HasConfig api c
444− route Proxy subserver = WithRequest $ \req ->
466+ route Proxy config subserver = WithRequest $ \req ->
445− route (Proxy :: Proxy api) (passToServer subserver $ httpVersion req)
467+ route (Proxy :: Proxy api) config (passToServer subserver $ httpVersion req)
447pathIsEmpty :: Request -> Bool
469pathIsEmpty :: Request -> Bool
448pathIsEmpty = go . pathInfo
470pathIsEmpty = go . pathInfo
@@ -452,3 +474,20 @@ pathIsEmpty = go . pathInfo
453ct_wildcard :: B.ByteString
475ct_wildcard :: B.ByteString
454ct_wildcard = "*" <> "/" <> "*" -- Because CPP
476ct_wildcard = "*" <> "/" <> "*" -- Because CPP
480+instance HasServer subApi => HasServer (WithNamedConfig name subConfig subApi) where
481+ type ServerT (WithNamedConfig name subConfig subApi) m =
483+ type HasConfig (WithNamedConfig name subConfig subApi) config =
484+ (HasConfigEntry config (NamedConfig name subConfig), HasConfig subApi subConfig)
486+ route Proxy config delayed =
487+ route subProxy subConfig delayed
489+ subProxy :: Proxy subApi
492+ subConfig :: Config subConfig
493+ subConfig = descendIntoNamedConfig (Proxy :: Proxy name) config