@@ -31,9 +31,7 @@ module Network.Wai.HTTP2
3131 , Responder(..)
3232 , RespondFunc
3333 , Body
34− , BodyOf
3534 , Chunk(..)
36− , TrailerFunc
3735 , Trailers
3836 -- * Server push
3937 , PushFunc
@@ -50,14 +48,13 @@ module Network.Wai.HTTP2
5048 , respondNotFound
5149 , respondWith
5250 -- ** Stream Bodies
53− , SimpleBody
5451 , streamFilePart
5552 , streamBuilder
5653 , streamSimple
5754 ) where
5855
5956import Blaze.ByteString.Builder (Builder)
60−import Control.Exception (Exception, SomeException, throwIO)
57+import Control.Exception (Exception, throwIO)
6158import Control.Monad.Trans.Cont (ContT(..))
6259import Data.ByteString (ByteString)
6360#if __GLASGOW_HASKELL__ < 709
@@ -76,6 +73,7 @@ import Network.Wai.Internal
7673 , Request(requestHeaders)
7774 , Response(..)
7875 , ResponseReceived(..)
76+ , StreamingBody
7977 , adjustForFilePart
8078 , chooseFilePart
8179 , tryGetFileSize
@@ -102,21 +100,12 @@ data Chunk = FileChunk FilePath FilePart | BuilderChunk Builder
102100
103101-- | The streaming body of a response. Equivalent to
104102-- 'Network.Wai.StreamingBody' except that it can also write file ranges and
105−-- return a result of type @a@.
106−type Body a = BodyOf Chunk a
103+-- return the stream's trailers.
104+type Body = (Chunk -> IO ()) -> IO () -> IO Trailers
107105
108−-- | Generalization of 'Body' to arbitrary chunk types.
109−--
110−-- 'Network.Wai.StreamingBody' is identical to @BodyOf Builder ()@.
111−type BodyOf c a = (c -> IO ()) -> IO () -> IO a
112−
113−-- | Create trailers based on the result of a stream body.
114−type TrailerFunc a = Either SomeException a -> Maybe Trailers -- TODO IO?
115−
116−-- | Given to 'Responders'; provide a status, headers, a stream body, and a way
117−-- to produce trailers, and we'll give you a token proving you called the
118−-- 'RespondFunc'.
119−type RespondFunc s a = H.Status -> H.ResponseHeaders -> TrailerFunc a -> Body a -> IO s
106+-- | Given to 'Responders'; provide a status, headers, and a stream body, and
107+-- we'll give you a token proving you called the 'RespondFunc'.
108+type RespondFunc s = H.Status -> H.ResponseHeaders -> Body -> IO s
120109
121110-- | The result of an 'HTTP2Application'; or, alternately, an application
122111-- that's independent of the request. This is a continuation-passing style
@@ -125,9 +114,9 @@ type RespondFunc s a = H.Status -> H.ResponseHeaders -> TrailerFunc a -> Body a
125114--
126115-- The respond function is similar to the one in 'Network.Wai.Application', but
127116-- it only takes a streaming body, the status and headers are curried, and it
128−-- passes on any result value from the stream body.
117+-- also produces trailers for the stream.
129118newtype Responder = Responder
130− { runResponder :: forall s. (forall a. RespondFunc s a) -> IO s }
119+ { runResponder :: forall s. RespondFunc s -> IO s }
131120
132121-- | A function given to an 'HTTP2Application' to initiate a server-pushed
133122-- stream. Its argument is the same as the result of an 'HTTP2Application', so
@@ -160,8 +149,8 @@ promiseHeaders p =
160149-- | Create a response body consisting of a single range of a file. Does not
161150-- set Content-Length or Content-Range headers. For that, use
162151-- 'respondFilePart' or 'respondFile'.
163−streamFilePart :: FilePath -> FilePart -> Body ()
164−streamFilePart path part write _ = write $ FileChunk path part
152+streamFilePart :: FilePath -> FilePart -> Body
153+streamFilePart path part write _ = write (FileChunk path part) >> return []
165154
166155-- | Respond with a single range of a file, adding the Accept-Ranges,
167156-- Content-Length and Content-Range headers and changing the status to 206 as
@@ -174,7 +163,7 @@ streamFilePart path part write _ = write $ FileChunk path part
174163respondFilePart :: H.Status -> H.ResponseHeaders -> FilePath -> FilePart -> Responder
175164respondFilePart s h path part = Responder $ \k -> do
176165 let (s', h') = adjustForFilePart s h part
177− k s' h' mempty $ streamFilePart path part
166+ k s' h' $ streamFilePart path part
178167
179168-- | Serve the requested range of the specified file (based on the Range
180169-- header), using the given 'H.Status' and 'H.ResponseHeaders' as a base. If
@@ -196,7 +185,7 @@ respondFileExists s h path size reqHdrs =
196185
197186-- | Respond with a minimal 404 page with the given headers.
198187respondNotFound :: H.ResponseHeaders -> Responder
199−respondNotFound h = Responder $ \k -> k H.notFound404 h' mempty $
188+respondNotFound h = Responder $ \k -> k H.notFound404 h' $
200189 streamBuilder "File not found."
201190 where
202191 contentType = (H.hContentType, "text/plain; charset=utf-8")
@@ -204,8 +193,8 @@ respondNotFound h = Responder $ \k -> k H.notFound404 h' mempty $
204193
205194-- | Construct a 'Responder' that will just call the 'RespondFunc' with the
206195-- given arguments.
207−respond :: H.Status -> H.ResponseHeaders -> TrailerFunc a -> Body a -> Responder
208−respond s h t b = Responder $ \k -> k s h t b
196+respond :: H.Status -> H.ResponseHeaders -> Body -> Responder
197+respond s h b = Responder $ \k -> k s h b
209198
210199-- | Fold the given bracketing action into a 'Responder'. Note the first
211200-- argument is isomorphic to @Codensity IO a@ or @forall s. ContT s IO a@, and
@@ -233,17 +222,12 @@ respondIO :: IO Responder -> Responder
233222respondIO io = Responder $ \k -> io >>= \r -> runResponder r k
234223
235224-- | Create a response body consisting of a single builder.
236−streamBuilder :: Builder -> Body ()
237−streamBuilder builder write _ = write $ BuilderChunk builder
238−
239−-- | Equivalent to 'Body' but only streaming 'Builder's.
240−--
241−-- 'Network.Wai.StreamingBody' is identical to @SimpleBody ()@.
242−type SimpleBody a = BodyOf Builder a
225+streamBuilder :: Builder -> Body
226+streamBuilder builder write _ = write (BuilderChunk builder) >> return []
243227
244228-- | Create a response body of a stream of 'Builder's.
245−streamSimple :: SimpleBody a -> Body a
246−streamSimple body write flush = body (write . BuilderChunk) flush
229+streamSimple :: StreamingBody -> Body
230+streamSimple body write flush = body (write . BuilderChunk) flush >> return []
247231
248232-- | Use a normal WAI 'Response' to send the response. Useful if you're
249233-- sharing code between HTTP\/2 applications and HTTP\/1 applications.
@@ -253,9 +237,9 @@ streamSimple body write flush = body (write . BuilderChunk) flush
253237promoteResponse :: Request -> Response -> Responder
254238promoteResponse req response = case response of
255239 (ResponseBuilder s h b) ->
256− Responder $ \k -> k s h mempty (streamBuilder b)
240+ Responder $ \k -> k s h (streamBuilder b)
257241 (ResponseStream s h body) ->
258− Responder $ \k -> k s h mempty (streamSimple body)
242+ Responder $ \k -> k s h (streamSimple body)
259243 (ResponseRaw _ fallback) -> promoteResponse req fallback
260244 (ResponseFile s h path mpart) -> maybe
261245 (respondFile s h path $ requestHeaders req)