-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A family of combinators for defining webservices APIs and serving them -- -- Interpret a Servant API as a Snap server, using any Snaplets you like. -- -- You can learn about the basics of servant in the Servant -- tutorial, and about the basics of Snap at the Snaplets -- tutorial -- -- Here is a runnable example, with comments, that defines a dummy -- API and implements a webserver that serves this API, using this -- package. One route delegates to the Auth snaplet, another -- delegates to Heist. -- -- CHANGELOG @package servant-snap @version 0.7 module Servant.Server.Internal.SnapShims type Application m = Request -> (Response -> m Response) -> m Response snapToApplication :: MonadSnap m => m () -> Application m snapToApplication' :: MonadSnap m => m a -> Application m applicationToSnap :: MonadSnap m => Application m -> m () unSnapMethod :: Method -> ByteString ok200 :: Status noContent204 :: Status created201 :: Status badRequest400 :: Status notFound404 :: Status methodNotAllowed405 :: Status unsupportedMediaType415 :: Status initThunk :: IO a -> IO (IO a) module Servant.Server.Internal.ServantErr data ServantErr ServantErr :: Int -> String -> ByteString -> [Header] -> ServantErr [errHTTPCode] :: ServantErr -> Int [errReasonPhrase] :: ServantErr -> String [errBody] :: ServantErr -> ByteString [errHeaders] :: ServantErr -> [Header] setHeaders :: [Header] -> Response -> Response responseServantErr :: ServantErr -> Response -- | Terminate request handling with a ServantErr via -- finishWith throwError :: MonadSnap m => ServantErr -> m a err300 :: ServantErr err301 :: ServantErr err302 :: ServantErr err303 :: ServantErr err304 :: ServantErr err305 :: ServantErr err307 :: ServantErr err400 :: ServantErr err401 :: ServantErr err402 :: ServantErr err403 :: ServantErr err404 :: ServantErr err405 :: ServantErr err406 :: ServantErr err407 :: ServantErr err409 :: ServantErr err410 :: ServantErr err411 :: ServantErr err412 :: ServantErr err413 :: ServantErr err414 :: ServantErr err415 :: ServantErr err416 :: ServantErr err417 :: ServantErr err500 :: ServantErr err501 :: ServantErr err502 :: ServantErr err503 :: ServantErr err504 :: ServantErr err505 :: ServantErr instance GHC.Read.Read Servant.Server.Internal.ServantErr.ServantErr instance GHC.Classes.Eq Servant.Server.Internal.ServantErr.ServantErr instance GHC.Show.Show Servant.Server.Internal.ServantErr.ServantErr module Servant.Server.Internal.RoutingApplication type RoutingApplication m = Request the request, the field 'pathInfo' may be modified by url routing -> (RouteResult Response -> m Response) -> m Response -- | The result of matching against a path in the route tree. data RouteResult a -- | Keep trying other paths. The ServantErr should only be 404, -- 405 or 406. Fail :: ServantErr -> RouteResult a -- | Don't try other paths. FailFatal :: !ServantErr -> RouteResult a Route :: !a -> RouteResult a toApplication :: forall m. MonadSnap m => RoutingApplication m -> Application m responseLBS :: Status -> [(CI ByteString, ByteString)] -> ByteString -> Response runAction :: MonadSnap m => Delayed m env (m a) -> env -> Request -> (RouteResult Response -> m r) -> (a -> RouteResult Response) -> m r data Delayed m env c [Delayed] :: {capturesD :: env -> DelayedM m captures, methodD :: DelayedM m (), authD :: DelayedM m auth, bodyD :: DelayedM m body, serverD :: captures -> auth -> body -> Request -> RouteResult c} -> Delayed m env c newtype DelayedM m a DelayedM :: (Request -> m (RouteResult a)) -> DelayedM m a [runDelayedM] :: DelayedM m a -> Request -> m (RouteResult a) -- | A Delayed without any stored checks. emptyDelayed :: Monad m => Proxy (m :: * -> *) -> RouteResult a -> Delayed m env a -- | Fail with the option to recover. delayedFail :: Monad m => ServantErr -> DelayedM m a -- | Fail fatally, i.e., without any option to recover. delayedFailFatal :: Monad m => ServantErr -> DelayedM m a -- | Gain access to the incoming request. withRequest :: (Request -> DelayedM m a) -> DelayedM m a -- | Add a capture to the end of the capture block. addCapture :: forall env a b captured m. Monad m => Delayed m env (a -> b) -> (captured -> DelayedM m a) -> Delayed m (captured, env) b -- | Add a method check to the end of the method block. addMethodCheck :: Monad m => Delayed m env a -> DelayedM m () -> Delayed m env a -- | Add an auth check to the end of the auth block. addAuthCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m a -> Delayed m env b -- | Add a body check to the end of the body block. addBodyCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m a -> Delayed m env b -- | Add an accept header check to the beginning of the body block. There -- is a tradeoff here. In principle, we'd like to take a bad body (400) -- response take precedence over a failed accept check (406). BUT to -- allow streaming the body, we cannot run the body check and then still -- backtrack. We therefore do the accept check before the body check, -- when we can still backtrack. There are other solutions to this, but -- they'd be more complicated (such as delaying the body check further so -- that it can still be run in a situation where we'd otherwise report -- 406). addAcceptCheck :: Monad m => Delayed m env a -> DelayedM m () -> Delayed m env a -- | Many combinators extract information that is passed to the handler -- without the possibility of failure. In such a case, -- passToServer can be used. passToServer :: Delayed m env (a -> b) -> (Request -> a) -> Delayed m env b -- | Run a delayed server. Performs all scheduled operations in order, and -- passes the results from the capture and body blocks on to the actual -- handler. -- -- This should only be called once per request; otherwise the guarantees -- about effect and HTTP error ordering break down. runDelayed :: Monad m => Delayed m env a -> env -> Request -> m (RouteResult a) instance GHC.Base.Functor Servant.Server.Internal.RoutingApplication.RouteResult instance GHC.Read.Read a => GHC.Read.Read (Servant.Server.Internal.RoutingApplication.RouteResult a) instance GHC.Show.Show a => GHC.Show.Show (Servant.Server.Internal.RoutingApplication.RouteResult a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Servant.Server.Internal.RoutingApplication.RouteResult a) instance GHC.Base.Functor (Servant.Server.Internal.RoutingApplication.Delayed m env) instance GHC.Base.Monad m => GHC.Base.Functor (Servant.Server.Internal.RoutingApplication.DelayedM m) instance GHC.Base.Monad m => GHC.Base.Applicative (Servant.Server.Internal.RoutingApplication.DelayedM m) instance GHC.Base.Monad m => GHC.Base.Monad (Servant.Server.Internal.RoutingApplication.DelayedM m) instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Servant.Server.Internal.RoutingApplication.DelayedM m) instance GHC.Base.Monad m => GHC.Base.Alternative (Servant.Server.Internal.RoutingApplication.DelayedM m) instance Control.Monad.Trans.Class.MonadTrans Servant.Server.Internal.RoutingApplication.DelayedM module Servant.Server.Internal.PathInfo rqPath :: Request -> ByteString pathInfo :: Request -> [Text] pathSafeTail :: Request -> ([ByteString], [ByteString]) reqSafeTail :: Request -> Request reqNoPath :: Request -> Request -- | Like `null . pathInfo`, but works with redundant trailing slashes. pathIsEmpty :: Request -> Bool splitMatrixParameters :: Text -> (Text, Text) parsePathInfo :: Request -> [Text] -- | Returns a processed pathInfo from the request. -- -- In order to handle matrix parameters in the request correctly, the raw -- pathInfo needs to be processed, so routing works as intended. Therefor -- this function should be used to access the pathInfo for routing -- purposes. processedPathInfo :: Request -> [Text] module Servant.Server.Internal.Router type Router (m :: * -> *) env = Router' m env (RoutingApplication m) -- | Internal representation of a router. data Router' (m :: * -> *) env a StaticRouter :: (Map Text (Router' m env a)) -> [env -> a] -> Router' env a CaptureRouter :: (Router' m (Text, env) a) -> Router' env a CaptureAllRouter :: (Router' m ([Text], env) a) -> Router' env a RawRouter :: (env -> a) -> Router' env a Choice :: (Router' m env a) -> (Router' m env a) -> Router' env a pathRouter :: Text -> Router' m env a -> Router' m env a leafRouter :: (env -> a) -> Router' m env a -- | Smart constructor for the choice between routers. We currently -- optimize the following cases: -- --
-- type MyApi = "books" :> Get '[JSON] [Book] -- GET /books -- :<|> "books" :> ReqBody Book :> Post '[JSON] Book -- POST /books -- -- server :: Server MyApi -- server = listAllBooks :<|> postBook -- where listAllBooks = ... -- postBook book = ... --captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a -- | If you use Capture in one of the endpoints for your API, this -- automatically requires your server-side handler to be a function that -- takes an argument of the type specified by the Capture. This -- lets servant worry about getting it from the URL and turning it into a -- value of the type you specify. -- -- You can control how it'll be converted from Text to your type -- by simply providing an instance of FromText for your type. -- -- Example: -- --
-- type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book -- -- server :: Server MyApi -- server = getBook -- where getBook :: Text -> EitherT ServantErr IO Book -- getBook isbn = ... --allowedMethodHead :: Method -> Request -> Bool allowedMethod :: Method -> Request -> Bool processMethodRouter :: Maybe (ByteString, ByteString) -> Status -> Method -> Maybe [(HeaderName, ByteString)] -> Request -> RouteResult Response methodCheck :: MonadSnap m => Method -> Request -> DelayedM m () acceptCheck :: (AllMime list, MonadSnap m) => Proxy list -> ByteString -> DelayedM m () methodRouter :: (AllCTRender ctypes a, MonadSnap m) => Method -> Proxy ctypes -> Status -> Delayed m env (m a) -> Router m env methodRouterHeaders :: (GetHeaders (Headers h v), AllCTRender ctypes v, MonadSnap m) => Method -> Proxy ctypes -> Status -> Delayed m env (m (Headers h v)) -> Router m env -- | If you use Header in one of the endpoints for your API, this -- automatically requires your server-side handler to be a function that -- takes an argument of the type specified by Header. This lets -- servant worry about extracting it from the request and turning it into -- a value of the type you specify. -- -- All it asks is for a FromText instance. -- -- Example: -- --
-- newtype Referer = Referer Text -- deriving (Eq, Show, FromText, ToText) -- -- -- GET /view-my-referer -- type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer -- -- server :: Server MyApi -- server = viewReferer -- where viewReferer :: Referer -> EitherT ServantErr IO referer -- viewReferer referer = return referer ---- | If you use QueryParam "author" Text in one of the -- endpoints for your API, this automatically requires your server-side -- handler to be a function that takes an argument of type -- Maybe Text. -- -- This lets servant worry about looking it up in the query string and -- turning it into a value of the type you specify, enclosed in -- Maybe, because it may not be there and servant would then hand -- you Nothing. -- -- You can control how it'll be converted from Text to your type -- by simply providing an instance of FromText for your type. -- -- Example: -- --
-- type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book] -- -- server :: Server MyApi -- server = getBooksBy -- where getBooksBy :: Maybe Text -> EitherT ServantErr IO [Book] -- getBooksBy Nothing = ...return all books... -- getBooksBy (Just author) = ...return books by the given author... ---- | If you use QueryParams "authors" Text in one of the -- endpoints for your API, this automatically requires your server-side -- handler to be a function that takes an argument of type -- [Text]. -- -- This lets servant worry about looking up 0 or more values in the query -- string associated to authors and turning each of them into a -- value of the type you specify. -- -- You can control how the individual values are converted from -- Text to your type by simply providing an instance of -- FromText for your type. -- -- Example: -- --
-- type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book] -- -- server :: Server MyApi -- server = getBooksBy -- where getBooksBy :: [Text] -> EitherT ServantErr IO [Book] -- getBooksBy authors = ...return all books by these authors... ---- | If you use QueryFlag "published" in one of the -- endpoints for your API, this automatically requires your server-side -- handler to be a function that takes an argument of type Bool. -- -- Example: -- --
-- type MyApi = "books" :> QueryFlag "published" :> Get '[JSON] [Book] -- -- server :: Server MyApi -- 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... ---- | Just pass the request to the underlying application and serve its -- response. -- -- Example: -- --
-- type MyApi = "images" :> Raw -- -- server :: Server MyApi -- server = serveDirectory "/var/www/images" ---- | If you use ReqBody in one of the endpoints for your API, this -- automatically requires your server-side handler to be a function that -- takes an argument of the type specified by ReqBody. The -- Content-Type header is inspected, and the list provided is -- used to attempt deserialization. If the request does not have a -- Content-Type header, it is treated as -- application/octet-stream. This lets servant worry about -- extracting it from the request and turning it into a value of the type -- you specify. -- -- All it asks is for a FromJSON instance. -- -- Example: -- --
-- type MyApi = "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book -- -- server :: Server MyApi -- server = postBook -- where postBook :: Book -> EitherT ServantErr IO Book -- postBook book = ...insert into your db... ---- | Make sure the incoming request starts with "/path", strip it -- and pass the rest of the request path to sublayout. ct_wildcard :: ByteString instance (Servant.Server.Internal.HasServer a, Servant.Server.Internal.HasServer b) => Servant.Server.Internal.HasServer (a Servant.API.Alternative.:<|> b) instance forall k a (sublayout :: k) (capture :: GHC.Types.Symbol). (Web.HttpApiData.Internal.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Capture.Capture capture a Servant.API.Sub.:> sublayout) instance forall k a (sublayout :: k) (capture :: GHC.Types.Symbol). (Web.HttpApiData.Internal.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Capture.CaptureAll capture a Servant.API.Sub.:> sublayout) 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) 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)) instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.Header.Header sym a Servant.API.Sub.:> sublayout) instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParam sym a Servant.API.Sub.:> sublayout) instance forall k (sym :: GHC.Types.Symbol) a (sublayout :: k). (GHC.TypeLits.KnownSymbol sym, Web.HttpApiData.Internal.FromHttpApiData a, Servant.Server.Internal.HasServer sublayout) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> sublayout) 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) instance Servant.Server.Internal.HasServer Servant.API.Raw.Raw 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) 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) instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Snap.Internal.Http.Types.HttpVersion Servant.API.Sub.:> api) instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Servant.API.IsSecure.IsSecure Servant.API.Sub.:> api) instance forall k (api :: k). Servant.Server.Internal.HasServer api => Servant.Server.Internal.HasServer (Servant.API.RemoteHost.RemoteHost Servant.API.Sub.:> api) -- | This module lets you implement Servers for defined APIs. You'll -- most likely just need serve. module Servant.Server serveSnap :: forall layout m. (HasServer layout, MonadSnap m) => Proxy layout -> Server layout m -> m () class HasServer api where type ServerT api (m :: * -> *) :: * where { type family ServerT api (m :: * -> *) :: *; } route :: (HasServer api, MonadSnap m) => Proxy api -> Delayed m env (Server api m) -> Router m env type Server api m = ServerT api m data ServantErr ServantErr :: Int -> String -> ByteString -> [Header] -> ServantErr [errHTTPCode] :: ServantErr -> Int [errReasonPhrase] :: ServantErr -> String [errBody] :: ServantErr -> ByteString [errHeaders] :: ServantErr -> [Header] -- | Terminate request handling with a ServantErr via -- finishWith throwError :: MonadSnap m => ServantErr -> m a err300 :: ServantErr err301 :: ServantErr err302 :: ServantErr err303 :: ServantErr err304 :: ServantErr err305 :: ServantErr err307 :: ServantErr err400 :: ServantErr err401 :: ServantErr err402 :: ServantErr err403 :: ServantErr err404 :: ServantErr err405 :: ServantErr err406 :: ServantErr err407 :: ServantErr err409 :: ServantErr err410 :: ServantErr err411 :: ServantErr err412 :: ServantErr err413 :: ServantErr err414 :: ServantErr err415 :: ServantErr err416 :: ServantErr err417 :: ServantErr err500 :: ServantErr err501 :: ServantErr err502 :: ServantErr err503 :: ServantErr err504 :: ServantErr err505 :: ServantErr -- | This module defines a sever-side handler that lets you serve static -- files. -- --
-- type MyApi = "static" :> Raw -- -- server :: Server MyApi -- server = serveDirectory "/var/www" ---- -- would capture any request to /static/<something> and -- look for <something> under /var/www. -- -- It will do its best to guess the MIME type for that file, based on the -- extension, and send an appropriate Content-Type header if -- possible. -- -- If your goal is to serve HTML, CSS and Javascript files that use the -- rest of the API as a webapp backend, you will most likely not want the -- static files to be hidden 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 module Servant -- | A concrete, poly-kinded proxy type data Proxy k (t :: k) :: forall k. k -> * Proxy :: Proxy k