packages feed

servant-snap 0.8.2 → 0.8.3

raw patch · 4 files changed

+418/−192 lines, 4 filesdep +taggeddep ~aesondep ~basedep ~containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: tagged

Dependency ranges changed: aeson, base, containers, either, hspec, hspec-core, hspec-snap, http-api-data, servant

API changes (from Hackage documentation)

- 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 (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 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 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: hoistServerWithContext :: HasServer api context m => proxy api -> proxy' context -> (forall x. m x -> n x) -> ServerT api context m -> ServerT api context n
+ Servant.Server.Internal: EmptyServer :: EmptyServer
+ Servant.Server.Internal: data EmptyServer
+ Servant.Server.Internal: emptyServer :: ServerT EmptyAPI context m
+ Servant.Server.Internal: hoistServerWithContext :: HasServer api context m => proxy api -> proxy' context -> (forall x. m x -> n x) -> ServerT api context m -> ServerT api context n
+ Servant.Server.Internal: instance (GHC.TypeLits.KnownSymbol path, Servant.Server.Internal.HasServer api context m) => Servant.Server.Internal.HasServer (path Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (GHC.TypeLits.KnownSymbol realm, Servant.Server.Internal.HasServer api context m, 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 (GHC.TypeLits.KnownSymbol sym, Servant.Server.Internal.HasServer api context m) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryFlag sym Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer api context m) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer api context m, Data.Singletons.Bool.SBoolI (Servant.API.Modifiers.FoldRequired mods), Data.Singletons.Bool.SBoolI (Servant.API.Modifiers.FoldLenient mods)) => Servant.Server.Internal.HasServer (Servant.API.Header.Header' mods sym a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (GHC.TypeLits.KnownSymbol sym, Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer api context m, Data.Singletons.Bool.SBoolI (Servant.API.Modifiers.FoldRequired mods), Data.Singletons.Bool.SBoolI (Servant.API.Modifiers.FoldLenient mods)) => Servant.Server.Internal.HasServer (Servant.API.QueryParam.QueryParam' mods sym a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (Servant.API.ContentTypes.AllCTUnrender list a, Servant.Server.Internal.HasServer api context m, Data.Singletons.Bool.SBoolI (Servant.API.Modifiers.FoldLenient mods), Snap.Internal.Core.MonadSnap m) => Servant.Server.Internal.HasServer (Servant.API.ReqBody.ReqBody' mods list a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer api context m) => Servant.Server.Internal.HasServer (Servant.API.Capture.Capture' mods capture a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance (Web.Internal.HttpApiData.FromHttpApiData a, Servant.Server.Internal.HasServer api context m) => Servant.Server.Internal.HasServer (Servant.API.Capture.CaptureAll capture a Servant.API.Sub.:> api) context m
+ Servant.Server.Internal: instance GHC.Classes.Eq Servant.Server.Internal.EmptyServer
+ Servant.Server.Internal: instance GHC.Enum.Bounded Servant.Server.Internal.EmptyServer
+ Servant.Server.Internal: instance GHC.Enum.Enum Servant.Server.Internal.EmptyServer
+ Servant.Server.Internal: instance GHC.Show.Show Servant.Server.Internal.EmptyServer
+ Servant.Server.Internal: instance Servant.Server.Internal.HasServer Servant.API.Empty.EmptyAPI context m
+ Servant.Server.Internal: instance Servant.Server.Internal.HasServer api ctx m => Servant.Server.Internal.HasServer (Servant.API.Description.Description desc Servant.API.Sub.:> api) ctx m
+ Servant.Server.Internal: instance Servant.Server.Internal.HasServer api ctx m => Servant.Server.Internal.HasServer (Servant.API.Description.Summary desc Servant.API.Sub.:> api) ctx m
+ Servant.Server.Internal: instance forall k1 ctype a (method :: k1) (status :: GHC.Types.Nat) framing b (context :: [*]) (m :: * -> *). (Servant.API.ContentTypes.MimeRender ctype a, Servant.API.Verbs.ReflectMethod method, GHC.TypeNats.KnownNat status, Servant.API.Stream.FramingRender framing ctype, Servant.API.Stream.ToStreamGenerator b a) => Servant.Server.Internal.HasServer (Servant.API.Stream.Stream method status framing ctype b) context m
+ Servant.Server.Internal: instance forall k1 ctype a (method :: k1) (status :: GHC.Types.Nat) framing b (h :: [*]) (context :: [*]) (m :: * -> *). (Servant.API.ContentTypes.MimeRender ctype a, Servant.API.Verbs.ReflectMethod method, GHC.TypeNats.KnownNat status, Servant.API.Stream.FramingRender framing ctype, Servant.API.Stream.ToStreamGenerator b a, Servant.API.ResponseHeaders.GetHeaders (Servant.API.ResponseHeaders.Headers h b)) => Servant.Server.Internal.HasServer (Servant.API.Stream.Stream method status framing ctype (Servant.API.ResponseHeaders.Headers h b)) context m
+ Servant.Server.Internal.Context: infixr 5 :.
+ Servant.Server.Internal.RoutingApplication: addHeaderCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m a -> Delayed m env b
+ Servant.Server.Internal.RoutingApplication: addParameterCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m a -> Delayed m env b
- Servant: Proxy :: Proxy k
+ Servant: Proxy :: Proxy
- Servant: data Proxy k (t :: k) :: forall k. () => k -> *
+ Servant: data Proxy (t :: k) :: forall k. () => k -> Type
- Servant.Server.Internal: captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a
+ Servant.Server.Internal: captured :: FromHttpApiData a => proxy (Capture' mods sym a) -> Text -> Maybe a
- 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
+ Servant.Server.Internal: streamRouter :: (MimeRender ctype a, FramingRender framing ctype, ToStreamGenerator b a, MonadSnap m) => (c -> ([(HeaderName, ByteString)], b)) -> Method -> Status -> Proxy framing -> Proxy ctype -> Delayed m env (m c) -> Router m env
- Servant.Server.Internal.Context: NamedContext :: (Context subContext) -> NamedContext
+ Servant.Server.Internal.Context: NamedContext :: Context subContext -> NamedContext
- Servant.Server.Internal.Router: CaptureAllRouter :: (Router' m ([Text], env) a) -> Router' env a
+ Servant.Server.Internal.Router: CaptureAllRouter :: Router' m ([Text], env) a -> Router' env a
- Servant.Server.Internal.Router: CaptureRouter :: (Router' m (Text, env) a) -> Router' env a
+ Servant.Server.Internal.Router: CaptureRouter :: Router' m (Text, env) a -> Router' env a
- Servant.Server.Internal.Router: Choice :: (Router' m env a) -> (Router' m env a) -> Router' env a
+ Servant.Server.Internal.Router: Choice :: Router' m env a -> Router' m env a -> Router' env a
- Servant.Server.Internal.Router: StaticRouter :: (Map Text (Router' m env a)) -> [env -> a] -> Router' env a
+ Servant.Server.Internal.Router: StaticRouter :: Map Text (Router' m env a) -> [env -> a] -> Router' env a
- Servant.Server.Internal.Router: StaticRouterStructure :: (Map Text RouterStructure) -> Int -> RouterStructure
+ Servant.Server.Internal.Router: StaticRouterStructure :: Map Text RouterStructure -> Int -> RouterStructure
- Servant.Server.Internal.RoutingApplication: [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
+ Servant.Server.Internal.RoutingApplication: [Delayed] :: {capturesD :: env -> DelayedM m captures, methodD :: DelayedM m (), authD :: DelayedM m auth, acceptD :: DelayedM m (), contentD :: DelayedM m contentType, paramsD :: DelayedM m params, headersD :: DelayedM m headers, bodyD :: contentType -> DelayedM m body, serverD :: captures -> params -> headers -> auth -> body -> Request -> RouteResult c} -> Delayed m env c
- Servant.Server.Internal.RoutingApplication: addBodyCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m a -> Delayed m env b
+ Servant.Server.Internal.RoutingApplication: addBodyCheck :: Monad m => Delayed m env (a -> b) -> DelayedM m c -> (c -> DelayedM m a) -> Delayed m env b
- 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
+ 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

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+0.8.3+-----++Add support for servant-0.14+Reorder handling of errors+ 0.8.2 ------ 
servant-snap.cabal view
@@ -1,5 +1,5 @@ name:                servant-snap-version:             0.8.2+version:             0.8.3 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.@@ -44,28 +44,29 @@     Servant.Server.Internal.SnapShims     Servant.Utils.StaticFiles   build-depends:-        base               >= 4.7  && < 4.11-      , aeson              >= 0.7  && < 1.3+        base               >= 4.7  && < 4.13+      , aeson              >= 0.7  && < 1.5       , 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-      , either             >= 4.3  && < 4.6+      , containers         >= 0.5  && < 0.7+      , either             >= 4.3  && < 5.1       , filepath           >= 1    && < 1.5       , http-media         >= 0.7.1.2 && < 0.8       , http-types         >= 0.8  && < 0.13-      , http-api-data      >= 0.2  && < 0.4+      , http-api-data      >= 0.2  && < 0.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.14+      , servant            >= 0.14 && < 0.15       , string-conversions >= 0.3  && < 0.5       , text               >= 1.2  && < 1.3       , snap               >= 1.0  && < 1.2       , snap-core          >= 1.0.2  && < 1.2       , snap-server        >= 1.0  && < 1.2+      , tagged             >= 0.8.5 && < 0.9       , transformers       >= 0.3  && < 0.6       , word8              >= 0.1  && < 0.2   hs-source-dirs: src@@ -114,14 +115,14 @@     , directory     , either     , exceptions-    , hspec              >= 2.4.3   && < 2.5+    , hspec              >= 2.4.3   && < 2.8     , io-streams     , lens     , process     , digestive-functors >= 0.8.1.0 && < 0.9     , time-    , hspec-snap         >= 1.0.0.2 && < 1.1-    , hspec-core         >= 2.4.3   && < 2.5+    , hspec-snap         >= 1.0.1.0 && < 1.1+    , hspec-core         >= 2.4.3   && < 2.8     , snap-cors     , http-types     , HUnit              >= 1.4
src/Servant/Server/Internal.hs view
@@ -7,10 +7,12 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings          #-} {-# LANGUAGE PolyKinds                  #-}+{-# LANGUAGE RankNTypes                 #-} {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE TupleSections              #-} {-# LANGUAGE TypeFamilies               #-} {-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE UndecidableInstances       #-} {-# LANGUAGE MultiParamTypeClasses      #-}  #if __GLASGOW_HASKELL__ < 710@@ -27,19 +29,25 @@  ------------------------------------------------------------------------------- import           Control.Applicative         ((<$>))+import           Control.Monad.Trans         (liftIO) import           Control.Monad.Trans.Class   (lift)-import           Control.Monad               (when)+import           Control.Monad               (join, 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.Either                 (partitionEithers)+import           Data.List                   (foldl') import           Data.Maybe                  (fromMaybe, isNothing, mapMaybe,                                               maybeToList) import           Data.Proxy-import           Data.String                 (fromString)+import           Data.String                 (IsString, fromString) import           Data.String.Conversions     (cs, (<>))+import           Data.Tagged                 (Tagged(..), retag)+import qualified Data.Text                   as T import           Data.Text                   (Text)+import           Data.Typeable               (Typeable) import           GHC.Generics import           GHC.TypeLits                (KnownNat, KnownSymbol, natVal,                                               symbolVal)@@ -49,8 +57,8 @@                                               hContentType, hAccept) import qualified Network.HTTP.Media          as NHM import           Web.HttpApiData             (FromHttpApiData,-                                              parseHeaderMaybe,-                                              parseQueryParamMaybe,+                                              parseHeader,+                                              parseQueryParam,                                               parseUrlPieceMaybe,                                               parseUrlPieces) import           Snap.Core                   hiding (Headers, Method,@@ -58,19 +66,24 @@                                               method, withRequest) import           Servant.API                 ((:<|>) (..), (:>), BasicAuth,                                               BoundaryStrategy(..),-                                              Capture,-                                              CaptureAll, FramingRender (..),-                                              Header, IsSecure(..), QueryFlag,-                                              QueryParam, QueryParams, Raw,-                                              RemoteHost, ReqBody,-                                              ReflectMethod(..), Stream,-                                              ToStreamGenerator (..), Verb,-                                              getStreamGenerator)+                                              Capture', CaptureAll,+                                              Description, EmptyAPI,+                                              FramingRender (..),+                                              Header', If, IsSecure(..),+                                              QueryFlag, QueryParam',+                                              QueryParams, Raw, RemoteHost,+                                              ReqBody', ReflectMethod(..),+                                              SBool(..), SBoolI,+                                              Stream, Summary,+                                              ToStreamGenerator (..), Vault,+                                              Verb, getStreamGenerator, sbool) import           Servant.API.ContentTypes    (AcceptHeader (..),                                               AllCTRender (..),                                               AllCTUnrender (..), AllMime(..),                                               MimeRender (..),                                               canHandleAcceptH, contentType)+import           Servant.API.Modifiers       (FoldLenient, FoldRequired,+                                              RequestArgument, unfoldRequestArgument) import           Servant.API.ResponseHeaders (Headers, getResponse, GetHeaders,                                               getHeaders) import qualified System.IO.Streams           as IOS@@ -93,6 +106,13 @@         -> Delayed m env (Server api context m)         -> Router m env +  hoistServerWithContext+      :: proxy api+      -> proxy' context+      -> (forall x. m x -> n x)+      -> ServerT api context m+      -> ServerT api context n+ type Server api context m = ServerT api context m  -- * Instances@@ -117,7 +137,11 @@     where pa = Proxy :: Proxy a           pb = Proxy :: Proxy b -captured :: FromHttpApiData a => proxy (Capture sym a) -> Text -> Maybe a+  hoistServerWithContext _ pc nt (a :<|> b) =+    hoistServerWithContext (Proxy :: Proxy a) pc nt a :<|>+    hoistServerWithContext (Proxy :: Proxy b) pc nt b++captured :: FromHttpApiData a => proxy (Capture' mods sym a) -> Text -> Maybe a captured _ = parseUrlPieceMaybe  -- | If you use 'Capture' in one of the endpoints for your API,@@ -127,7 +151,7 @@ -- 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.+-- by simply providing an instance of 'FromHttpApiData' for your type. -- -- Example: --@@ -137,30 +161,33 @@ -- > server = getBook -- >   where getBook :: Text -> EitherT ServantErr IO Book -- >         getBook isbn = ...-instance (FromHttpApiData a, HasServer sublayout context m)-      => HasServer (Capture capture a :> sublayout) context m where+instance (FromHttpApiData a, HasServer api context m)+      => HasServer (Capture' mods capture a :> api) context m where -  type ServerT (Capture capture a :> sublayout) context m =-     a -> ServerT sublayout context m+  type ServerT (Capture' mods capture a :> api) context m =+     a -> ServerT api context m +  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s+   route Proxy ctx d =     CaptureRouter $-      route (Proxy :: Proxy sublayout) ctx+      route (Proxy :: Proxy api) ctx         (addCapture d $ \ txt -> case parseUrlPieceMaybe txt of                                    Nothing -> delayedFail err400                                    Just v  -> return v         ) +instance (FromHttpApiData a, HasServer api context m)+      => HasServer (CaptureAll capture a :> api) context m where -instance (FromHttpApiData a, HasServer sublayout context m)-      => HasServer (CaptureAll capture a :> sublayout) context m where+  type ServerT (CaptureAll capture a :> api) context m =+    [a] -> ServerT api context m -  type ServerT (CaptureAll capture a :> sublayout) context m =-    [a] -> ServerT sublayout context m+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s    route Proxy ctx d =     CaptureAllRouter $-        route (Proxy :: Proxy sublayout) ctx+        route (Proxy :: Proxy api) ctx               (addCapture d $ \ txts -> case parseUrlPieces txts of                  Left _  -> delayedFail err400                  Right v -> return v@@ -240,10 +267,13 @@                                KnownNat status)   => HasServer (Verb method status ctypes a) context m where   type ServerT (Verb method status ctypes  a) context m = m a+  hoistServerWithContext _ _ nt s = nt s+   route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status     where method = reflectMethod (Proxy :: Proxy method)           status = toEnum . fromInteger $ natVal (Proxy :: Proxy status) + instance {-# OVERLAPPABLE #-} (AllCTRender ctypes a,                                ReflectMethod method,                                KnownNat status,@@ -251,19 +281,99 @@   => HasServer (Verb method status ctypes (Headers h a)) context m where    type ServerT (Verb method status ctypes (Headers h a)) context m = m (Headers h a)+  hoistServerWithContext _ _ nt s = nt s+   route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status     where method = reflectMethod (Proxy :: Proxy method)           status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)   +instance {-# OVERLAPPABLE #-} (MimeRender ctype a,+                               ReflectMethod method,+                               KnownNat status,+                               FramingRender framing ctype,+                               ToStreamGenerator b a)+  => HasServer (Stream method status framing ctype b) context m where+  type ServerT (Stream method status framing ctype b) context m = m b+  hoistServerWithContext _ _ nt s = nt s++  route Proxy _ = streamRouter ([],) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)+    where method = reflectMethod (Proxy :: Proxy method)+          status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)+++instance {-# OVERLAPPABLE #-} (MimeRender ctype a,+                               ReflectMethod method,+                               KnownNat status,+                               FramingRender framing ctype,+                               ToStreamGenerator b a,+                               GetHeaders (Headers h b))+  => HasServer (Stream method status framing ctype (Headers h b)) context m where++  type ServerT (Stream method status framing ctype (Headers h b)) context m = m (Headers h b)+  hoistServerWithContext _ _ nt s = nt s++  route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method status (Proxy :: Proxy framing) (Proxy :: Proxy ctype)+    where method = reflectMethod (Proxy :: Proxy method)+          status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)++streamRouter :: (MimeRender ctype a,+                 FramingRender framing ctype,+                 ToStreamGenerator b a,+                 MonadSnap m+                ) =>+                (c -> ([(HeaderName, B.ByteString)], b))+             -> Method+             -> Status+             -> Proxy framing+             -> Proxy ctype+             -> Delayed m env (m c)+             -> Router m env+streamRouter splitHeaders method (Status code msg) 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 (headerz, fa) = splitHeaders output+                    k = getStreamGenerator . toStreamGenerator $ fa in+                Route $ setResponseStatus code msg+                      $ (\r -> foldl' (\r' (h,h') -> addHeader h h' r') r $ contentHeader : headerz)+                      $ 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++ -- | 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.+-- All it asks is for a 'FromHttpApiData' instance. -- -- Example: --@@ -277,18 +387,39 @@ -- > server = viewReferer -- >   where viewReferer :: Referer -> EitherT ServantErr IO referer -- >         viewReferer referer = return referer-instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout context m)-      => HasServer (Header sym a :> sublayout) context m where+instance (KnownSymbol sym, FromHttpApiData a, HasServer api context m+         , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)+         )+      => HasServer (Header' mods sym a :> api) context m where -  type ServerT (Header sym a :> sublayout) context m =-    Maybe a -> ServerT sublayout context m+  type ServerT (Header' mods sym a :> api) context m =+    RequestArgument mods a -> ServerT api context m -  route Proxy ctx subserver =-    let mheader req = parseHeaderMaybe =<< getHeader str req-    in  route (Proxy :: Proxy sublayout) ctx (passToServer subserver mheader)-    where str = fromString $ symbolVal (Proxy :: Proxy sym)+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s +  route Proxy ctx subserver = route (Proxy :: Proxy api) ctx $+      subserver `addHeaderCheck` withRequest headerCheck+    where+      headerName :: IsString n => n+      headerName = fromString $ symbolVal (Proxy :: Proxy sym) +      headerCheck :: Request -> DelayedM m (RequestArgument mods a)+      headerCheck req =+          unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev+        where+          mev :: Maybe (Either Text a)+          mev = fmap parseHeader $ getHeader headerName req++          errReq = delayedFailFatal err400+            { errBody = "Header " <> headerName <> " is required"+            }++          errSt e = delayedFailFatal err400+            { errBody = cs $ "Error parsing header "+                             <> headerName+                             <> " failed: " <> e+            }+ -- | 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'@.@@ -299,7 +430,7 @@ -- 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.+-- by simply providing an instance of 'FromHttpApiData' for your type. -- -- Example: --@@ -310,24 +441,40 @@ -- >   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 context m)-      => HasServer (QueryParam sym a :> sublayout) context m where+instance (KnownSymbol sym, FromHttpApiData a, HasServer api context m+         , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)+         )+      => HasServer (QueryParam' mods sym a :> api) context m where -  type ServerT (QueryParam sym a :> sublayout) context m =-    Maybe a -> ServerT sublayout context m+  type ServerT (QueryParam' mods sym a :> api) context m =+    RequestArgument mods a -> ServerT api context m +  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s+   route Proxy ctx subserver =     let querytext r = parseQueryText $ rqQueryString r-        param r =-          case lookup paramname (querytext r) of-            Nothing       -> Nothing -- param absent from the query string-            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) ctx (passToServer subserver param)-    where paramname = cs $ symbolVal (Proxy :: Proxy sym)+        paramname = cs $ symbolVal (Proxy :: Proxy sym) +        parseParam :: Request -> DelayedM m (RequestArgument mods a)+        parseParam req =+            unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev+          where+            mev :: Maybe (Either T.Text a)+            mev = fmap parseQueryParam $ join $ lookup paramname $ querytext req +            errReq = delayedFailFatal err400+              { errBody = cs $ "Query parameter " <> paramname <> " is required"+              }++            errSt e = delayedFailFatal err400+              { errBody = cs $ "Error parsing query parameter "+                               <> paramname <> " failed: " <> e+              }++        delayed = addParameterCheck subserver . withRequest $ parseParam++    in route (Proxy :: Proxy api) ctx delayed+ -- | 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']@.@@ -337,7 +484,7 @@ -- 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.+-- by simply providing an instance of 'FromHttpApiData' for your type. -- -- Example: --@@ -347,26 +494,36 @@ -- > server = getBooksBy -- >   where getBooksBy :: [Text] -> EitherT ServantErr IO [Book] -- >         getBooksBy authors = ...return all books by these authors...-instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout context m)-      => HasServer (QueryParams sym a :> sublayout) context m where+instance (KnownSymbol sym, FromHttpApiData a, HasServer api context m)+      => HasServer (QueryParams sym a :> api) context m where -  type ServerT (QueryParams sym a :> sublayout) context m =-    [a] -> ServerT sublayout context m+  type ServerT (QueryParams sym a :> api) context m =+    [a] -> ServerT api context m -  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) ctx (passToServer subserver values)-    where paramname = cs $ symbolVal (Proxy :: Proxy sym)-          looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")-          convert Nothing = Nothing-          convert (Just v) = parseQueryParamMaybe v+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s +  route Proxy ctx subserver = route (Proxy :: Proxy api) ctx $+      subserver `addParameterCheck` withRequest paramsCheck+    where+      paramname = cs $ symbolVal (Proxy :: Proxy sym)+      paramsCheck req =+          case partitionEithers $ fmap parseQueryParam params of+              ([], parsed) -> return parsed+              (errs, _)   -> delayedFailFatal err400+                  { errBody = cs $ "Error parsing query parameter(s) "+                                   <> paramname <> " failed: "+                                   <> T.intercalate ", " errs+                  }+         where+           params :: [T.Text]+           params = mapMaybe snd+                  . filter (looksLikeParam . fst)+                  . parseQueryText+                  . rqQueryString+                  $ req +           looksLikeParam name = name == paramname || name == (paramname <> "[]")+ -- | 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'.@@ -379,19 +536,21 @@ -- > 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 context m)-      => HasServer (QueryFlag sym :> sublayout) context m where+instance (KnownSymbol sym, HasServer api context m)+      => HasServer (QueryFlag sym :> api) context m where -  type ServerT (QueryFlag sym :> sublayout) context m =-    Bool -> ServerT sublayout context m+  type ServerT (QueryFlag sym :> api) context m =+    Bool -> ServerT api context m -  route Proxy ctx subserver =+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s++  route Proxy context 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) ctx (passToServer subserver param)+    in  route (Proxy :: Proxy api) context (passToServer subserver param)     where paramname = cs $ symbolVal (Proxy :: Proxy sym)           examine v | v == "true" || v == "1" || v == "" = True                     | otherwise = False@@ -409,6 +568,8 @@    type ServerT Raw context m = m () +  hoistServerWithContext _ _ nt = nt+   route Proxy _ rawApplication = RawRouter $ \ env request respond -> do     r <- runDelayed rawApplication env request     case r of@@ -416,13 +577,13 @@       Fail a      -> respond $ Fail a       FailFatal e -> respond $ FailFatal e - -- | 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@.+-- header, it is treated as @application/octet-stream@ (as specified in+-- <https://tools.ietf.org/html/rfc7231#section-3.1.1.4 RFC7231>. -- This lets servant worry about extracting it from the request and turning -- it into a value of the type you specify. --@@ -437,95 +598,153 @@ -- > server = postBook -- >   where postBook :: Book -> EitherT ServantErr IO Book -- >         postBook book = ...insert into your db...-instance ( AllCTUnrender list a, HasServer sublayout context m-         ) => HasServer (ReqBody list a :> sublayout) context m where+instance ( AllCTUnrender list a, HasServer api context m, SBoolI (FoldLenient mods), MonadSnap m+         ) => HasServer (ReqBody' mods list a :> api) context m where -  type ServerT (ReqBody list a :> sublayout) context m =-    a -> ServerT sublayout context m+  type ServerT (ReqBody' mods list a :> api) context m =+    If (FoldLenient mods) (Either String a) a -> ServerT api context m -  route Proxy ctx subserver =-    route (Proxy :: Proxy sublayout) ctx (addBodyCheck (subserver ) bodyCheck')+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s++  route Proxy context subserver+      = route (Proxy :: Proxy api) context $+          addBodyCheck subserver ctCheck bodyCheck     where-      -- bodyCheck' :: DelayedM m a-      bodyCheck' = do-        req <- lift getRequest-        let contentTypeH = fromMaybe "application/octet-stream" $ getHeader hContentType req-        mrqbody <- handleCTypeH (Proxy :: Proxy list) (cs contentTypeH) <$>-                                 lift (readRequestBody 2147483647) -- Maximum size: 2GB-        case mrqbody of-          Nothing        -> delayedFailFatal err415-          Just (Left e)  -> delayedFailFatal err400 { errBody = cs e }-          Just (Right v) -> return v+      -- Content-Type check, we only lookup we can try to parse the request body+      ctCheck = withRequest $ \ request -> do+        -- See HTTP RFC 2616, section 7.2.1+        -- http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1+        -- See also "W3C Internet Media Type registration, consistency of use"+        -- http://www.w3.org/2001/tag/2002/0129-mime+        let contentTypeH = fromMaybe "application/octet-stream"+                         $ getHeader hContentType request+        case canHandleCTypeH (Proxy :: Proxy list) (cs contentTypeH) :: Maybe (BL.ByteString -> Either String a) of+          Nothing -> delayedFail err415+          Just f -> return f +      -- Body check, we get a body parsing functions as the first argument.+      bodyCheck f = do+        mrqbody <- lift $ runRequestBody $ \ stream -> f <$> BL.fromChunks <$> IOS.toList {- IOS.toLazyByteString -} stream+        case sbool :: SBool (FoldLenient mods) of+          STrue -> return mrqbody+          SFalse -> case mrqbody of+            Left e  -> delayedFailFatal err400 { errBody = cs e }+            Right v -> return v  -- | 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 context m) => HasServer (path :> sublayout) context m where+-- pass the rest of the request path to @api@.+instance (KnownSymbol path, HasServer api context m) => HasServer (path :> api) context m where -  type ServerT (path :> sublayout) context m = ServerT sublayout context m+  type ServerT (path :> api) context m = ServerT api context m -  route Proxy ctx subserver =+  route Proxy context subserver =     pathRouter       (cs (symbolVal proxyPath))-      (route (Proxy :: Proxy sublayout) ctx subserver)+      (route (Proxy :: Proxy api) context subserver)     where proxyPath = Proxy :: Proxy path+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt s +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 context subserver =+    route (Proxy :: Proxy api) context (passToServer subserver rqHostName)+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s++instance HasServer api context m => HasServer (IsSecure :> api) context m where+  type ServerT (IsSecure :> api) context m = IsSecure -> ServerT api context m++  route Proxy context subserver =+    route (Proxy :: Proxy api) context (passToServer subserver (bool NotSecure Secure . rqIsSecure))+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s++-- This appears to be about 1000000% WAI-specific, so better hope you didn't need it++-- instance HasServer api context m => HasServer (Vault :> api) context m where+--   type ServerT (Vault :> api) context m = Vault -> ServerT api context m+-- +--   route Proxy context subserver =+--     route (Proxy :: Proxy api) context (passToServer subserver vault)+--   hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s+ instance HasServer api context m => HasServer (HttpVersion :> api) context m where   type ServerT (HttpVersion :> api) context m = HttpVersion -> ServerT api context m    route Proxy ctx subserver =     route (Proxy :: Proxy api) ctx (passToServer subserver rqVersion)+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s +-- | Ignore @'Summary'@ in server handlers.+instance HasServer api ctx m => HasServer (Summary desc :> api) ctx m where+  type ServerT (Summary desc :> api) ctx m = ServerT api ctx m -instance HasServer api context m => HasServer (IsSecure :> api) context m where-  type ServerT (IsSecure :> api) context m = IsSecure -> ServerT api context m+  route _ = route (Proxy :: Proxy api)+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt s -  route Proxy ctx subserver =-    route (Proxy :: Proxy api) ctx (passToServer subserver (bool NotSecure Secure . rqIsSecure))+-- | Ignore @'Description'@ in server handlers.+instance HasServer api ctx m => HasServer (Description desc :> api) ctx m where+  type ServerT (Description desc :> api) ctx m = ServerT api ctx 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 _ = route (Proxy :: Proxy api)+  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt s -  route Proxy ctx subserver =-    route (Proxy :: Proxy api) ctx (passToServer subserver rqHostName)+-- | Singleton type representing a server that serves an empty API.+data EmptyServer = EmptyServer deriving (Typeable, Eq, Show, Bounded, Enum) --- newtype BasicAuthCheck m usr =---   BasicAuthCheck { unBasicAuthCheck :: BasicAuthData -> m (BasicAuthResult usr) }---   deriving (Functor, Generic)+-- | Server for `EmptyAPI`+emptyServer :: ServerT EmptyAPI context m+emptyServer = Tagged EmptyServer -data BasicAuthResult usr = Unauthorized | BadPassword | NoSuchUser | Authorized usr-  deriving (Functor, Eq, Read, Show, Generic)+-- | The server for an `EmptyAPI` is `emptyAPIServer`.+--+-- > type MyApi = "nothing" :> EmptyApi+-- >+-- > server :: Server MyApi+-- > server = emptyAPIServer+instance HasServer EmptyAPI context m where+  type ServerT EmptyAPI context m = Tagged m EmptyServer -instance (HasServer api context m,-          KnownSymbol realm,-          HasContextEntry context (BasicAuthCheck m usr)-         ) => HasServer (BasicAuth realm usr :> api) context m where+  route Proxy _ _ = StaticRouter mempty mempty++  hoistServerWithContext _ _ _ = retag++-- | Basic Authentication+instance ( KnownSymbol realm+         , HasServer api context m+         , 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)+  route Proxy context subserver =+    route (Proxy :: Proxy api) context (subserver `addAuthCheck` authCheck)     where       realm = B.pack $ symbolVal (Proxy :: Proxy realm)-      basicAuthContext = getContextEntry ctx-      authCheck = withRequest $ \req -> runBasicAuth req realm basicAuthContext+      basicAuthContext = getContextEntry context+      authCheck = withRequest $ \ req -> runBasicAuth req realm basicAuthContext +  hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s++-- * helpers+-- 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)+           FramingRender framing ctype -- , ToStreamGenerator f a+         ) => HasServer (Stream method status framing ctype (f a)) context m where+  type ServerT (Stream method status 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))+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 status framing ctype (Headers h (f a))) context m where+  type ServerT (Stream method status framing ctype (Headers h (f a))) context m = m (Headers h (f a))    route Proxy _ action =     streamRouter (\x -> (getHeaders x, getResponse x))@@ -537,48 +756,11 @@     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++-- 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)+-}
src/Servant/Server/Internal/RoutingApplication.hs view
@@ -118,14 +118,18 @@   Delayed :: { capturesD :: env -> DelayedM m captures              , methodD   :: DelayedM m ()              , authD     :: DelayedM m auth-             , bodyD     :: DelayedM m body-             , serverD   :: captures -> auth -> body -> Request -> RouteResult c+             , acceptD   :: DelayedM m ()+             , contentD  :: DelayedM m contentType+             , paramsD   :: DelayedM m params+             , headersD  :: DelayedM m headers+             , bodyD     :: contentType -> DelayedM m body+             , serverD   :: captures -> params -> headers -> auth -> body -> Request -> RouteResult c              } -> Delayed m env c  instance Functor (Delayed m env) where   fmap f Delayed{..} =     Delayed-      { serverD = \ c a b req -> f <$> serverD c a b req+      { serverD = \ c p h a b req -> f <$> serverD c p h a b req       , ..       } @@ -169,7 +173,7 @@ -- | A 'Delayed' without any stored checks. emptyDelayed :: Monad m => Proxy (m :: * -> *) -> RouteResult a -> Delayed m env a emptyDelayed (Proxy :: Proxy m) result =-  Delayed (const r) r r r (\ _ _ _ _ -> result)+  Delayed (const r) r r r r r r (const r) (\ _ _ _ _ _ _ -> result)   where     r :: DelayedM m ()     r = return ()@@ -193,10 +197,34 @@ addCapture Delayed{..} new =   Delayed     { capturesD = \ (txt, env) -> (,) <$> capturesD env <*> new txt-    , serverD   = \ (x, v) a b req -> ($ v) <$> serverD x a b req+    , serverD   = \ (x, v) p h a b req -> ($ v) <$> serverD x p h a b req     , ..     } -- Note [Existential Record Update] +-- | Add a parameter check to the end of the params block+addParameterCheck :: Monad m+               => Delayed m env (a -> b)+               -> DelayedM m a+               -> Delayed m env b+addParameterCheck Delayed {..} new =+  Delayed+    { paramsD = (,) <$> paramsD <*> new+    , serverD = \c (p, pNew) h a b req -> ($ pNew) <$> serverD c p h a b req+    , ..+    }++-- | Add a header check to the end of the headers block+addHeaderCheck :: Monad m+               => Delayed m env (a -> b)+               -> DelayedM m a+               -> Delayed m env b+addHeaderCheck Delayed {..} new =+  Delayed+    { headersD = (,) <$> headersD <*> new+    , serverD = \c p (h, hNew) a b req -> ($ hNew) <$> serverD c p h a b req+    , ..+    }+ -- | Add a method check to the end of the method block. addMethodCheck :: Monad m                => Delayed m env a@@ -216,25 +244,30 @@ addAuthCheck Delayed{..} new =   Delayed     { authD   = (,) <$> authD <*> new-    , serverD = \ c (y, v) b req -> ($ v) <$> serverD c y b req+    , serverD = \ c p h (y, v) b req -> ($ v) <$> serverD c p h y b req     , ..     } -- Note [Existential Record Update] --- | Add a body check to the end of the body block.+-- | Add a content type and body checks around parameter checks.+--+-- We'll report failed content type check (415), before trying to parse+-- query parameters (400). Which, in turn, happens before request body parsing. addBodyCheck :: Monad m              => Delayed m env (a -> b)-             -> DelayedM m a+             -> DelayedM m c        -- ^ content type check+             -> (c -> DelayedM m a) -- ^ body check              -> Delayed m env b-addBodyCheck Delayed{..} new =+addBodyCheck Delayed{..} newContentD newBodyD =   Delayed-    { bodyD   = (,) <$> bodyD <*> new-    , serverD = \ c a (z, v) req -> ($ v) <$> serverD c a z req+    { contentD = (,) <$> contentD <*> newContentD+    , bodyD   = \(content, c) -> (,) <$> bodyD content <*> newBodyD c+    , serverD = \ c p h a (z, v) req -> ($ v) <$> serverD c p h a z req     , ..     } -- Note [Existential Record Update]  --- | Add an accept header check to the beginning of the body--- block. There is a tradeoff here. In principle, we'd like+-- | Add an accept header check before handling parameters.+-- 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.@@ -249,7 +282,7 @@                -> Delayed m env a addAcceptCheck Delayed{..} new =   Delayed-    { bodyD = new *> bodyD+    { acceptD = acceptD *> new     , ..     } -- Note [Existential Record Update] @@ -259,7 +292,7 @@ passToServer :: Delayed m env (a -> b) -> (Request -> a) -> Delayed m env b passToServer Delayed{..} x =   Delayed-    { serverD = \ c a b req -> ($ x req) <$> serverD c a b req+    { serverD = \ c p h a b req -> ($ x req) <$> serverD c p h a b req     , ..     } -- Note [Existential Record Update] @@ -279,5 +312,9 @@   c <- capturesD env   methodD   a <- authD-  b <- bodyD-  DelayedM (\ req -> return $ serverD c a b req)+  acceptD+  content <- contentD+  p <- paramsD  -- Has to be before body parsing, but after content-type checks+  h <- headersD+  b <- bodyD content+  DelayedM (\ req -> return $ serverD c p h a b req)