on master help sign in
StreamingBody instead of Maybe Builder
447cd4aMichael Snoyman
wai/Network/Wai.hs+24−28
@@ -1,5 +1,6 @@
1{-# LANGUAGE Rank2Types #-}
1{-# LANGUAGE Rank2Types #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ImpredicativeTypes #-}
3{-|
4{-|
4
5
5This module defines a generic web application interface. It is a common
6This module defines a generic web application interface. It is a common
@@ -61,13 +62,13 @@ module Network.Wai
61 , lazyRequestBody
62 , lazyRequestBody
62 -- * Response
63 -- * Response
63 , Response
64 , Response
65+ , StreamingBody
64 , FilePart (..)
66 , FilePart (..)
65 -- ** Response composers
67 -- ** Response composers
66 , responseFile
68 , responseFile
67 , responseBuilder
69 , responseBuilder
68 , responseLBS
70 , responseLBS
69 , responseStream
71 , responseStream
70− , responseStreamBracket
71 , responseRaw
72 , responseRaw
72 -- * Response accessors
73 -- * Response accessors
73 , responseStatus
74 , responseStatus
@@ -78,9 +79,12 @@ module Network.Wai
78import Blaze.ByteString.Builder (Builder, fromLazyByteString)
79import Blaze.ByteString.Builder (Builder, fromLazyByteString)
79import Blaze.ByteString.Builder (fromByteString)
80import Blaze.ByteString.Builder (fromByteString)
80import Control.Exception (bracket, bracketOnError)
81import Control.Exception (bracket, bracketOnError)
82+import Control.Monad (unless)
81import qualified Data.ByteString as B
83import qualified Data.ByteString as B
82import qualified Data.ByteString.Lazy as L
84import qualified Data.ByteString.Lazy as L
85+import Data.ByteString.Lazy.Internal (defaultChunkSize)
83import Data.ByteString.Lazy.Char8 ()
86import Data.ByteString.Lazy.Char8 ()
87+import Data.Function (fix)
84import Data.Monoid (mempty)
88import Data.Monoid (mempty)
85import qualified Network.HTTP.Types as H
89import qualified Network.HTTP.Types as H
86import Network.Socket (SockAddr (SockAddrInet))
90import Network.Socket (SockAddr (SockAddrInet))
@@ -125,30 +129,12 @@ responseLBS :: H.Status -> H.ResponseHeaders -> L.ByteString -> Response
125responseLBS s h = ResponseBuilder s h . fromLazyByteString
129responseLBS s h = ResponseBuilder s h . fromLazyByteString
126
130
127-- | Creating 'Response' from 'C.Source'.
131-- | Creating 'Response' from 'C.Source'.
128−responseStream :: H.Status -> H.ResponseHeaders -> ((Maybe Builder -> IO ()) -> IO ()) -> Response
132+responseStream :: H.Status
133+ -> H.ResponseHeaders
134+ -> StreamingBody
135+ -> Response
129responseStream = ResponseStream
136responseStream = ResponseStream
130
137
131−-- | Creating 'Response' with allocated resource safely released.
132−--
133−-- * The first argument is an action to allocate resource.
134−--
135−-- * The second argument is a function to release the resource.
136−--
137−-- * The third argument is a function to create
138−-- ('H.Status','H.ResponseHeaders','C.Source' 'IO' ('C.Flush' 'Builder'))
139−-- from the resource.
140−responseStreamBracket :: IO a
141− -> (a -> IO ())
142− -> (a -> IO (H.Status
143− ,H.ResponseHeaders
144− ,(Maybe Builder -> IO ()) -> IO ()))
145− -> IO Response
146−responseStreamBracket setup teardown action =
147− bracketOnError setup teardown $ \resource -> do
148− (st,hdr,stream) <- action resource
149− return $ ResponseStream st hdr $ \f ->
150− bracket (return resource) teardown (\_ -> stream f)
151−
152-- | Create a response for a raw application. This is useful for \"upgrade\"
138-- | Create a response for a raw application. This is useful for \"upgrade\"
153-- situations such as WebSockets, where an application requests for the server
139-- situations such as WebSockets, where an application requests for the server
154-- to grant it raw network access.
140-- to grant it raw network access.
@@ -183,16 +169,26 @@ responseHeaders (ResponseRaw _ res) = responseHeaders res
183
169
184-- | Converting the body information in 'Response' to 'Source'.
170-- | Converting the body information in 'Response' to 'Source'.
185responseToStream :: Response
171responseToStream :: Response
172+ -> ( H.Status
186− -> (H.Status, H.ResponseHeaders, (Maybe Builder -> IO ()) -> IO ())
173+ , H.ResponseHeaders
174+ , forall a. (StreamingBody -> IO a) -> IO a
175+ )
187−responseToStream (ResponseStream s h b) = (s, h, b)
176+responseToStream (ResponseStream s h b) = (s, h, ($ b))
188responseToStream (ResponseFile s h fp (Just part)) =
177responseToStream (ResponseFile s h fp (Just part)) =
189 error "FIXME responseToStream"
178 error "FIXME responseToStream"
190 --(s, h, \f -> IO.withFile fp IO.ReadMode $ \handle -> f $ sourceFilePart handle part C.$= CL.map (C.Chunk . fromByteString))
179 --(s, h, \f -> IO.withFile fp IO.ReadMode $ \handle -> f $ sourceFilePart handle part C.$= CL.map (C.Chunk . fromByteString))
191responseToStream (ResponseFile s h fp Nothing) =
180responseToStream (ResponseFile s h fp Nothing) =
192− error "FIXME responseToStream"
181+ ( s
182+ , h
193− --(s, h, \f -> IO.withFile fp IO.ReadMode $ \handle -> f $ CB.sourceHandle handle C.$= CL.map (C.Chunk . fromByteString))
183+ , \withBody -> IO.withFile fp IO.ReadMode $ \handle ->
184+ withBody $ \sendChunk _flush -> fix $ \loop -> do
185+ bs <- B.hGetSome handle defaultChunkSize
186+ unless (B.null bs) $ do
187+ sendChunk $ fromByteString bs
188+ loop
189+ )
194responseToStream (ResponseBuilder s h b) =
190responseToStream (ResponseBuilder s h b) =
195− (s, h, ($ Just b))
191+ (s, h, \withBody -> withBody $ \sendChunk _flush -> sendChunk b)
196responseToStream (ResponseRaw _ res) = responseToStream res
192responseToStream (ResponseRaw _ res) = responseToStream res
197
193
198{- FIXME
194{- FIXME
wai/Network/Wai/Internal.hs+9−4
@@ -84,13 +84,18 @@ data Request = Request {
84data Response
84data Response
85 = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)
85 = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart)
86 | ResponseBuilder H.Status H.ResponseHeaders Builder
86 | ResponseBuilder H.Status H.ResponseHeaders Builder
87− -- FIXME perhaps need a better distinction between "allocate/free
88− -- resources" and "start running". See, for example, restore ugliness in
89− -- Warp
90− | ResponseStream H.Status H.ResponseHeaders ((Maybe Builder -> IO ()) -> IO ())
87+ | ResponseStream H.Status H.ResponseHeaders StreamingBody
91 | ResponseRaw (IO B.ByteString -> (B.ByteString -> IO ()) -> IO ()) Response
88 | ResponseRaw (IO B.ByteString -> (B.ByteString -> IO ()) -> IO ()) Response
92 deriving Typeable
89 deriving Typeable
93
90
91+-- | Represents a streaming HTTP response body. It's a function of two
92+-- parameters; the first parameter provides a means of sending another chunk of
93+-- data, and the second parameter provides a means of flushing the data to the
94+-- client.
95+--
96+-- Since 3.0.0
97+type StreamingBody = (Builder -> IO ()) -> IO () -> IO ()
98+
94-- | The size of the request body. In the case of chunked bodies, the size will
99-- | The size of the request body. In the case of chunked bodies, the size will
95-- not be known.
100-- not be known.
96--
101--