@@ -25,12 +25,15 @@ module Servant.Server.Internal
25 , module Servant.Server.Internal.ServantErr
25 , module Servant.Server.Internal.ServantErr
28+import Control.Monad (when)
28import Control.Monad.Trans (liftIO)
29import Control.Monad.Trans (liftIO)
29import Control.Monad.Trans.Resource (runResourceT)
30import Control.Monad.Trans.Resource (runResourceT)
30import qualified Data.ByteString as B
31import qualified Data.ByteString as B
32+import qualified Data.ByteString.Builder as BB
31import qualified Data.ByteString.Char8 as BC8
33import qualified Data.ByteString.Char8 as BC8
32import qualified Data.ByteString.Lazy as BL
34import qualified Data.ByteString.Lazy as BL
33−import Data.Maybe (fromMaybe, mapMaybe)
35+import Data.Maybe (fromMaybe, mapMaybe,
36+ isNothing, maybeToList)
34import Data.Either (partitionEithers)
37import Data.Either (partitionEithers)
35import Data.String (fromString)
38import Data.String (fromString)
36import Data.String.Conversions (cs, (<>))
39import Data.String.Conversions (cs, (<>))
@@ -40,13 +43,15 @@ import Data.Typeable
40import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
43import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
41 symbolVal)
44 symbolVal)
42import Network.HTTP.Types hiding (Header, ResponseHeaders)
45import Network.HTTP.Types hiding (Header, ResponseHeaders)
46+import qualified Network.HTTP.Media as NHM
43import Network.Socket (SockAddr)
47import Network.Socket (SockAddr)
44import Network.Wai (Application, Request, Response,
48import Network.Wai (Application, Request, Response,
45 httpVersion, isSecure,
49 httpVersion, isSecure,
46 lazyRequestBody,
50 lazyRequestBody,
47 rawQueryString, remoteHost,
51 rawQueryString, remoteHost,
48 requestHeaders, requestMethod,
52 requestHeaders, requestMethod,
49− responseLBS, vault)
53+ responseLBS, responseStream,
50import Prelude ()
55import Prelude ()
51import Prelude.Compat
56import Prelude.Compat
52import Web.HttpApiData (FromHttpApiData, parseHeader,
57import Web.HttpApiData (FromHttpApiData, parseHeader,
@@ -60,11 +65,16 @@ import Servant.API ((:<|>) (..), (:>), BasicAuth, Capt
60 QueryParam, QueryParams, Raw,
65 QueryParam, QueryParams, Raw,
61 RemoteHost, ReqBody, Vault,
66 RemoteHost, ReqBody, Vault,
62 WithNamedContext,
67 WithNamedContext,
63− Description, Summary)
68+ Description, Summary,
72+ BoundaryStrategy(..))
64import Servant.API.ContentTypes (AcceptHeader (..),
73import Servant.API.ContentTypes (AcceptHeader (..),
65 AllCTRender (..),
74 AllCTRender (..),
66 AllCTUnrender (..),
75 AllCTUnrender (..),
68 canHandleAcceptH)
78 canHandleAcceptH)
69import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
79import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders,
70 getResponse)
80 getResponse)
@@ -276,6 +286,42 @@ instance OVERLAPPING_
276 where method = reflectMethod (Proxy :: Proxy method)
286 where method = reflectMethod (Proxy :: Proxy method)
277 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
287 status = toEnum . fromInteger $ natVal (Proxy :: Proxy status)
290+instance ( MimeRender ctype a, ReflectMethod method, Framing framing ctype
291+ ) => HasServer (Stream method framing ctype a) context where
293+ type ServerT (Stream method framing ctype a) m = m (StreamGenerator a)
294+ hoistServerWithContext _ _ nt s = nt s
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)
314+ BoundaryStrategyIntersperse sep -> k
316+ write . BB.lazyByteString . mimeRender (Proxy :: Proxy ctype) $ x
319+ write . (BB.lazyByteString sep <>) . BB.lazyByteString . mimeRender (Proxy :: Proxy ctype) $ x
321+ write . BB.lazyByteString . terminate (Proxy :: Proxy framing) $ (Proxy :: Proxy ctype)
322+ where method = reflectMethod (Proxy :: Proxy method)
279-- | If you use 'Header' in one of the endpoints for your API,
325-- | If you use 'Header' in one of the endpoints for your API,
280-- this automatically requires your server-side handler to be a function
326-- this automatically requires your server-side handler to be a function
281-- that takes an argument of the type specified by 'Header'.
327-- that takes an argument of the type specified by 'Header'.
@@ -318,7 +364,7 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer api context)
318 <> fromString headerName
364 <> fromString headerName
319 <> " failed: " <> e
365 <> " failed: " <> e
321− Right header -> return $ Just header
367+ Right hdr -> return $ Just hdr
323-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
369-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
324-- this automatically requires your server-side handler to be a function
370-- this automatically requires your server-side handler to be a function