on master help sign in
Switched Enumerator to newtype
commitb4470e7161b5bd6e55f89feef08c55739be0ca54 authorMichael Snoyman <michael@snoyman.com> dateWed 24 Feb 2010, 11:42 (17 years ago) parentb4be853
5 files changed+41−36
Network/Wai.hs+6−4 Network/Wai/Enumerator.hs+22−20 Network/Wai/Handler/SimpleServer.hs+7−6 lazy-test.hs+5−5 test.hs+1−1
Network/Wai.hs+6−4
@@ -56,7 +56,7 @@ module Network.Wai
5656 , statusCode
5757 , statusMessage
5858 -- * Enumerator
59− , Enumerator
59+ , Enumerator (..)
6060 -- * WAI interface
6161 , Request (..)
6262 , Response (..)
@@ -341,9 +341,11 @@ statusMessage (Status _ m) = m
341341-- 'Enumerator' may only be called once. While this requirement puts a bit of a
342342-- strain on the caller in some situations, it saves a large amount of
343343-- complication- and thus performance- on the producer.
344−type Enumerator a = (a -> B.ByteString -> IO (Either a a))
344+newtype Enumerator = Enumerator { runEnumerator :: forall a.
345+ (a -> B.ByteString -> IO (Either a a))
345346 -> a
346347 -> IO (Either a a)
348+}
347349
348350-- | Information on the request sent by the client. This abstracts away the
349351-- details of the underlying implementation.
@@ -361,7 +363,7 @@ data Request = Request
361363 , serverPort :: Int
362364 , requestHeaders :: [(RequestHeader, B.ByteString)]
363365 , urlScheme :: UrlScheme
364− , requestBody :: forall a. Enumerator a
366+ , requestBody :: Enumerator
365367 , errorHandler :: String -> IO ()
366368 -- | The client\'s host information.
367369 , remoteHost :: B.ByteString
@@ -374,7 +376,7 @@ data Response = Response
374376 -- files from the disk. This datatype facilitates this optimization; if
375377 -- 'Left' is returns, the server will send the file from the disk by whatever
376378 -- means it wishes. If 'Right', it will call the 'Enumerator'.
377− , responseBody :: forall a. Either FilePath (Enumerator a)
379+ , responseBody :: Either FilePath Enumerator
378380 }
379381
380382type Application = Request -> IO Response
Network/Wai/Enumerator.hs+22−20
@@ -17,7 +17,7 @@ module Network.Wai.Enumerator
1717 , fromEitherFile
1818 ) where
1919
20−import Network.Wai (Enumerator)
20+import Network.Wai (Enumerator (..))
2121import qualified Data.ByteString.Lazy as L
2222import qualified Data.ByteString as B
2323import System.IO.Unsafe (unsafeInterleaveIO)
@@ -28,13 +28,13 @@ import Control.Concurrent.MVar
2828
2929-- | Performs a specified conversion on each 'B.ByteString' output by an
3030-- enumerator.
31−mapE :: (B.ByteString -> B.ByteString) -> Enumerator a -> Enumerator a
32−mapE f e iter = e iter' where
33− iter' a = iter a . f
31+mapE :: (B.ByteString -> B.ByteString) -> Enumerator -> Enumerator
32+mapE f (Enumerator e) = Enumerator $ \iter -> e (iter' iter) where
33+ iter' iter a = iter a . f
3434
3535-- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
3636-- normal lazy I/O warnings apply.
37−toLBS :: Enumerator () -> IO L.ByteString
37+toLBS :: Enumerator -> IO L.ByteString
3838toLBS e = do
3939 source <- toSource e
4040 L.fromChunks `fmap` helper source
@@ -48,26 +48,27 @@ toLBS e = do
4848 return $ x' : xs
4949
5050-- | This function safely converts a lazy bytestring into an enumerator.
51−fromLBS :: L.ByteString -> (forall a. Enumerator a)
52−fromLBS lbs iter a0 = helper a0 $ L.toChunks lbs where
53− helper a [] = return $ Right a
54− helper a (x:xs) = do
51+fromLBS :: L.ByteString -> Enumerator
52+fromLBS lbs = Enumerator $ \iter a0 -> helper iter a0 $ L.toChunks lbs where
53+ helper _ a [] = return $ Right a
54+ helper iter a (x:xs) = do
5555 ea <- iter a x
5656 case ea of
5757 Left a' -> return $ Left a'
58− Right a' -> helper a' xs
58+ Right a' -> helper iter a' xs
5959
6060-- | Same as 'fromLBS', but the lazy bytestring is in the IO monad. This allows
6161-- you to lazily read a file into memory, perform some mapping on the data and
6262-- convert it into an enumerator.
63−fromLBS' :: IO L.ByteString -> (forall a. Enumerator a)
64−fromLBS' lbs' iter a0 = lbs' >>= \lbs -> fromLBS lbs iter a0
63+fromLBS' :: IO L.ByteString -> Enumerator
64+fromLBS' lbs' = Enumerator $ \iter a0 -> lbs' >>= \lbs ->
65+ runEnumerator (fromLBS lbs) iter a0
6566
6667-- | A source is a more standard way of accessing data from an 'Enumerator'.
6768-- Each time you call it, it returns the next chunk of data if available, or
6869-- 'Nothing' if the data has been completely consumed.
69−toSource :: Enumerator () -> IO (IO (Maybe B.ByteString))
70−toSource e = do
70+toSource :: Enumerator -> IO (IO (Maybe B.ByteString))
71+toSource (Enumerator e) = do
7172 buffer <- newEmptyMVar
7273 _ <- forkIO $ e (helper buffer) () >> putMVar buffer Nothing
7374 return $ source buffer
@@ -93,8 +94,8 @@ toSource e = do
9394-- | Read a chunk of data from the given 'Handle' at a time. We use
9495-- 'defaultChunkSize' from the bytestring package to determine the largest
9596-- chunk to take.
96−fromHandle :: Handle -> Enumerator a
97−fromHandle h iter a = do
97+fromHandle :: Handle -> Enumerator
98+fromHandle h = Enumerator $ \iter a -> do
9899 eof <- hIsEOF h
99100 if eof
100101 then return $ Right a
@@ -103,14 +104,15 @@ fromHandle h iter a = do
103104 ea' <- iter a bs
104105 case ea' of
105106 Left a' -> return $ Left a'
106− Right a' -> fromHandle h iter a'
107+ Right a' -> runEnumerator (fromHandle h) iter a'
107108
108109-- | A little wrapper around 'fromHandle' which first opens a file for reading.
109−fromFile :: FilePath -> Enumerator a
110−fromFile fp iter a0 = withBinaryFile fp ReadMode $ \h -> fromHandle h iter a0
110+fromFile :: FilePath -> Enumerator
111+fromFile fp = Enumerator $ \iter a0 -> withBinaryFile fp ReadMode $ \h ->
112+ runEnumerator (fromHandle h) iter a0
111113
112114-- | Since the response body is defined as an 'Either' 'FilePath' 'Enumerator',
113115-- this function simply reduces the whole operator to an enumerator. This can
114116-- be convenient for server implementations not optimizing file sending.
115−fromEitherFile :: Either FilePath (Enumerator a) -> Enumerator a
117+fromEitherFile :: Either FilePath Enumerator -> Enumerator
116118fromEitherFile = either fromFile id
Network/Wai/Handler/SimpleServer.hs+7−6
@@ -134,10 +134,11 @@ parseRequest port lines' handle remoteHost' = do
134134 , remoteHost = B8.pack remoteHost'
135135 }
136136
137−requestBodyHandle :: Handle -> MVar Int -> Enumerator a
138−requestBodyHandle h mlen iter accum = modifyMVar mlen (helper accum) where
139− helper a 0 = return (0, Right a)
140− helper a len = do
137+requestBodyHandle :: Handle -> MVar Int -> Enumerator
138+requestBodyHandle h mlen = Enumerator $ \iter accum ->
139+ modifyMVar mlen (helper iter accum) where
140+ helper _ a 0 = return (0, Right a)
141+ helper iter a len = do
141142 let maxChunkSize = 1024
142143 bs <- BS.hGet h $ min len maxChunkSize
143144 let newLen = len - BS.length bs
@@ -145,7 +146,7 @@ requestBodyHandle h mlen iter accum = modifyMVar mlen (helper accum) where
145146 ea' <- iter a bs
146147 case ea' of
147148 Left a' -> return (newLen, Left a')
148− Right a' -> helper a' newLen
149+ Right a' -> helper iter a' newLen
149150
150151parseFirst :: (StringLike s, MonadFailure InvalidRequest m) =>
151152 s
@@ -170,7 +171,7 @@ sendResponse h res = do
170171 BS.hPut h $ SL.pack "\r\n"
171172 case responseBody res of
172173 Left fp -> unsafeSendFile h fp
173− Right enum -> enum myPut h >> return ()
174+ Right (Enumerator enum) -> enum myPut h >> return ()
174175 where
175176 myPut _ bs = do
176177 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
1515indexResponse :: IO Response
1616indexResponse = return Response
1717 { status = Status200
18− , headers = [(ContentType, B8.pack "text/html")]
19− , body = index
18+ , responseHeaders = [(ContentType, B8.pack "text/html")]
19+ , responseBody = index
2020 }
2121
22−postResponse :: (forall a. Enumerator a) -> IO Response
22+postResponse :: Enumerator -> IO Response
2323postResponse rb = return Response
2424 { status = Status200
25− , headers = [(ContentType, B8.pack "text/plain")]
26− , body = Right $ fromLBS' $ toLBS rb
25+ , responseHeaders = [(ContentType, B8.pack "text/plain")]
26+ , responseBody = Right $ fromLBS' $ toLBS rb
2727 }
2828
2929index :: Either FilePath a
test.hs+1−1
@@ -18,7 +18,7 @@ indexResponse = return Response
1818 , responseBody = index
1919 }
2020
21−postResponse :: (forall a. Enumerator a) -> IO Response
21+postResponse :: Enumerator -> IO Response
2222postResponse rb = return Response
2323 { status = Status200
2424 , responseHeaders = [(ContentType, B8.pack "text/plain")]