@@ -25,12 +25,15 @@ module Servant.Server.Internal
2525 , module Servant.Server.Internal.ServantErr
2626 ) where
2727
28+import Control.Monad (when)
2829import Control.Monad.Trans (liftIO)
2930import Control.Monad.Trans.Resource (runResourceT)
3031import qualified Data.ByteString as B
32+import qualified Data.ByteString.Builder as BB
3133import qualified Data.ByteString.Char8 as BC8
3234import qualified Data.ByteString.Lazy as BL
33−import Data.Maybe (fromMaybe, mapMaybe)
35+import Data.Maybe (fromMaybe, mapMaybe,
36+ isNothing, maybeToList)
3437import Data.Either (partitionEithers)
3538import Data.String (fromString)
3639import Data.String.Conversions (cs, (<>))
@@ -40,13 +43,15 @@ import Data.Typeable
4043import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
4144 symbolVal)
4245import Network.HTTP.Types hiding (Header, ResponseHeaders)
46+import qualified Network.HTTP.Media as NHM
4347import Network.Socket (SockAddr)
4448import Network.Wai (Application, Request, Response,
4549 httpVersion, isSecure,
4650 lazyRequestBody,
4751 rawQueryString, remoteHost,
4852 requestHeaders, requestMethod,
49− responseLBS, vault)
53+ responseLBS, responseStream,
54+ vault)
5055import Prelude ()
5156import Prelude.Compat
5257import Web.HttpApiData (FromHttpApiData, parseHeader,
@@ -60,11 +65,16 @@ import Servant.API ((:<|>) (..), (:>), BasicAuth, Capt
6065 QueryParam, QueryParams, Raw,
6166 RemoteHost, ReqBody, Vault,
6267 WithNamedContext,
63− Description, Summary)
68+ Description, Summary,
69+ Accept(..),
70+ Framing(..), Stream,
71+ StreamGenerator(..),
72+ BoundaryStrategy(..))
6473import Servant.API.ContentTypes (AcceptHeader (..),
6574 AllCTRender (..),
6675 AllCTUnrender (..),
6776 AllMime,
77+ MimeRender(..),
6878 canHandleAcceptH)
6979import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
7080 getResponse)
@@ -276,6 +286,42 @@ instance OVERLAPPING_
276286 where method = reflectMethod (Proxy :: Proxy method)
277287 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
278288
289+
290+instance ( MimeRender ctype a, ReflectMethod method, Framing framing ctype
291+ ) => HasServer (Stream method framing ctype a) context where
292+
293+ type ServerT (Stream method framing ctype a) m = m (StreamGenerator a)
294+ hoistServerWithContext _ _ nt s = nt s
295+
296+ route Proxy _ action = leafRouter $ \env request respond ->
297+ let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
298+ cmediatype = NHM.matchAccept [contentType (Proxy :: Proxy ctype)] accH
299+ accCheck = when (isNothing cmediatype) $ delayedFail err406
300+ contentHeader = (hContentType, NHM.renderHeader . maybeToList $ cmediatype)
301+ in runAction (action `addMethodCheck` methodCheck method request
302+ `addAcceptCheck` accCheck
303+ ) env request respond $ \ (StreamGenerator k) ->
304+ Route $ responseStream status200 [contentHeader] $ \write flush -> do
305+ write . BB.lazyByteString . header (Proxy :: Proxy framing) $ (Proxy :: Proxy ctype)
306+ case boundary (Proxy :: Proxy framing) (Proxy :: Proxy ctype) of
307+ BoundaryStrategyBracket f ->
308+ let go x = let bs = mimeRender (Proxy :: Proxy ctype) $ x
309+ (before, after) = f bs
310+ in write ( BB.lazyByteString before
311+ <> BB.lazyByteString bs
312+ <> BB.lazyByteString after)
313+ in k go go
314+ BoundaryStrategyIntersperse sep -> k
315+ (\x -> do
316+ write . BB.lazyByteString . mimeRender (Proxy :: Proxy ctype) $ x
317+ flush)
318+ (\x -> do
319+ write . (BB.lazyByteString sep <>) . BB.lazyByteString . mimeRender (Proxy :: Proxy ctype) $ x
320+ flush)
321+ write . BB.lazyByteString . terminate (Proxy :: Proxy framing) $ (Proxy :: Proxy ctype)
322+ where method = reflectMethod (Proxy :: Proxy method)
323+
324+
279325-- | If you use 'Header' in one of the endpoints for your API,
280326-- this automatically requires your server-side handler to be a function
281327-- that takes an argument of the type specified by 'Header'.
@@ -318,7 +364,7 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer api context)
318364 <> fromString headerName
319365 <> " failed: " <> e
320366 }
321− Right header -> return $ Just header
367+ Right hdr -> return $ Just hdr
322368
323369-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
324370-- this automatically requires your server-side handler to be a function