packages feed

servant-snap 0.7.3 → 0.8

raw patch · 8 files changed

+316/−86 lines, 8 filesdep +word8dep ~basedep ~base64-bytestringdep ~io-streamsPVP ok

version bump matches the API change (PVP)

Dependencies added: word8

Dependency ranges changed: base, base64-bytestring, io-streams

API changes (from Hackage documentation)

- Servant.Server.Internal: instance (Servant.Server.Internal.HasServer a, Servant.Server.Internal.HasServer b) => Servant.Server.Internal.HasServer (a Servant.API.Alternative.:<|> b)
- Servant.Server.Internal: instance Servant.Server.Internal.HasServer Servant.API.Raw.Raw
- Servant.Server.Internal: instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Servant.API.IsSecure.IsSecure Servant.API.Sub.:> api)
- Servant.Server.Internal: instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Servant.API.RemoteHost.RemoteHost Servant.API.Sub.:> api)
- Servant.Server.Internal: instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Snap.Internal.Http.Types.HttpVersion Servant.API.Sub.:> api)
- Servant.Server.Internal: instance forall k (list :: [GHC.Types.*]) a (sublayout :: k). (Servant.API.ContentTypes.AllCTUnrender list a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.ReqBody.ReqBody list a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k (path :: GHC.Types.Symbol) (sublayout :: k). (GHC.TypeLits.KnownSymbol path, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (path Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryFlag sym Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Header.Header sym a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParam sym a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k a (sublayout :: k) (capture :: GHC.Types.Symbol). (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Capture.Capture capture a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k a (sublayout :: k) (capture :: GHC.Types.Symbol). (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Capture.CaptureAll capture a Servant.API.Sub.:> sublayout)
- Servant.Server.Internal: instance forall k1 (ctypes :: [GHC.Types.*]) a (method :: k1) (status :: GHC.Types.Nat) (h :: [*]). (Servant.API.ContentTypes.AllCTRender ctypes a, Servant.API.Verbs.ReflectMethod method, GHC.TypeLits.KnownNat status, Servant.API.ResponseHeaders.GetHeaders (Servant.API.ResponseHeaders.Headers h a)) => Servant.Server.Internal.HasServer (Servant.API.Verbs.Verb method status ctypes (Servant.API.ResponseHeaders.Headers h a))
- Servant.Server.Internal: instance forall k1 (ctypes :: [GHC.Types.*]) a (method :: k1) (status :: GHC.Types.Nat). (Servant.API.ContentTypes.AllCTRender ctypes a, Servant.API.Verbs.ReflectMethod method, GHC.TypeLits.KnownNat status) => Servant.Server.Internal.HasServer (Servant.API.Verbs.Verb method status ctypes a)
+ Servant.Server: serveSnapWithContext :: forall layout context m. (HasServer layout context m, MonadSnap m) => Proxy layout -> Context context -> Server layout context m -> m ()
+ Servant.Server.Internal: Authorized :: usr -> BasicAuthResult usr
+ Servant.Server.Internal: BadPassword :: BasicAuthResult usr
+ Servant.Server.Internal: NoSuchUser :: BasicAuthResult usr
+ Servant.Server.Internal: Unauthorized :: BasicAuthResult usr
+ Servant.Server.Internal: data BasicAuthResult usr
+ Servant.Server.Internal: instance (Servant.Server.Internal.HasServer a ctx m, Servant.Server.Internal.HasServer b ctx m) => Servant.Server.Internal.HasServer (a Servant.API.Alternative.:<|> b) ctx m
+ Servant.Server.Internal: instance GHC.Base.Functor Servant.Server.Internal.BasicAuthResult
+ Servant.Server.Internal: instance GHC.Classes.Eq usr => GHC.Classes.Eq (Servant.Server.Internal.BasicAuthResult usr)
+ Servant.Server.Internal: instance GHC.Generics.Generic (Servant.Server.Internal.BasicAuthResult usr)
+ Servant.Server.Internal: instance GHC.Read.Read usr => GHC.Read.Read (Servant.Server.Internal.BasicAuthResult usr)
+ Servant.Server.Internal: instance GHC.Show.Show usr => GHC.Show.Show (Servant.Server.Internal.BasicAuthResult usr)
+ Servant.Server.Internal: instance Servant.Server.Internal.HasServer Servant.API.Raw.Raw context m
+ Servant.Server.Internal: instance forall k (api :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*) (realm :: GHC.Types.Symbol) usr. (Servant.Server.Internal.HasServer api context m, GHC.TypeLits.KnownSymbol realm, Servant.Server.Internal.Context.HasContextEntry context (Servant.Server.Internal.BasicAuth.BasicAuthCheck m usr)) => Servant.Server.Internal.HasServer (Servant.API.BasicAuth.BasicAuth realm usr Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance forall k (api :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). Servant.Server.Internal.HasServer api context m => Servant.Server.Internal.HasServer (Servant.API.IsSecure.IsSecure Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance forall k (api :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). Servant.Server.Internal.HasServer api context m => Servant.Server.Internal.HasServer (Servant.API.RemoteHost.RemoteHost Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance forall k (api :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). Servant.Server.Internal.HasServer api context m => Servant.Server.Internal.HasServer (Snap.Internal.Http.Types.HttpVersion Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance forall k (list :: [GHC.Types.*]) a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (Servant.API.ContentTypes.AllCTUnrender list a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.ReqBody.ReqBody list a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k (path :: GHC.Types.Symbol) (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (GHC.TypeLits.KnownSymbol path, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (path Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (GHC.TypeLits.KnownSymbol sym, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryFlag sym Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.Header.Header sym a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParam sym a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*) (capture :: GHC.Types.Symbol). (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.Capture.Capture capture a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k a (sublayout :: k) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*) (capture :: GHC.Types.Symbol). (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout context m) => Servant.Server.Internal.HasServer (Servant.API.Capture.CaptureAll capture a Servant.API.Sub.:> sublayout) context m
+ Servant.Server.Internal: instance forall k1 (ctypes :: [GHC.Types.*]) a (method :: k1) (status :: GHC.Types.Nat) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (Servant.API.ContentTypes.AllCTRender ctypes a, Servant.API.Verbs.ReflectMethod method, GHC.TypeLits.KnownNat status) => Servant.Server.Internal.HasServer (Servant.API.Verbs.Verb method status ctypes a) context m
+ Servant.Server.Internal: instance forall k1 (ctypes :: [GHC.Types.*]) a (method :: k1) (status :: GHC.Types.Nat) (h :: [*]) (context :: [*]) (m :: GHC.Types.* -> GHC.Types.*). (Servant.API.ContentTypes.AllCTRender ctypes a, Servant.API.Verbs.ReflectMethod method, GHC.TypeLits.KnownNat status, Servant.API.ResponseHeaders.GetHeaders (Servant.API.ResponseHeaders.Headers h a)) => Servant.Server.Internal.HasServer (Servant.API.Verbs.Verb method status ctypes (Servant.API.ResponseHeaders.Headers h a)) context m
+ Servant.Server.Internal.BasicAuth: Authorized :: usr -> BasicAuthResult usr
+ Servant.Server.Internal.BasicAuth: BadPassword :: BasicAuthResult usr
+ Servant.Server.Internal.BasicAuth: BasicAuthCheck :: (BasicAuthData -> m (BasicAuthResult usr)) -> BasicAuthCheck m usr
+ Servant.Server.Internal.BasicAuth: NoSuchUser :: BasicAuthResult usr
+ Servant.Server.Internal.BasicAuth: Unauthorized :: BasicAuthResult usr
+ Servant.Server.Internal.BasicAuth: [unBasicAuthCheck] :: BasicAuthCheck m usr -> BasicAuthData -> m (BasicAuthResult usr)
+ Servant.Server.Internal.BasicAuth: data BasicAuthResult usr
+ Servant.Server.Internal.BasicAuth: decodeBAHdr :: Request -> Maybe BasicAuthData
+ Servant.Server.Internal.BasicAuth: instance GHC.Base.Functor Servant.Server.Internal.BasicAuth.BasicAuthResult
+ Servant.Server.Internal.BasicAuth: instance GHC.Base.Functor m => GHC.Base.Functor (Servant.Server.Internal.BasicAuth.BasicAuthCheck m)
+ Servant.Server.Internal.BasicAuth: instance GHC.Classes.Eq usr => GHC.Classes.Eq (Servant.Server.Internal.BasicAuth.BasicAuthResult usr)
+ Servant.Server.Internal.BasicAuth: instance GHC.Generics.Generic (Servant.Server.Internal.BasicAuth.BasicAuthCheck m usr)
+ Servant.Server.Internal.BasicAuth: instance GHC.Generics.Generic (Servant.Server.Internal.BasicAuth.BasicAuthResult usr)
+ Servant.Server.Internal.BasicAuth: instance GHC.Read.Read usr => GHC.Read.Read (Servant.Server.Internal.BasicAuth.BasicAuthResult usr)
+ Servant.Server.Internal.BasicAuth: instance GHC.Show.Show usr => GHC.Show.Show (Servant.Server.Internal.BasicAuth.BasicAuthResult usr)
+ Servant.Server.Internal.BasicAuth: mkBAChallengerHdr :: ByteString -> (CI ByteString, ByteString)
+ Servant.Server.Internal.BasicAuth: newtype BasicAuthCheck m usr
+ Servant.Server.Internal.BasicAuth: runBasicAuth :: MonadSnap m => Request -> ByteString -> BasicAuthCheck m usr -> DelayedM m usr
+ Servant.Server.Internal.Context: NamedContext :: (Context subContext) -> NamedContext
+ Servant.Server.Internal.Context: [:.] :: x -> Context xs -> Context (x : xs)
+ Servant.Server.Internal.Context: [EmptyContext] :: Context '[]
+ Servant.Server.Internal.Context: class HasContextEntry (context :: [*]) (val :: *)
+ Servant.Server.Internal.Context: data Context contextTypes
+ Servant.Server.Internal.Context: data NamedContext (name :: Symbol) (subContext :: [*])
+ Servant.Server.Internal.Context: descendIntoNamedContext :: forall context name subContext. HasContextEntry context (NamedContext name subContext) => Proxy (name :: Symbol) -> Context context -> Context subContext
+ Servant.Server.Internal.Context: getContextEntry :: HasContextEntry context val => Context context -> val
+ Servant.Server.Internal.Context: instance (GHC.Classes.Eq a, GHC.Classes.Eq (Servant.Server.Internal.Context.Context as)) => GHC.Classes.Eq (Servant.Server.Internal.Context.Context (a : as))
+ Servant.Server.Internal.Context: instance (GHC.Show.Show a, GHC.Show.Show (Servant.Server.Internal.Context.Context as)) => GHC.Show.Show (Servant.Server.Internal.Context.Context (a : as))
+ Servant.Server.Internal.Context: instance GHC.Classes.Eq (Servant.Server.Internal.Context.Context '[])
+ Servant.Server.Internal.Context: instance GHC.Show.Show (Servant.Server.Internal.Context.Context '[])
+ Servant.Server.Internal.Context: instance Servant.Server.Internal.Context.HasContextEntry (val : xs) val
+ Servant.Server.Internal.Context: instance Servant.Server.Internal.Context.HasContextEntry xs val => Servant.Server.Internal.Context.HasContextEntry (notIt : xs) val
- Servant.Server: class HasServer api where type ServerT api (m :: * -> *) :: * where {
+ Servant.Server: class HasServer api context (m :: * -> *) where type ServerT api context m :: * where {
- Servant.Server: route :: (HasServer api, MonadSnap m) => Proxy api -> Delayed m env (Server api m) -> Router m env
+ Servant.Server: route :: (HasServer api context m, MonadSnap m) => Proxy api -> Context context -> Delayed m env (Server api context m) -> Router m env
- Servant.Server: serveSnap :: forall layout m. (HasServer layout, MonadSnap m) => Proxy layout -> Server layout m -> m ()
+ Servant.Server: serveSnap :: forall layout m. (HasServer layout '[] m, MonadSnap m) => Proxy layout -> Server layout '[] m -> m ()
- Servant.Server: type Server api m = ServerT api m
+ Servant.Server: type Server api context m = ServerT api context m
- Servant.Server: type family ServerT api (m :: * -> *) :: *;
+ Servant.Server: type family ServerT api context m :: *;
- Servant.Server.Internal: class HasServer api where type ServerT api (m :: * -> *) :: * where {
+ Servant.Server.Internal: class HasServer api context (m :: * -> *) where type ServerT api context m :: * where {
- Servant.Server.Internal: route :: (HasServer api, MonadSnap m) => Proxy api -> Delayed m env (Server api m) -> Router m env
+ Servant.Server.Internal: route :: (HasServer api context m, MonadSnap m) => Proxy api -> Context context -> Delayed m env (Server api context m) -> Router m env
- Servant.Server.Internal: type Server api m = ServerT api m
+ Servant.Server.Internal: type Server api context m = ServerT api context m
- Servant.Server.Internal: type family ServerT api (m :: * -> *) :: *;
+ Servant.Server.Internal: type family ServerT api context m :: *;
- Servant.Utils.StaticFiles: serveDirectory :: MonadSnap m => FilePath -> Server Raw m
+ Servant.Utils.StaticFiles: serveDirectory :: MonadSnap m => FilePath -> Server Raw context m

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+0.8+-------++Copy BasicAuth and Context from servant-server to support basic auth checking+ 0.7.1 ------- 
example/greet.hs view
@@ -78,7 +78,7 @@ -- -- Each handler runs in the 'AppHandler' monad. -server :: Server (TestApi AppHandler) AppHandler+server :: Server (TestApi AppHandler) '[] AppHandler server = helloH     :<|> helloH'     :<|> postGreetH
servant-snap.cabal view
@@ -1,5 +1,5 @@ name:                servant-snap-version:             0.7.3+version:             0.8 synopsis:            A family of combinators for defining webservices APIs and serving them description:   Interpret a Servant API as a Snap server, using any Snaplets you like.@@ -36,6 +36,8 @@     Servant.Server     Servant.Server.Internal     Servant.Server.Internal.PathInfo+    Servant.Server.Internal.BasicAuth+    Servant.Server.Internal.Context     Servant.Server.Internal.Router     Servant.Server.Internal.RoutingApplication     Servant.Server.Internal.ServantErr@@ -45,6 +47,7 @@         base               >= 4.7  && < 4.11       , aeson              >= 0.7  && < 1.3       , attoparsec         >= 0.12 && < 0.14+      , base64-bytestring  >= 1.0  && < 1.1       , bytestring         >= 0.10 && < 0.11       , case-insensitive   >= 1.2  && < 1.3       , containers         >= 0.5  && < 0.6@@ -52,7 +55,7 @@       , filepath           >= 1    && < 1.5       , http-types         >= 0.8  && < 0.10       , http-api-data      >= 0.2  && < 0.4-      , io-streams         >= 1.3  && < 1.4+      , io-streams         >= 1.3  && < 1.5       , network-uri        >= 2.6  && < 2.7       , mtl                >= 2.0  && < 2.3       , mmorph             >= 1    && < 1.2@@ -63,6 +66,7 @@       , snap-core          >= 1.0  && < 1.1       , snap-server        >= 1.0  && < 1.1       , transformers       >= 0.3  && < 0.6+      , word8              >= 0.1  && < 0.2   hs-source-dirs: src   default-language: Haskell2010   ghc-options: -Wall@@ -107,6 +111,7 @@     , either     , exceptions     , hspec              >= 2.4.3   && < 2.5+    , lens     , process     , digestive-functors >= 0.8.1.0 && < 0.9     , time
src/Servant/Server.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP               #-}+{-# LANGUAGE DataKinds         #-} {-# LANGUAGE FlexibleContexts  #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes        #-}@@ -9,11 +10,16 @@ module Servant.Server   ( -- * Run a snap handler from an API     serveSnap+  , serveSnapWithContext    , -- * Handlers for all standard combinators     HasServer(..)   , Server +  , -- * Reexports+    module Servant.Server.Internal.BasicAuth+  , module Servant.Server.Internal.Context+     -- ** Basic functions and datatypes      -- * Default error type@@ -57,6 +63,8 @@  import           Data.Proxy                        (Proxy(..)) import           Servant.Server.Internal+import           Servant.Server.Internal.BasicAuth+import           Servant.Server.Internal.Context import           Servant.Server.Internal.SnapShims import           Snap.Core                         hiding (route) @@ -86,15 +94,24 @@ --  serveApplication-  :: forall layout m.(HasServer layout, MonadSnap m)+  :: forall layout context m.(HasServer layout context m, MonadSnap m)   => Proxy layout-  -> Server layout m+  -> Context context+  -> Server layout context m   -> Application m-serveApplication p server = toApplication (runRouter (route p (emptyDelayed (Proxy :: Proxy (m :: * -> *)) ((Route server)))))+serveApplication p ctx server = toApplication (runRouter (route p ctx (emptyDelayed (Proxy :: Proxy (m :: * -> *)) ((Route server))))) +serveSnapWithContext+  :: forall layout context m.(HasServer layout context m, MonadSnap m)+  => Proxy layout+  -> Context context+  -> Server layout context m+  -> m ()+serveSnapWithContext p ctx server = applicationToSnap $ serveApplication p ctx server+ serveSnap-  :: forall layout m.(HasServer layout, MonadSnap m)+  :: forall layout m.(HasServer layout '[] m, MonadSnap m)   => Proxy layout-  -> Server layout m+  -> Server layout '[] m   -> m ()-serveSnap p server = applicationToSnap $ serveApplication p server+serveSnap p server = serveSnapWithContext p EmptyContext server
src/Servant/Server/Internal.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE CPP                  #-} {-# LANGUAGE DataKinds            #-}+{-# LANGUAGE DeriveFunctor        #-}+{-# LANGUAGE DeriveGeneric        #-} {-# LANGUAGE FlexibleContexts     #-} {-# LANGUAGE FlexibleInstances    #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}@@ -33,6 +35,7 @@ import           Data.String                 (fromString) import           Data.String.Conversions     (cs, (<>)) import           Data.Text                   (Text)+import           GHC.Generics import           GHC.TypeLits                (KnownNat, KnownSymbol, natVal,                                               symbolVal) import           Network.HTTP.Types          (HeaderName, Method,@@ -47,7 +50,8 @@ import           Snap.Core                   hiding (Headers, Method,                                               getResponse, headers, route,                                               method, withRequest)-import           Servant.API                 ((:<|>) (..), (:>), Capture,+import           Servant.API                 ((:<|>) (..), (:>), BasicAuth,+                                              Capture,                                               CaptureAll, Header,                                               IsSecure(..), QueryFlag,                                               QueryParam, QueryParams, Raw,@@ -60,21 +64,25 @@                                               getHeaders) -- import           Servant.Common.Text         (FromText, fromText) +import           Servant.Server.Internal.BasicAuth+import           Servant.Server.Internal.Context import           Servant.Server.Internal.PathInfo import           Servant.Server.Internal.Router import           Servant.Server.Internal.RoutingApplication import           Servant.Server.Internal.ServantErr import           Servant.Server.Internal.SnapShims -class HasServer api where-  type ServerT api (m :: * -> *) :: *+-- TODO: add MonadSnap m => m ?+class HasServer api context (m :: * -> *) where+  type ServerT api context m :: *    route :: MonadSnap m         => Proxy api-        -> Delayed m env (Server api m)+        -> Context context+        -> Delayed m env (Server api context m)         -> Router m env -type Server api m = ServerT api m+type Server api context m = ServerT api context m  -- * Instances @@ -89,12 +97,12 @@ -- > server = listAllBooks :<|> postBook -- >   where listAllBooks = ... -- >         postBook book = ...-instance (HasServer a, HasServer b) => HasServer (a :<|> b) where+instance (HasServer a ctx m, HasServer b ctx m) => HasServer (a :<|> b) ctx m where -  type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m+  type ServerT (a :<|> b) ctx m = ServerT a ctx m :<|> ServerT b ctx m -  route Proxy server = choice (route pa ((\ (a :<|> _) -> a) <$> server))-                              (route pb ((\ (_ :<|> b) -> b) <$> server))+  route Proxy ctx server = choice (route pa ctx ((\ (a :<|> _) -> a) <$> server))+                                  (route pb ctx ((\ (_ :<|> b) -> b) <$> server))     where pa = Proxy :: Proxy a           pb = Proxy :: Proxy b @@ -118,30 +126,30 @@ -- > server = getBook -- >   where getBook :: Text -> EitherT ServantErr IO Book -- >         getBook isbn = ...-instance (FromHttpApiData a, HasServer sublayout)-      => HasServer (Capture capture a :> sublayout) where+instance (FromHttpApiData a, HasServer sublayout context m)+      => HasServer (Capture capture a :> sublayout) context m where -  type ServerT (Capture capture a :> sublayout) m =-     a -> ServerT sublayout m+  type ServerT (Capture capture a :> sublayout) context m =+     a -> ServerT sublayout context m -  route Proxy d =+  route Proxy ctx d =     CaptureRouter $-      route (Proxy :: Proxy sublayout)+      route (Proxy :: Proxy sublayout) ctx         (addCapture d $ \ txt -> case parseUrlPieceMaybe txt of                                    Nothing -> delayedFail err400                                    Just v  -> return v         )  -instance (FromHttpApiData a, HasServer sublayout)-      => HasServer (CaptureAll capture a :> sublayout) where+instance (FromHttpApiData a, HasServer sublayout context m)+      => HasServer (CaptureAll capture a :> sublayout) context m where -  type ServerT (CaptureAll capture a :> sublayout) m =-    [a] -> ServerT sublayout m+  type ServerT (CaptureAll capture a :> sublayout) context m =+    [a] -> ServerT sublayout context m -  route Proxy d =+  route Proxy ctx d =     CaptureAllRouter $-        route (Proxy :: Proxy sublayout)+        route (Proxy :: Proxy sublayout) ctx               (addCapture d $ \ txts -> case parseUrlPieces txts of                  Left _  -> delayedFail err400                  Right v -> return v@@ -219,9 +227,9 @@ instance {-# OVERLAPPABLE #-} (AllCTRender ctypes a,                                ReflectMethod method,                                KnownNat status)-  => HasServer (Verb method status ctypes a) where-  type ServerT (Verb method status ctypes  a) m = m a-  route Proxy = methodRouter method (Proxy :: Proxy ctypes) status+  => HasServer (Verb method status ctypes a) context m where+  type ServerT (Verb method status ctypes  a) context m = m a+  route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status     where method = reflectMethod (Proxy :: Proxy method)           status = toEnum . fromInteger $ natVal (Proxy :: Proxy status) @@ -229,10 +237,10 @@                                ReflectMethod method,                                KnownNat status,                                GetHeaders (Headers h a))-  => HasServer (Verb method status ctypes (Headers h a)) where+  => HasServer (Verb method status ctypes (Headers h a)) context m where -  type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)-  route Proxy = methodRouterHeaders method (Proxy :: Proxy ctypes) status+  type ServerT (Verb method status ctypes (Headers h a)) context m = m (Headers h a)+  route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status     where method = reflectMethod (Proxy :: Proxy method)           status = toEnum . fromInteger $ natVal (Proxy :: Proxy status) @@ -258,15 +266,15 @@ -- > server = viewReferer -- >   where viewReferer :: Referer -> EitherT ServantErr IO referer -- >         viewReferer referer = return referer-instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)-      => HasServer (Header sym a :> sublayout) where+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout context m)+      => HasServer (Header sym a :> sublayout) context m where -  type ServerT (Header sym a :> sublayout) m =-    Maybe a -> ServerT sublayout m+  type ServerT (Header sym a :> sublayout) context m =+    Maybe a -> ServerT sublayout context m -  route Proxy subserver =+  route Proxy ctx subserver =     let mheader req = parseHeaderMaybe =<< getHeader str req-    in  route (Proxy :: Proxy sublayout) (passToServer subserver mheader)+    in  route (Proxy :: Proxy sublayout) ctx (passToServer subserver mheader)     where str = fromString $ symbolVal (Proxy :: Proxy sym)  @@ -291,13 +299,13 @@ -- >   where getBooksBy :: Maybe Text -> EitherT ServantErr IO [Book] -- >         getBooksBy Nothing       = ...return all books... -- >         getBooksBy (Just author) = ...return books by the given author...-instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)-      => HasServer (QueryParam sym a :> sublayout) where+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout context m)+      => HasServer (QueryParam sym a :> sublayout) context m where -  type ServerT (QueryParam sym a :> sublayout) m =-    Maybe a -> ServerT sublayout m+  type ServerT (QueryParam sym a :> sublayout) context m =+    Maybe a -> ServerT sublayout context m -  route Proxy subserver =+  route Proxy ctx subserver =     let querytext r = parseQueryText $ rqQueryString r         param r =           case lookup paramname (querytext r) of@@ -305,7 +313,7 @@             Just Nothing  -> Nothing -- param present with no value -> Nothing             Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to                                         -- the right type-    in route (Proxy :: Proxy sublayout) (passToServer subserver param)+    in route (Proxy :: Proxy sublayout) ctx (passToServer subserver param)     where paramname = cs $ symbolVal (Proxy :: Proxy sym)  @@ -328,20 +336,20 @@ -- > server = getBooksBy -- >   where getBooksBy :: [Text] -> EitherT ServantErr IO [Book] -- >         getBooksBy authors = ...return all books by these authors...-instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)-      => HasServer (QueryParams sym a :> sublayout) where+instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout context m)+      => HasServer (QueryParams sym a :> sublayout) context m where -  type ServerT (QueryParams sym a :> sublayout) m =-    [a] -> ServerT sublayout m+  type ServerT (QueryParams sym a :> sublayout) context m =+    [a] -> ServerT sublayout context m -  route Proxy subserver =+  route Proxy ctx subserver =     let querytext r = parseQueryText $ rqQueryString r         -- if sym is "foo", we look for query string parameters         -- named "foo" or "foo[]" and call parseQueryParam on the         -- corresponding values         parameters r = filter looksLikeParam (querytext r)         values r = mapMaybe (convert . snd) (parameters r)-    in  route (Proxy :: Proxy sublayout) (passToServer subserver values)+    in  route (Proxy :: Proxy sublayout) ctx (passToServer subserver values)     where paramname = cs $ symbolVal (Proxy :: Proxy sym)           looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")           convert Nothing = Nothing@@ -360,19 +368,19 @@ -- > server = getBooks -- >   where getBooks :: Bool -> EitherT ServantErr IO [Book] -- >         getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument...-instance (KnownSymbol sym, HasServer sublayout)-      => HasServer (QueryFlag sym :> sublayout) where+instance (KnownSymbol sym, HasServer sublayout context m)+      => HasServer (QueryFlag sym :> sublayout) context m where -  type ServerT (QueryFlag sym :> sublayout) m =-    Bool -> ServerT sublayout m+  type ServerT (QueryFlag sym :> sublayout) context m =+    Bool -> ServerT sublayout context m -  route Proxy subserver =+  route Proxy ctx subserver =     let querytext r = parseQueryText $ rqQueryString r         param r = case lookup paramname (querytext r) of           Just Nothing  -> True  -- param is there, with no value           Just (Just v) -> examine v -- param with a value           Nothing       -> False -- param not in the query string-    in  route (Proxy :: Proxy sublayout) (passToServer subserver param)+    in  route (Proxy :: Proxy sublayout) ctx (passToServer subserver param)     where paramname = cs $ symbolVal (Proxy :: Proxy sym)           examine v | v == "true" || v == "1" || v == "" = True                     | otherwise = False@@ -386,11 +394,11 @@ -- > -- > server :: Server MyApi -- > server = serveDirectory "/var/www/images"-instance HasServer Raw where+instance HasServer Raw context m where -  type ServerT Raw m = m ()+  type ServerT Raw context m = m () -  route Proxy rawApplication = RawRouter $ \ env request respond -> do+  route Proxy _ rawApplication = RawRouter $ \ env request respond -> do     r <- runDelayed rawApplication env request     case r of       Route app   -> (snapToApplication' app) request (respond . Route)@@ -418,14 +426,14 @@ -- > server = postBook -- >   where postBook :: Book -> EitherT ServantErr IO Book -- >         postBook book = ...insert into your db...-instance ( AllCTUnrender list a, HasServer sublayout-         ) => HasServer (ReqBody list a :> sublayout) where+instance ( AllCTUnrender list a, HasServer sublayout context m+         ) => HasServer (ReqBody list a :> sublayout) context m where -  type ServerT (ReqBody list a :> sublayout) m =-    a -> ServerT sublayout m+  type ServerT (ReqBody list a :> sublayout) context m =+    a -> ServerT sublayout context m -  route Proxy subserver =-    route (Proxy :: Proxy sublayout) (addBodyCheck (subserver ) bodyCheck')+  route Proxy ctx subserver =+    route (Proxy :: Proxy sublayout) ctx (addBodyCheck (subserver ) bodyCheck')     where       -- bodyCheck' :: DelayedM m a       bodyCheck' = do@@ -441,35 +449,55 @@  -- | Make sure the incoming request starts with @"/path"@, strip it and -- pass the rest of the request path to @sublayout@.-instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where+instance (KnownSymbol path, HasServer sublayout context m) => HasServer (path :> sublayout) context m where -  type ServerT (path :> sublayout) m = ServerT sublayout m+  type ServerT (path :> sublayout) context m = ServerT sublayout context m -  route Proxy subserver =+  route Proxy ctx subserver =     pathRouter       (cs (symbolVal proxyPath))-      (route (Proxy :: Proxy sublayout) subserver)+      (route (Proxy :: Proxy sublayout) ctx subserver)     where proxyPath = Proxy :: Proxy path  -instance HasServer api => HasServer (HttpVersion :> api) where-  type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m+instance HasServer api context m => HasServer (HttpVersion :> api) context m where+  type ServerT (HttpVersion :> api) context m = HttpVersion -> ServerT api context m -  route Proxy subserver =-    route (Proxy :: Proxy api) (passToServer subserver rqVersion)+  route Proxy ctx subserver =+    route (Proxy :: Proxy api) ctx (passToServer subserver rqVersion)  -instance HasServer api => HasServer (IsSecure :> api) where-  type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m+instance HasServer api context m => HasServer (IsSecure :> api) context m where+  type ServerT (IsSecure :> api) context m = IsSecure -> ServerT api context m -  route Proxy subserver =-    route (Proxy :: Proxy api) (passToServer subserver (bool NotSecure Secure . rqIsSecure))+  route Proxy ctx subserver =+    route (Proxy :: Proxy api) ctx (passToServer subserver (bool NotSecure Secure . rqIsSecure)) -instance HasServer api => HasServer (RemoteHost :> api) where-  type ServerT (RemoteHost :> api) m = B.ByteString -> ServerT api m+instance HasServer api context m => HasServer (RemoteHost :> api) context m where+  type ServerT (RemoteHost :> api) context m = B.ByteString -> ServerT api context m -  route Proxy subserver =-    route (Proxy :: Proxy api) (passToServer subserver rqHostName)+  route Proxy ctx subserver =+    route (Proxy :: Proxy api) ctx (passToServer subserver rqHostName)++-- newtype BasicAuthCheck m usr =+--   BasicAuthCheck { unBasicAuthCheck :: BasicAuthData -> m (BasicAuthResult usr) }+--   deriving (Functor, Generic)++data BasicAuthResult usr = Unauthorized | BadPassword | NoSuchUser | Authorized usr+  deriving (Functor, Eq, Read, Show, Generic)++instance (HasServer api context m,+          KnownSymbol realm,+          HasContextEntry context (BasicAuthCheck m usr)+         ) => HasServer (BasicAuth realm usr :> api) context m where+  type ServerT (BasicAuth realm usr :> api) context m = usr -> ServerT api context m++  route Proxy ctx subserver =+    route (Proxy :: Proxy api) ctx (subserver `addAuthCheck` authCheck)+    where+      realm = B.pack $ symbolVal (Proxy :: Proxy realm)+      basicAuthContext = getContextEntry ctx+      authCheck = withRequest $ \req -> runBasicAuth req realm basicAuthContext  ct_wildcard :: B.ByteString ct_wildcard = "*" <> "/" <> "*" -- Because CPP
+ src/Servant/Server/Internal/BasicAuth.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Servant.Server.Internal.BasicAuth where++import           Control.Monad          (guard)+import           Control.Monad.Trans    (liftIO)+import qualified Data.ByteString        as BS+import           Data.ByteString.Base64 (decodeLenient)+import           Data.CaseInsensitive   (CI(..)) +import           Data.Monoid            ((<>))+import           Data.Typeable          (Typeable)+import           Data.Word8             (isSpace, toLower, _colon)+import           GHC.Generics+import           Snap.Core+-- import           Network.HTTP.Types     (Header)+-- import           Network.Wai            (Request, requestHeaders)++import           Servant.API.BasicAuth (BasicAuthData(BasicAuthData))+import           Servant.Server.Internal.RoutingApplication+import           Servant.Server.Internal.ServantErr++-- * Basic Auth++-- | servant-server's current implementation of basic authentication is not+-- immune to certian kinds of timing attacks. Decoding payloads does not take+-- a fixed amount of time.++-- | The result of authentication/authorization+data BasicAuthResult usr+  = Unauthorized+  | BadPassword+  | NoSuchUser+  | Authorized usr+  deriving (Eq, Show, Read, Generic, Typeable, Functor)++-- | Datatype wrapping a function used to check authentication.+newtype BasicAuthCheck m usr = BasicAuthCheck+  { unBasicAuthCheck :: BasicAuthData+                     -> m (BasicAuthResult usr)+  }+  deriving (Generic, Typeable, Functor)++-- | Internal method to make a basic-auth challenge+mkBAChallengerHdr :: BS.ByteString -> (CI BS.ByteString, BS.ByteString)+mkBAChallengerHdr realm = ("WWW-Authenticate", "Basic realm=\"" <> realm <> "\"")++-- | Find and decode an 'Authorization' header from the request as Basic Auth+decodeBAHdr :: Request -> Maybe BasicAuthData+decodeBAHdr req = do+    ah <- getHeader "Authorization" req+    let (b, rest) = BS.break isSpace ah+    guard (BS.map toLower b == "basic")+    let decoded = decodeLenient (BS.dropWhile isSpace rest)+    let (username, passWithColonAtHead) = BS.break (== _colon) decoded+    (_, password) <- BS.uncons passWithColonAtHead+    return (BasicAuthData username password)++-- | Run and check basic authentication, returning the appropriate http error per+-- the spec.+runBasicAuth :: MonadSnap m => Request -> BS.ByteString -> BasicAuthCheck m usr -> DelayedM m usr+runBasicAuth req realm (BasicAuthCheck ba) =+  case decodeBAHdr req of+     Nothing -> plzAuthenticate+     Just e  -> DelayedM (const $ Route <$> ba e) >>= \res -> case res of+       BadPassword    -> plzAuthenticate+       NoSuchUser     -> plzAuthenticate+       Unauthorized   -> delayedFailFatal err403+       Authorized usr -> return usr+  where plzAuthenticate = delayedFailFatal err401 { errHeaders = [mkBAChallengerHdr realm] }
+ src/Servant/Server/Internal/Context.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE KindSignatures             #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeOperators              #-}++module Servant.Server.Internal.Context where++import           Data.Proxy+import           GHC.TypeLits++-- | 'Context's are used to pass values to combinators. (They are __not__ meant+-- to be used to pass parameters to your handlers, i.e. they should not replace+-- any custom 'Control.Monad.Trans.Reader.ReaderT'-monad-stack that you're using+-- with 'Servant.Utils.Enter'.) If you don't use combinators that+-- require any context entries, you can just use 'Servant.Server.serve' as always.+--+-- If you are using combinators that require a non-empty 'Context' you have to+-- use 'Servant.Server.serveWithContext' and pass it a 'Context' that contains all+-- the values your combinators need. A 'Context' is essentially a heterogenous+-- list and accessing the elements is being done by type (see 'getContextEntry').+-- The parameter of the type 'Context' is a type-level list reflecting the types+-- of the contained context entries. To create a 'Context' with entries, use the+-- operator @(':.')@:+--+-- >>> :type True :. () :. EmptyContext+-- True :. () :. EmptyContext :: Context '[Bool, ()]+data Context contextTypes where+    EmptyContext :: Context '[]+    (:.) :: x -> Context xs -> Context (x ': xs)+infixr 5 :.++instance Show (Context '[]) where+  show EmptyContext = "EmptyContext"+instance (Show a, Show (Context as)) => Show (Context (a ': as)) where+  showsPrec outerPrecedence (a :. as) =+    showParen (outerPrecedence > 5) $+      shows a . showString " :. " . shows as++instance Eq (Context '[]) where+    _ == _ = True+instance (Eq a, Eq (Context as)) => Eq (Context (a ': as)) where+    x1 :. y1 == x2 :. y2 = x1 == x2 && y1 == y2++-- | This class is used to access context entries in 'Context's. 'getContextEntry'+-- returns the first value where the type matches:+--+-- >>> getContextEntry (True :. False :. EmptyContext) :: Bool+-- True+--+-- If the 'Context' does not contain an entry of the requested type, you'll get+-- an error:+--+-- >>> getContextEntry (True :. False :. EmptyContext) :: String+-- ...+-- ...No instance for (HasContextEntry '[] [Char])+-- ...+class HasContextEntry (context :: [*]) (val :: *) where+    getContextEntry :: Context context -> val++-- TODO OVERLAPPABLE_+instance {-# OVERLAPPABLE #-}+         HasContextEntry xs val => HasContextEntry (notIt ': xs) val where+    getContextEntry (_ :. xs) = getContextEntry xs++instance {-# OVERLAPPABLE #-}+         HasContextEntry (val ': xs) val where+    getContextEntry (x :. _) = x++-- * support for named subcontexts++-- | Normally context entries are accessed by their types. In case you need+-- to have multiple values of the same type in your 'Context' and need to access+-- them, we provide 'NamedContext'. You can think of it as sub-namespaces for+-- 'Context's.+data NamedContext (name :: Symbol) (subContext :: [*])+  = NamedContext (Context subContext)++-- | 'descendIntoNamedContext' allows you to access `NamedContext's. Usually you+-- won't have to use it yourself but instead use a combinator like+-- 'Servant.API.WithNamedContext.WithNamedContext'.+--+-- This is how 'descendIntoNamedContext' works:+--+-- >>> :set -XFlexibleContexts+-- >>> let subContext = True :. EmptyContext+-- >>> :type subContext+-- subContext :: Context '[Bool]+-- >>> let parentContext = False :. (NamedContext subContext :: NamedContext "subContext" '[Bool]) :. EmptyContext+-- >>> :type parentContext+-- parentContext :: Context '[Bool, NamedContext "subContext" '[Bool]]+-- >>> descendIntoNamedContext (Proxy :: Proxy "subContext") parentContext :: Context '[Bool]+-- True :. EmptyContext+descendIntoNamedContext :: forall context name subContext .+  HasContextEntry context (NamedContext name subContext) =>+  Proxy (name :: Symbol) -> Context context -> Context subContext+descendIntoNamedContext Proxy context =+  let NamedContext subContext = getContextEntry context :: NamedContext name subContext+  in subContext
src/Servant/Utils/StaticFiles.hs view
@@ -34,5 +34,5 @@ -- behind a /\/static\// prefix. In that case, remember to put the 'serveDirectory' -- handler in the last position, because /servant/ will try to match the handlers -- in order.-serveDirectory :: MonadSnap m => FilePath -> Server Raw m+serveDirectory :: MonadSnap m => FilePath -> Server Raw context m serveDirectory fp = liftSnap $ Snap.serveDirectory fp