@@ -1,4 +1,5 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleContexts #-}
45{-# LANGUAGE FlexibleInstances #-}
@@ -13,6 +14,7 @@
1314
1415module Servant.Server.Internal
1516 ( module Servant.Server.Internal
17+ , module Servant.Server.Internal.Config
1618 , module Servant.Server.Internal.Router
1719 , module Servant.Server.Internal.RoutingApplication
1820 , module Servant.Server.Internal.ServantErr
@@ -30,6 +32,7 @@ import Data.String (fromString)
3032import Data.String.Conversions (cs, (<>))
3133import Data.Text (Text)
3234import Data.Typeable
35+import GHC.Exts (Constraint)
3336import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
3437 symbolVal)
3538import Network.HTTP.Types hiding (Header, ResponseHeaders)
@@ -49,7 +52,8 @@ import Servant.API ((:<|>) (..), (:>), Capture,
4952 Verb, ReflectMethod(reflectMethod),
5053 IsSecure(..), Header,
5154 QueryFlag, QueryParam, QueryParams,
52− Raw, RemoteHost, ReqBody, Vault)
55+ Raw, RemoteHost, ReqBody, Vault,
56+ WithNamedConfig)
5357import Servant.API.ContentTypes (AcceptHeader (..),
5458 AllCTRender (..),
5559 AllCTUnrender (..),
@@ -58,6 +62,7 @@ import Servant.API.ContentTypes (AcceptHeader (..),
5862import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
5963 getResponse)
6064
65+import Servant.Server.Internal.Config
6166import Servant.Server.Internal.Router
6267import Servant.Server.Internal.RoutingApplication
6368import Servant.Server.Internal.ServantErr
@@ -65,8 +70,9 @@ import Servant.Server.Internal.ServantErr
6570
6671class HasServer layout where
6772 type ServerT layout (m :: * -> *) :: *
73+ type HasConfig layout (c :: [*]) :: Constraint
6874
69− route :: Proxy layout -> Delayed (Server layout) -> Router
75+ route :: HasConfig layout a => Proxy layout -> Config a -> Delayed (Server layout) -> Router
7076
7177type Server layout = ServerT layout (ExceptT ServantErr IO)
7278
@@ -86,9 +92,10 @@ type Server layout = ServerT layout (ExceptT ServantErr IO)
8692instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
8793
8894 type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m
95+ type HasConfig (a :<|> b) c = (HasConfig a c, HasConfig b c)
8996
90− route Proxy server = choice (route pa ((\ (a :<|> _) -> a) <$> server))
91− (route pb ((\ (_ :<|> b) -> b) <$> server))
97+ route Proxy config server = choice (route pa config ((\ (a :<|> _) -> a) <$> server))
98+ (route pb config ((\ (_ :<|> b) -> b) <$> server))
9299 where pa = Proxy :: Proxy a
93100 pb = Proxy :: Proxy b
94101
@@ -117,10 +124,12 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
117124
118125 type ServerT (Capture capture a :> sublayout) m =
119126 a -> ServerT sublayout m
127+ type HasConfig (Capture capture a :> sublayout) c = (HasConfig sublayout c)
120128
121− route Proxy d =
129+ route Proxy config d =
122130 DynamicRouter $ \ first ->
123131 route (Proxy :: Proxy sublayout)
132+ config
124133 (addCapture d $ case captured captureProxy first of
125134 Nothing -> return $ Fail err404
126135 Just v -> return $ Route v
@@ -192,8 +201,9 @@ instance OVERLAPPABLE_
192201 ) => HasServer (Verb method status ctypes a) where
193202
194203 type ServerT (Verb method status ctypes a) m = m a
204+ type HasConfig (Verb method status ctypes a) c = ()
195205
196− route Proxy = methodRouter method (Proxy :: Proxy ctypes) status
206+ route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status
197207 where method = reflectMethod (Proxy :: Proxy method)
198208 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
199209
@@ -203,8 +213,9 @@ instance OVERLAPPING_
203213 ) => HasServer (Verb method status ctypes (Headers h a)) where
204214
205215 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 = ()
206217
207− route Proxy = methodRouterHeaders method (Proxy :: Proxy ctypes) status
218+ route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status
208219 where method = reflectMethod (Proxy :: Proxy method)
209220 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
210221
@@ -233,10 +244,11 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
233244
234245 type ServerT (Header sym a :> sublayout) m =
235246 Maybe a -> ServerT sublayout m
247+ type HasConfig (Header sym a :> sublayout) c = HasConfig sublayout c
236248
237− route Proxy subserver = WithRequest $ \ request ->
249+ route Proxy config subserver = WithRequest $ \ request ->
238250 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)
240252 where str = fromString $ symbolVal (Proxy :: Proxy sym)
241253
242254-- | 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)
265277
266278 type ServerT (QueryParam sym a :> sublayout) m =
267279 Maybe a -> ServerT sublayout m
280+ type HasConfig (QueryParam sym a :> sublayout) c = HasConfig sublayout c
268281
269− route Proxy subserver = WithRequest $ \ request ->
282+ route Proxy config subserver = WithRequest $ \ request ->
270283 let querytext = parseQueryText $ rawQueryString request
271284 param =
272285 case lookup paramname querytext of
@@ -274,7 +287,7 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
274287 Just Nothing -> Nothing -- param present with no value -> Nothing
275288 Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
276289 -- the right type
277− in route (Proxy :: Proxy sublayout) (passToServer subserver param)
290+ in route (Proxy :: Proxy sublayout) config (passToServer subserver param)
278291 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
279292
280293-- | 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)
301314
302315 type ServerT (QueryParams sym a :> sublayout) m =
303316 [a] -> ServerT sublayout m
317+ type HasConfig (QueryParams sym a :> sublayout) c = HasConfig sublayout c
304318
305− route Proxy subserver = WithRequest $ \ request ->
319+ route Proxy config subserver = WithRequest $ \ request ->
306320 let querytext = parseQueryText $ rawQueryString request
307321 -- if sym is "foo", we look for query string parameters
308322 -- named "foo" or "foo[]" and call parseQueryParam on the
309323 -- corresponding values
310324 parameters = filter looksLikeParam querytext
311325 values = mapMaybe (convert . snd) parameters
312− in route (Proxy :: Proxy sublayout) (passToServer subserver values)
326+ in route (Proxy :: Proxy sublayout) config (passToServer subserver values)
313327 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
314328 looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
315329 convert Nothing = Nothing
@@ -332,14 +346,15 @@ instance (KnownSymbol sym, HasServer sublayout)
332346
333347 type ServerT (QueryFlag sym :> sublayout) m =
334348 Bool -> ServerT sublayout m
349+ type HasConfig (QueryFlag sym :> sublayout) c = HasConfig sublayout c
335350
336− route Proxy subserver = WithRequest $ \ request ->
351+ route Proxy config subserver = WithRequest $ \ request ->
337352 let querytext = parseQueryText $ rawQueryString request
338353 param = case lookup paramname querytext of
339354 Just Nothing -> True -- param is there, with no value
340355 Just (Just v) -> examine v -- param with a value
341356 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)
343358 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
344359 examine v | v == "true" || v == "1" || v == "" = True
345360 | otherwise = False
@@ -355,8 +370,9 @@ instance (KnownSymbol sym, HasServer sublayout)
355370instance HasServer Raw where
356371
357372 type ServerT Raw m = Application
373+ type HasConfig Raw c = ()
358374
359− route Proxy rawApplication = LeafRouter $ \ request respond -> do
375+ route Proxy _ rawApplication = LeafRouter $ \ request respond -> do
360376 r <- runDelayed rawApplication
361377 case r of
362378 Route app -> app request (respond . Route)
@@ -389,9 +405,10 @@ instance ( AllCTUnrender list a, HasServer sublayout
389405
390406 type ServerT (ReqBody list a :> sublayout) m =
391407 a -> ServerT sublayout m
408+ type HasConfig (ReqBody list a :> sublayout) c = HasConfig sublayout c
392409
393− route Proxy subserver = WithRequest $ \ request ->
394− route (Proxy :: Proxy sublayout) (addBodyCheck subserver (bodyCheck request))
410+ route Proxy config subserver = WithRequest $ \ request ->
411+ route (Proxy :: Proxy sublayout) config (addBodyCheck subserver (bodyCheck request))
395412 where
396413 bodyCheck request = do
397414 -- See HTTP RFC 2616, section 7.2.1
@@ -412,37 +429,42 @@ instance ( AllCTUnrender list a, HasServer sublayout
412429instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where
413430
414431 type ServerT (path :> sublayout) m = ServerT sublayout m
432+ type HasConfig (path :> sublayout) c = HasConfig sublayout c
415433
416− route Proxy subserver = StaticRouter $
434+ route Proxy config subserver = StaticRouter $
417435 M.singleton (cs (symbolVal proxyPath))
418− (route (Proxy :: Proxy sublayout) subserver)
436+ (route (Proxy :: Proxy sublayout) config subserver)
419437 where proxyPath = Proxy :: Proxy path
420438
421439instance HasServer api => HasServer (RemoteHost :> api) where
422440 type ServerT (RemoteHost :> api) m = SockAddr -> ServerT api m
441+ type HasConfig (RemoteHost :> api) c = HasConfig api c
423442
424− route Proxy subserver = WithRequest $ \req ->
425− route (Proxy :: Proxy api) (passToServer subserver $ remoteHost req)
443+ route Proxy config subserver = WithRequest $ \req ->
444+ route (Proxy :: Proxy api) config (passToServer subserver $ remoteHost req)
426445
427446instance HasServer api => HasServer (IsSecure :> api) where
428447 type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m
448+ type HasConfig (IsSecure :> api) c = HasConfig api c
429449
430− route Proxy subserver = WithRequest $ \req ->
431− route (Proxy :: Proxy api) (passToServer subserver $ secure req)
450+ route Proxy config subserver = WithRequest $ \req ->
451+ route (Proxy :: Proxy api) config (passToServer subserver $ secure req)
432452
433453 where secure req = if isSecure req then Secure else NotSecure
434454
435455instance HasServer api => HasServer (Vault :> api) where
436456 type ServerT (Vault :> api) m = Vault -> ServerT api m
457+ type HasConfig (Vault :> api) c = HasConfig api c
437458
438− route Proxy subserver = WithRequest $ \req ->
439− route (Proxy :: Proxy api) (passToServer subserver $ vault req)
459+ route Proxy config subserver = WithRequest $ \req ->
460+ route (Proxy :: Proxy api) config (passToServer subserver $ vault req)
440461
441462instance HasServer api => HasServer (HttpVersion :> api) where
442463 type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m
464+ type HasConfig (HttpVersion :> api) c = HasConfig api c
443465
444− route Proxy subserver = WithRequest $ \req ->
445− route (Proxy :: Proxy api) (passToServer subserver $ httpVersion req)
466+ route Proxy config subserver = WithRequest $ \req ->
467+ route (Proxy :: Proxy api) config (passToServer subserver $ httpVersion req)
446468
447469pathIsEmpty :: Request -> Bool
448470pathIsEmpty = go . pathInfo
@@ -452,3 +474,20 @@ pathIsEmpty = go . pathInfo
452474
453475ct_wildcard :: B.ByteString
454476ct_wildcard = "*" <> "/" <> "*" -- Because CPP
477+
478+-- * configs
479+
480+instance HasServer subApi => HasServer (WithNamedConfig name subConfig subApi) where
481+ type ServerT (WithNamedConfig name subConfig subApi) m =
482+ ServerT subApi m
483+ type HasConfig (WithNamedConfig name subConfig subApi) config =
484+ (HasConfigEntry config (NamedConfig name subConfig), HasConfig subApi subConfig)
485+
486+ route Proxy config delayed =
487+ route subProxy subConfig delayed
488+ where
489+ subProxy :: Proxy subApi
490+ subProxy = Proxy
491+
492+ subConfig :: Config subConfig
493+ subConfig = descendIntoNamedConfig (Proxy :: Proxy name) config