@@ -1,5 +1,6 @@
1{-# LANGUAGE Rank2Types #-}
1{-# LANGUAGE Rank2Types #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ImpredicativeTypes #-}
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
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
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
129responseStream = ResponseStream
136responseStream = ResponseStream
131−-- | Creating 'Response' with allocated resource safely released.
133−-- * The first argument is an action to allocate resource.
135−-- * The second argument is a function to release the resource.
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
142− -> (a -> IO (H.Status
144− ,(Maybe Builder -> IO ()) -> IO ()))
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)
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
184-- | Converting the body information in 'Response' to 'Source'.
170-- | Converting the body information in 'Response' to 'Source'.
185responseToStream :: Response
171responseToStream :: Response
186− -> (H.Status, H.ResponseHeaders, (Maybe Builder -> IO ()) -> IO ())
173+ , H.ResponseHeaders
174+ , forall a. (StreamingBody -> IO a) -> IO a
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
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
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