on master help sign in
Switched Enumerator to newtype
b4470e7Michael Snoyman
Network/Wai.hs+6−4
@@ -56,7 +56,7 @@ module Network.Wai
56 , statusCode
56 , statusCode
57 , statusMessage
57 , statusMessage
58 -- * Enumerator
58 -- * Enumerator
59− , Enumerator
59+ , Enumerator (..)
60 -- * WAI interface
60 -- * WAI interface
61 , Request (..)
61 , Request (..)
62 , Response (..)
62 , Response (..)
@@ -341,9 +341,11 @@ statusMessage (Status _ m) = m
341-- 'Enumerator' may only be called once. While this requirement puts a bit of a
341-- 'Enumerator' may only be called once. While this requirement puts a bit of a
342-- strain on the caller in some situations, it saves a large amount of
342-- strain on the caller in some situations, it saves a large amount of
343-- complication- and thus performance- on the producer.
343-- complication- and thus performance- on the producer.
344+newtype Enumerator = Enumerator { runEnumerator :: forall a.
344−type Enumerator a = (a -> B.ByteString -> IO (Either a a))
345+ (a -> B.ByteString -> IO (Either a a))
345 -> a
346 -> a
346 -> IO (Either a a)
347 -> IO (Either a a)
348+}
347
349
348-- | Information on the request sent by the client. This abstracts away the
350-- | Information on the request sent by the client. This abstracts away the
349-- details of the underlying implementation.
351-- details of the underlying implementation.
@@ -361,7 +363,7 @@ data Request = Request
361 , serverPort :: Int
363 , serverPort :: Int
362 , requestHeaders :: [(RequestHeader, B.ByteString)]
364 , requestHeaders :: [(RequestHeader, B.ByteString)]
363 , urlScheme :: UrlScheme
365 , urlScheme :: UrlScheme
364− , requestBody :: forall a. Enumerator a
366+ , requestBody :: Enumerator
365 , errorHandler :: String -> IO ()
367 , errorHandler :: String -> IO ()
366 -- | The client\'s host information.
368 -- | The client\'s host information.
367 , remoteHost :: B.ByteString
369 , remoteHost :: B.ByteString
@@ -374,7 +376,7 @@ data Response = Response
374 -- files from the disk. This datatype facilitates this optimization; if
376 -- files from the disk. This datatype facilitates this optimization; if
375 -- 'Left' is returns, the server will send the file from the disk by whatever
377 -- 'Left' is returns, the server will send the file from the disk by whatever
376 -- means it wishes. If 'Right', it will call the 'Enumerator'.
378 -- means it wishes. If 'Right', it will call the 'Enumerator'.
377− , responseBody :: forall a. Either FilePath (Enumerator a)
379+ , responseBody :: Either FilePath Enumerator
378 }
380 }
379
381
380type Application = Request -> IO Response
382type Application = Request -> IO Response
Network/Wai/Enumerator.hs+22−20
@@ -17,7 +17,7 @@ module Network.Wai.Enumerator
17 , fromEitherFile
17 , fromEitherFile
18 ) where
18 ) where
19
19
20−import Network.Wai (Enumerator)
20+import Network.Wai (Enumerator (..))
21import qualified Data.ByteString.Lazy as L
21import qualified Data.ByteString.Lazy as L
22import qualified Data.ByteString as B
22import qualified Data.ByteString as B
23import System.IO.Unsafe (unsafeInterleaveIO)
23import System.IO.Unsafe (unsafeInterleaveIO)
@@ -28,13 +28,13 @@ import Control.Concurrent.MVar
28
28
29-- | Performs a specified conversion on each 'B.ByteString' output by an
29-- | Performs a specified conversion on each 'B.ByteString' output by an
30-- enumerator.
30-- enumerator.
31−mapE :: (B.ByteString -> B.ByteString) -> Enumerator a -> Enumerator a
31+mapE :: (B.ByteString -> B.ByteString) -> Enumerator -> Enumerator
32−mapE f e iter = e iter' where
32+mapE f (Enumerator e) = Enumerator $ \iter -> e (iter' iter) where
33− iter' a = iter a . f
33+ iter' iter a = iter a . f
34
34
35-- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
35-- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
36-- normal lazy I/O warnings apply.
36-- normal lazy I/O warnings apply.
37−toLBS :: Enumerator () -> IO L.ByteString
37+toLBS :: Enumerator -> IO L.ByteString
38toLBS e = do
38toLBS e = do
39 source <- toSource e
39 source <- toSource e
40 L.fromChunks `fmap` helper source
40 L.fromChunks `fmap` helper source
@@ -48,26 +48,27 @@ toLBS e = do
48 return $ x' : xs
48 return $ x' : xs
49
49
50-- | This function safely converts a lazy bytestring into an enumerator.
50-- | This function safely converts a lazy bytestring into an enumerator.
51−fromLBS :: L.ByteString -> (forall a. Enumerator a)
51+fromLBS :: L.ByteString -> Enumerator
52−fromLBS lbs iter a0 = helper a0 $ L.toChunks lbs where
52+fromLBS lbs = Enumerator $ \iter a0 -> helper iter a0 $ L.toChunks lbs where
53− helper a [] = return $ Right a
53+ helper _ a [] = return $ Right a
54− helper a (x:xs) = do
54+ helper iter a (x:xs) = do
55 ea <- iter a x
55 ea <- iter a x
56 case ea of
56 case ea of
57 Left a' -> return $ Left a'
57 Left a' -> return $ Left a'
58− Right a' -> helper a' xs
58+ Right a' -> helper iter a' xs
59
59
60-- | Same as 'fromLBS', but the lazy bytestring is in the IO monad. This allows
60-- | Same as 'fromLBS', but the lazy bytestring is in the IO monad. This allows
61-- you to lazily read a file into memory, perform some mapping on the data and
61-- you to lazily read a file into memory, perform some mapping on the data and
62-- convert it into an enumerator.
62-- convert it into an enumerator.
63−fromLBS' :: IO L.ByteString -> (forall a. Enumerator a)
63+fromLBS' :: IO L.ByteString -> Enumerator
64−fromLBS' lbs' iter a0 = lbs' >>= \lbs -> fromLBS lbs iter a0
64+fromLBS' lbs' = Enumerator $ \iter a0 -> lbs' >>= \lbs ->
65+ runEnumerator (fromLBS lbs) iter a0
65
66
66-- | A source is a more standard way of accessing data from an 'Enumerator'.
67-- | A source is a more standard way of accessing data from an 'Enumerator'.
67-- Each time you call it, it returns the next chunk of data if available, or
68-- Each time you call it, it returns the next chunk of data if available, or
68-- 'Nothing' if the data has been completely consumed.
69-- 'Nothing' if the data has been completely consumed.
69−toSource :: Enumerator () -> IO (IO (Maybe B.ByteString))
70+toSource :: Enumerator -> IO (IO (Maybe B.ByteString))
70−toSource e = do
71+toSource (Enumerator e) = do
71 buffer <- newEmptyMVar
72 buffer <- newEmptyMVar
72 _ <- forkIO $ e (helper buffer) () >> putMVar buffer Nothing
73 _ <- forkIO $ e (helper buffer) () >> putMVar buffer Nothing
73 return $ source buffer
74 return $ source buffer
@@ -93,8 +94,8 @@ toSource e = do
93-- | Read a chunk of data from the given 'Handle' at a time. We use
94-- | Read a chunk of data from the given 'Handle' at a time. We use
94-- 'defaultChunkSize' from the bytestring package to determine the largest
95-- 'defaultChunkSize' from the bytestring package to determine the largest
95-- chunk to take.
96-- chunk to take.
96−fromHandle :: Handle -> Enumerator a
97+fromHandle :: Handle -> Enumerator
97−fromHandle h iter a = do
98+fromHandle h = Enumerator $ \iter a -> do
98 eof <- hIsEOF h
99 eof <- hIsEOF h
99 if eof
100 if eof
100 then return $ Right a
101 then return $ Right a
@@ -103,14 +104,15 @@ fromHandle h iter a = do
103 ea' <- iter a bs
104 ea' <- iter a bs
104 case ea' of
105 case ea' of
105 Left a' -> return $ Left a'
106 Left a' -> return $ Left a'
106− Right a' -> fromHandle h iter a'
107+ Right a' -> runEnumerator (fromHandle h) iter a'
107
108
108-- | A little wrapper around 'fromHandle' which first opens a file for reading.
109-- | A little wrapper around 'fromHandle' which first opens a file for reading.
109−fromFile :: FilePath -> Enumerator a
110+fromFile :: FilePath -> Enumerator
110−fromFile fp iter a0 = withBinaryFile fp ReadMode $ \h -> fromHandle h iter a0
111+fromFile fp = Enumerator $ \iter a0 -> withBinaryFile fp ReadMode $ \h ->
112+ runEnumerator (fromHandle h) iter a0
111
113
112-- | Since the response body is defined as an 'Either' 'FilePath' 'Enumerator',
114-- | Since the response body is defined as an 'Either' 'FilePath' 'Enumerator',
113-- this function simply reduces the whole operator to an enumerator. This can
115-- this function simply reduces the whole operator to an enumerator. This can
114-- be convenient for server implementations not optimizing file sending.
116-- be convenient for server implementations not optimizing file sending.
115−fromEitherFile :: Either FilePath (Enumerator a) -> Enumerator a
117+fromEitherFile :: Either FilePath Enumerator -> Enumerator
116fromEitherFile = either fromFile id
118fromEitherFile = either fromFile id
Network/Wai/Handler/SimpleServer.hs+7−6
@@ -134,10 +134,11 @@ parseRequest port lines' handle remoteHost' = do
134 , remoteHost = B8.pack remoteHost'
134 , remoteHost = B8.pack remoteHost'
135 }
135 }
136
136
137−requestBodyHandle :: Handle -> MVar Int -> Enumerator a
137+requestBodyHandle :: Handle -> MVar Int -> Enumerator
138+requestBodyHandle h mlen = Enumerator $ \iter accum ->
138−requestBodyHandle h mlen iter accum = modifyMVar mlen (helper accum) where
139+ modifyMVar mlen (helper iter accum) where
139− helper a 0 = return (0, Right a)
140+ helper _ a 0 = return (0, Right a)
140− helper a len = do
141+ helper iter a len = do
141 let maxChunkSize = 1024
142 let maxChunkSize = 1024
142 bs <- BS.hGet h $ min len maxChunkSize
143 bs <- BS.hGet h $ min len maxChunkSize
143 let newLen = len - BS.length bs
144 let newLen = len - BS.length bs
@@ -145,7 +146,7 @@ requestBodyHandle h mlen iter accum = modifyMVar mlen (helper accum) where
145 ea' <- iter a bs
146 ea' <- iter a bs
146 case ea' of
147 case ea' of
147 Left a' -> return (newLen, Left a')
148 Left a' -> return (newLen, Left a')
148− Right a' -> helper a' newLen
149+ Right a' -> helper iter a' newLen
149
150
150parseFirst :: (StringLike s, MonadFailure InvalidRequest m) =>
151parseFirst :: (StringLike s, MonadFailure InvalidRequest m) =>
151 s
152 s
@@ -170,7 +171,7 @@ sendResponse h res = do
170 BS.hPut h $ SL.pack "\r\n"
171 BS.hPut h $ SL.pack "\r\n"
171 case responseBody res of
172 case responseBody res of
172 Left fp -> unsafeSendFile h fp
173 Left fp -> unsafeSendFile h fp
173− Right enum -> enum myPut h >> return ()
174+ Right (Enumerator enum) -> enum myPut h >> return ()
174 where
175 where
175 myPut _ bs = do
176 myPut _ bs = do
176 putStrLn $ "sending a chunk of size " ++ show (BS.length bs)
177 putStrLn $ "sending a chunk of size " ++ show (BS.length bs)
lazy-test.hs+5−5
@@ -15,15 +15,15 @@ app req = case B8.unpack $ pathInfo req of
15indexResponse :: IO Response
15indexResponse :: IO Response
16indexResponse = return Response
16indexResponse = return Response
17 { status = Status200
17 { status = Status200
18− , headers = [(ContentType, B8.pack "text/html")]
18+ , responseHeaders = [(ContentType, B8.pack "text/html")]
19− , body = index
19+ , responseBody = index
20 }
20 }
21
21
22−postResponse :: (forall a. Enumerator a) -> IO Response
22+postResponse :: Enumerator -> IO Response
23postResponse rb = return Response
23postResponse rb = return Response
24 { status = Status200
24 { status = Status200
25− , headers = [(ContentType, B8.pack "text/plain")]
25+ , responseHeaders = [(ContentType, B8.pack "text/plain")]
26− , body = Right $ fromLBS' $ toLBS rb
26+ , responseBody = Right $ fromLBS' $ toLBS rb
27 }
27 }
28
28
29index :: Either FilePath a
29index :: Either FilePath a
test.hs+1−1
@@ -18,7 +18,7 @@ indexResponse = return Response
18 , responseBody = index
18 , responseBody = index
19 }
19 }
20
20
21−postResponse :: (forall a. Enumerator a) -> IO Response
21+postResponse :: Enumerator -> IO Response
22postResponse rb = return Response
22postResponse rb = return Response
23 { status = Status200
23 { status = Status200
24 , responseHeaders = [(ContentType, B8.pack "text/plain")]
24 , responseHeaders = [(ContentType, B8.pack "text/plain")]