servant-snap 0.8.1 → 0.8.2
raw patch · 4 files changed
+118/−19 lines, 4 filesdep +http-clientdep +http-mediadep +servant-clientdep ~eitherdep ~http-typesdep ~io-streamsPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: http-client, http-media, servant-client
Dependency ranges changed: either, http-types, io-streams, servant, snap, snap-core, snap-server
API changes (from Hackage documentation)
- Servant.Server.Internal: instance forall k1 (api :: k1) (context :: [*]) (m :: * -> *) (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 k1 (api :: k1) (context :: [*]) (m :: * -> *). 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 k1 (api :: k1) (context :: [*]) (m :: * -> *). 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 k1 (api :: k1) (context :: [*]) (m :: * -> *). 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 k1 (list :: [*]) a (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 (path :: GHC.Types.Symbol) (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 (sym :: GHC.Types.Symbol) (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 (sym :: GHC.Types.Symbol) a (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 (sym :: GHC.Types.Symbol) a (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 (sym :: GHC.Types.Symbol) a (sublayout :: k1) (context :: [*]) (m :: * -> *). (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 k1 a (sublayout :: k1) (context :: [*]) (m :: * -> *) (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 k1 a (sublayout :: k1) (context :: [*]) (m :: * -> *) (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 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 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 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 k1 ctype a (method :: k1) (f :: * -> *) (m :: * -> *) framing (h :: [*]) (context :: [*]). (Servant.API.ContentTypes.MimeRender ctype a, Servant.API.Verbs.ReflectMethod method, Servant.API.ContentTypes.MimeRender ctype (f a), Servant.API.Stream.ToStreamGenerator m (f a), Servant.API.Stream.FramingRender framing ctype, Servant.API.Stream.ToStreamGenerator f a, Servant.API.ResponseHeaders.GetHeaders (Servant.API.ResponseHeaders.Headers h (f a))) => Servant.Server.Internal.HasServer (Servant.API.Stream.Stream method framing ctype (Servant.API.ResponseHeaders.Headers h (f a))) context m
+ Servant.Server.Internal: instance forall k1 ctype a (method :: k1) framing (f :: * -> *) (context :: [*]) (m :: * -> *). (Servant.API.ContentTypes.MimeRender ctype a, Servant.API.Verbs.ReflectMethod method, Servant.API.ContentTypes.MimeRender ctype a, Servant.API.Stream.FramingRender framing ctype, Servant.API.Stream.ToStreamGenerator f a) => Servant.Server.Internal.HasServer (Servant.API.Stream.Stream method framing ctype (f a)) context m
+ Servant.Server.Internal: streamRouter :: (MimeRender ctype a, FramingRender framing ctype, ToStreamGenerator f a, MonadSnap m) => (b -> ([(HeaderName, ByteString)], f a)) -> Method -> Proxy m -> Proxy framing -> Proxy ctype -> Delayed m env (m b) -> Router m env
Files
- CHANGELOG.md +6/−0
- example/greet.hs +12/−4
- servant-snap.cabal +12/−8
- src/Servant/Server/Internal.hs +88/−7
CHANGELOG.md view
@@ -1,3 +1,9 @@+0.8.2+------++Add `HasServer` instances for `StreamGenerator`++ 0.8.0.1 -------
example/greet.hs view
@@ -14,7 +14,7 @@ import Data.Text import GHC.Generics import qualified Heist.Interpreted as I-import Snap.Core+import Snap.Core hiding (GET) import Snap.CORS import Snap.Snaplet import Snap.Snaplet.Auth@@ -37,7 +37,7 @@ instance ToJSON Greet -- API specification-type TestApi m =+type TestApi m f = -- GET /hello/:name?capital={true, false} returns a Greet as JSON "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON] Greet@@ -52,6 +52,8 @@ -- DELETE /greet/:greetid :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] () + :<|> "stream" :> StreamGet NetstringFraming JSON (f Greet)+ :<|> "files" :> Raw :<|> "doraw" :> Raw @@ -69,7 +71,7 @@ type AppHandler = Handler App App -testApi :: Proxy (TestApi AppHandler)+testApi :: Proxy (TestApi AppHandler StreamGenerator) testApi = Proxy @@ -80,11 +82,12 @@ -- -- Each handler runs in the 'AppHandler' monad. -server :: Server (TestApi AppHandler) '[] AppHandler+server :: Server (TestApi AppHandler StreamGenerator) '[] AppHandler server = helloH :<|> helloH' :<|> postGreetH :<|> deleteGreetH+ :<|> doStream :<|> serveDirectory "static" :<|> doRaw @@ -103,6 +106,11 @@ postGreetH greet = return greet deleteGreetH _ = return ()++ doStream = return $ StreamGenerator $ \sendFirst sendRest -> do+ sendFirst (Greet "hi")+ sendRest (Greet "tao")+ sendRest (Greet "Howareya") doRaw = with auth $ do u <- currentUser
servant-snap.cabal view
@@ -1,5 +1,5 @@ name: servant-snap-version: 0.8.1+version: 0.8.2 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.@@ -51,20 +51,21 @@ , bytestring >= 0.10 && < 0.11 , case-insensitive >= 1.2 && < 1.3 , containers >= 0.5 && < 0.6- , either >= 4.3 && < 4.5+ , either >= 4.3 && < 4.6 , filepath >= 1 && < 1.5- , http-types >= 0.8 && < 0.10+ , http-media >= 0.7.1.2 && < 0.8+ , http-types >= 0.8 && < 0.13 , http-api-data >= 0.2 && < 0.4- , io-streams >= 1.3 && < 1.5+ , io-streams >= 1.3 && < 1.6 , network-uri >= 2.6 && < 2.7 , mtl >= 2.0 && < 2.3 , mmorph >= 1 && < 1.2- , servant >= 0.8 && < 0.13+ , servant >= 0.8 && < 0.14 , string-conversions >= 0.3 && < 0.5 , text >= 1.2 && < 1.3- , snap >= 1.0 && < 1.1- , snap-core >= 1.0 && < 1.1- , snap-server >= 1.0 && < 1.1+ , snap >= 1.0 && < 1.2+ , snap-core >= 1.0.2 && < 1.2+ , snap-server >= 1.0 && < 1.2 , transformers >= 0.3 && < 0.6 , word8 >= 0.1 && < 0.2 hs-source-dirs: src@@ -83,8 +84,10 @@ , either , errors , heist+ , http-client , lens , servant+ , servant-client , servant-snap , snap-cors , map-syntax@@ -112,6 +115,7 @@ , either , exceptions , hspec >= 2.4.3 && < 2.5+ , io-streams , lens , process , digestive-functors >= 0.8.1.0 && < 0.9
src/Servant/Server/Internal.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -27,10 +28,14 @@ ------------------------------------------------------------------------------- import Control.Applicative ((<$>)) import Control.Monad.Trans.Class (lift)+import Control.Monad (when) import Data.Bool (bool)+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Builder.Extra as BB import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as BL-import Data.Maybe (fromMaybe, mapMaybe)+import Data.Maybe (fromMaybe, isNothing, mapMaybe,+ maybeToList) import Data.Proxy import Data.String (fromString) import Data.String.Conversions (cs, (<>))@@ -42,6 +47,7 @@ Status(..), parseQueryText, methodGet, methodHead, hContentType, hAccept)+import qualified Network.HTTP.Media as NHM import Web.HttpApiData (FromHttpApiData, parseHeaderMaybe, parseQueryParamMaybe,@@ -51,18 +57,23 @@ getResponse, route, method, withRequest) import Servant.API ((:<|>) (..), (:>), BasicAuth,+ BoundaryStrategy(..), Capture,- CaptureAll, Header,- IsSecure(..), QueryFlag,+ CaptureAll, FramingRender (..),+ Header, IsSecure(..), QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, ReqBody,- ReflectMethod(..), Verb)+ ReflectMethod(..), Stream,+ ToStreamGenerator (..), Verb,+ getStreamGenerator) import Servant.API.ContentTypes (AcceptHeader (..), AllCTRender (..),- AllCTUnrender (..), AllMime(..), canHandleAcceptH)+ AllCTUnrender (..), AllMime(..),+ MimeRender (..),+ canHandleAcceptH, contentType) import Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders, getHeaders)--- import Servant.Common.Text (FromText, fromText)+import qualified System.IO.Streams as IOS import Servant.Server.Internal.BasicAuth import Servant.Server.Internal.Context@@ -72,7 +83,7 @@ import Servant.Server.Internal.ServantErr import Servant.Server.Internal.SnapShims --- TODO: add MonadSnap m => m ?+ class HasServer api context (m :: * -> *) where type ServerT api context m :: * @@ -501,3 +512,73 @@ ct_wildcard :: B.ByteString ct_wildcard = "*" <> "/" <> "*" -- Because CPP++instance ( MimeRender ctype a, ReflectMethod method, MimeRender ctype a, -- ToStreamGenerator m (f a),+ FramingRender framing ctype, ToStreamGenerator f a+ ) => HasServer (Stream method framing ctype (f a)) context m where+ type ServerT (Stream method framing ctype (f a)) context m = m (f a)++ route Proxy _ action = streamRouter ([],) method (Proxy) (Proxy :: Proxy framing) (Proxy :: Proxy ctype) action+ where method = reflectMethod (Proxy :: Proxy method)+ -- status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)++instance ( MimeRender ctype a, ReflectMethod method, MimeRender ctype (f a), ToStreamGenerator m (f a),+ FramingRender framing ctype, ToStreamGenerator f a, GetHeaders (Headers h (f a))+ ) => HasServer (Stream method framing ctype (Headers h (f a))) context m where+ type ServerT (Stream method framing ctype (Headers h (f a))) context m = m (Headers h (f a))++ route Proxy _ action =+ streamRouter (\x -> (getHeaders x, getResponse x))+ method+ Proxy+ (Proxy :: Proxy framing)+ (Proxy :: Proxy ctype)+ action+ where method = reflectMethod (Proxy :: Proxy method)+++streamRouter :: (MimeRender ctype a,+ FramingRender framing ctype,+ ToStreamGenerator f a,+ MonadSnap m+ ) =>+ (b -> ([(HeaderName, B.ByteString)], f a))+ -> Method+ -> Proxy m+ -> Proxy framing+ -> Proxy ctype+ -> Delayed m env (m b)+ -> Router m env+streamRouter splitHeaders method handlerProxy framingproxy ctypeproxy action = leafRouter $ \env request respond ->+ let accH = fromMaybe ct_wildcard $ getHeader hAccept request+ cmediatype = NHM.matchAccept [contentType ctypeproxy] accH+ accCheck = when (isNothing cmediatype) $ delayedFail err406+ contentHeader = (hContentType, NHM.renderHeader . maybeToList $ cmediatype)+ in runAction (action `addMethodCheck` methodCheck method request+ `addAcceptCheck` accCheck+ ) env request respond $ \ output ->+ let (headers, fa) = splitHeaders output+ k = getStreamGenerator . toStreamGenerator $ fa in+ Route $ flip setResponseBody emptyResponse $ \outStream -> do+ let writeAndFlush bb = IOS.write (Just $ bb <> BB.flush) outStream+ writeAndFlush (BB.lazyByteString $ header framingproxy ctypeproxy)+ case boundary framingproxy ctypeproxy of+ BoundaryStrategyBracket f ->+ let go x = let bs = mimeRender ctypeproxy x+ (before, after) = f bs+ in writeAndFlush ( BB.lazyByteString before+ <> BB.lazyByteString bs+ <> BB.lazyByteString after)+ in k go go+ BoundaryStrategyIntersperse sep -> k+ (\x -> do+ writeAndFlush . BB.lazyByteString $ mimeRender ctypeproxy x+ )+ (\x -> do+ writeAndFlush . (BB.lazyByteString sep <>) . BB.lazyByteString $ mimeRender ctypeproxy x+ )+ BoundaryStrategyGeneral f ->+ let go = writeAndFlush . BB.lazyByteString . f . mimeRender ctypeproxy+ in k go go+ IOS.write (Just . BB.lazyByteString $ trailer framingproxy ctypeproxy) outStream+ return outStream