on master help sign in
Most of wai-extra rewritten
commit02ae797b60bd9b454a5712509e05799449b43c6d authorMichael Snoyman <michael@snoyman.com> dateThu 22 May 2014, 16:29 (12 years ago) parentfdcc6aa
7 files changed+171−134
wai-extra/Network/Wai/Handler/CGI.hs+54−51 wai-extra/Network/Wai/Middleware/Autohead.hs+5−10 wai-extra/Network/Wai/Middleware/CleanPath.hs+3−3 wai-extra/Network/Wai/Middleware/RequestLogger.hs+35−30 wai-extra/Network/Wai/Parse.hs+48−16 wai-extra/test/WaiExtraTest.hs+25−22 wai-extra/wai-extra.cabal+1−2
wai-extra/Network/Wai/Handler/CGI.hs+54−51
@@ -13,8 +13,8 @@ module Network.Wai.Handler.CGI
1313import Network.Wai
1414import Network.Wai.Internal
1515import Network.Socket (getAddrInfo, addrAddress)
16+import Data.IORef
1617import Data.Maybe (fromMaybe)
17−import Control.Exception (mask)
1818import qualified Data.ByteString.Char8 as B
1919import qualified Data.ByteString.Lazy as L
2020import Control.Arrow ((***))
@@ -22,15 +22,17 @@ import Data.Char (toLower)
2222import qualified System.IO
2323import qualified Data.String as String
2424import Data.Monoid (mconcat, mempty)
25−import Blaze.ByteString.Builder (fromByteString, toLazyByteString)
25+import Blaze.ByteString.Builder (fromByteString, toLazyByteString, flush)
2626import Blaze.ByteString.Builder.Char8 (fromChar, fromString)
27−import Control.Monad.IO.Class (liftIO)
2827import Data.ByteString.Lazy.Internal (defaultChunkSize)
2928import System.IO (Handle)
3029import Network.HTTP.Types (Status (..))
3130import qualified Network.HTTP.Types as H
3231import qualified Data.CaseInsensitive as CI
3332import Data.Monoid (mappend)
33+import qualified Data.Streaming.Blaze as Blaze
34+import Data.Function (fix)
35+import Control.Monad (unless, void)
3436
3537#if WINDOWS
3638import System.Environment (getEnvironment)
@@ -74,7 +76,7 @@ runSendfile sf app = do
7476-- stick with 'run' or 'runSendfile'.
7577runGeneric
7678 :: [(String, String)] -- ^ all variables
77− -> (Int -> Source IO B.ByteString) -- ^ responseBody of input
79+ -> (Int -> IO (IO B.ByteString)) -- ^ responseBody of input
7880 -> (B.ByteString -> IO ()) -- ^ destination for output
7981 -> Maybe B.ByteString -- ^ does the server support the X-Sendfile header?
8082 -> Application
@@ -96,40 +98,48 @@ runGeneric vars inputH outputH xsendfile app = do
9698 "https" -> True
9799 _ -> False
98100 addrs <- getAddrInfo Nothing (Just remoteHost') Nothing
101+ requestBody' <- inputH contentLength
99102 let addr =
100103 case addrs of
101104 a:_ -> addrAddress a
102105 [] -> error $ "Invalid REMOTE_ADDR or REMOTE_HOST: " ++ remoteHost'
103− mask $ \restore -> do
104− let reqHeaders = map (cleanupVarName *** B.pack) vars
105− env = Request
106− { requestMethod = rmethod
107− , rawPathInfo = B.pack pinfo
108− , pathInfo = H.decodePathSegments $ B.pack pinfo
109− , rawQueryString = B.pack qstring
110− , queryString = H.parseQuery $ B.pack qstring
111− , requestHeaders = reqHeaders
112− , isSecure = isSecure'
113− , remoteHost = addr
114− , httpVersion = H.http11 -- FIXME
115− , requestBody = inputH contentLength
116− , vault = mempty
117− , requestBodyLength = KnownLength $ fromIntegral contentLength
118− , requestHeaderHost = lookup "host" reqHeaders
119− , requestHeaderRange = lookup "range" reqHeaders
120− }
121− -- FIXME worry about exception?
122− res <- restore $ app env
106+ reqHeaders = map (cleanupVarName *** B.pack) vars
107+ env = Request
108+ { requestMethod = rmethod
109+ , rawPathInfo = B.pack pinfo
110+ , pathInfo = H.decodePathSegments $ B.pack pinfo
111+ , rawQueryString = B.pack qstring
112+ , queryString = H.parseQuery $ B.pack qstring
113+ , requestHeaders = reqHeaders
114+ , isSecure = isSecure'
115+ , remoteHost = addr
116+ , httpVersion = H.http11 -- FIXME
117+ , requestBody = requestBody'
118+ , vault = mempty
119+ , requestBodyLength = KnownLength $ fromIntegral contentLength
120+ , requestHeaderHost = lookup "host" reqHeaders
121+ , requestHeaderRange = lookup "range" reqHeaders
122+ }
123+ void $ app env $ \res ->
123124 case (xsendfile, res) of
124− (Just sf, ResponseFile s hs fp Nothing) ->
125− restore $ mapM_ outputH $ L.toChunks $ toLazyByteString $ sfBuilder s hs sf fp
125+ (Just sf, ResponseFile s hs fp Nothing) -> do
126+ mapM_ outputH $ L.toChunks $ toLazyByteString $ sfBuilder s hs sf fp
127+ return ResponseReceived
126128 _ -> do
127− let (s, hs, wb) = responseToSource res
128− wb $ \b ->
129− let src = do
130− yield (Chunk $ headers s hs `mappend` fromChar '\n')
131− b
132− in src $$ builderSink
129+ let (s, hs, wb) = responseToStream res
130+ (blazeRecv, blazeFinish) <- Blaze.newBlazeRecv Blaze.defaultStrategy
131+ wb $ \b -> do
132+ let sendBuilder builder = do
133+ popper <- blazeRecv builder
134+ fix $ \loop -> do
135+ bs <- popper
136+ unless (B.null bs) $ do
137+ outputH bs
138+ loop
139+ sendBuilder $ headers s hs `mappend` fromChar '\n'
140+ b sendBuilder (sendBuilder flush)
141+ blazeFinish >>= maybe (return ()) outputH
142+ return ResponseReceived
133143 where
134144 headers s hs = mconcat (map header $ status s : map header' (fixHeaders hs))
135145 status (Status i m) = (fromByteString "Status", mconcat
@@ -151,13 +161,6 @@ runGeneric vars inputH outputH xsendfile app = do
151161 , fromByteString sf
152162 , fromByteString " not supported"
153163 ]
154− bsSink = await >>= maybe (return ()) push
155− push (Chunk bs) = do
156− liftIO $ outputH bs
157− bsSink
158− -- FIXME actually flush?
159− push Flush = bsSink
160− builderSink = builderToByteStringFlush =$ bsSink
161164 fixHeaders h =
162165 case lookup "content-type" h of
163166 Nothing -> ("Content-Type", "text/html; charset=utf-8") : h
@@ -176,19 +179,19 @@ cleanupVarName s =
176179 helper' (x:rest) = toLower x : helper' rest
177180 helper' [] = []
178181
179−requestBodyHandle :: Handle -> Int -> Source IO B.ByteString
182+requestBodyHandle :: Handle -> Int -> IO (IO B.ByteString)
180183requestBodyHandle h = requestBodyFunc $ \i -> do
181184 bs <- B.hGet h i
182185 return $ if B.null bs then Nothing else Just bs
183186
184−requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> Source IO B.ByteString
185−requestBodyFunc get =
186− loop
187− where
188− loop 0 = return ()
189− loop count = do
190− mbs <- liftIO $ get $ min count defaultChunkSize
191− let count' = count - maybe 0 B.length mbs
192− case mbs of
193− Nothing -> return ()
194− Just bs -> yield bs >> loop count'
187+requestBodyFunc :: (Int -> IO (Maybe B.ByteString)) -> Int -> IO (IO B.ByteString)
188+requestBodyFunc get count0 = do
189+ ref <- newIORef count0
190+ return $ do
191+ count <- readIORef ref
192+ if count <= 0
193+ then return B.empty
194+ else do
195+ mbs <- get $ min count defaultChunkSize
196+ writeIORef ref $ count - maybe 0 B.length mbs
197+ return $ fromMaybe B.empty mbs
wai-extra/Network/Wai/Middleware/Autohead.hs+5−10
@@ -4,17 +4,12 @@
44module Network.Wai.Middleware.Autohead (autohead) where
55
66import Network.Wai
7−import Network.Wai.Internal
87import Data.Monoid (mempty)
98
109autohead :: Middleware
11−autohead app req
12− | requestMethod req == "HEAD" = do
13− res <- app req { requestMethod = "GET" }
14− let go (ResponseFile s hs _ _) = ResponseBuilder s hs mempty
15− go (ResponseBuilder s hs _) = ResponseBuilder s hs mempty
16− go (ResponseSource s hs _) = ResponseBuilder s hs mempty
17− go (ResponseRaw raw r) = ResponseRaw raw (go r)
18− return (go res)
19− | otherwise = app req
10+autohead app req sendResponse
11+ | requestMethod req == "HEAD" = app req { requestMethod = "GET" } $ \res -> do
12+ let (s, hs, _) = responseToStream res
13+ sendResponse $ responseBuilder s hs mempty
14+ | otherwise = app req sendResponse
2015
wai-extra/Network/Wai/Middleware/CleanPath.hs+3−3
@@ -14,10 +14,10 @@ cleanPath :: ([Text] -> Either B.ByteString [Text])
1414 -> B.ByteString
1515 -> ([Text] -> Application)
1616 -> Application
17−cleanPath splitter prefix app env =
17+cleanPath splitter prefix app env sendResponse =
1818 case splitter $ pathInfo env of
19− Right pieces -> app pieces env
20− Left p -> return
19+ Right pieces -> app pieces env sendResponse
20+ Left p -> sendResponse
2121 $ responseLBS status301
2222 [("Location", mconcat [prefix, p, suffix])]
2323 $ L.empty
wai-extra/Network/Wai/Middleware/RequestLogger.hs+35−30
@@ -93,23 +93,21 @@ mkRequestLogger RequestLoggerSettings{..} = do
9393 return $ customMiddleware callback getdate formatter
9494
9595apacheMiddleware :: ApacheLoggerActions -> Middleware
96−apacheMiddleware ala app req = do
97− res <- app req
96+apacheMiddleware ala app req sendResponse = app req $ \res -> do
9897 let msize = lookup "content-length" (responseHeaders res) >>= readInt'
9998 readInt' bs =
10099 case S8.readInteger bs of
101100 Just (i, "") -> Just i
102101 _ -> Nothing
103102 apacheLogger ala req (responseStatus res) msize
104− return res
103+ sendResponse res
105104
106105customMiddleware :: Callback -> IO ZonedDate -> OutputFormatter -> Middleware
107−customMiddleware cb getdate formatter app req = do
108− res <- app req
106+customMiddleware cb getdate formatter app req sendResponse = app req $ \res -> do
109107 date <- liftIO getdate
110108 -- We use Nothing for the response size since we generally don't know it
111109 liftIO $ cb $ formatter date req (responseStatus res) Nothing
112− return res
110+ sendResponse res
113111
114112-- | Production request logger middleware.
115113-- Implemented on top of "logCallback", but prints to 'stdout'
@@ -186,13 +184,18 @@ ansiColor color bs = [
186184detailedMiddleware' :: Callback
187185 -> IO (BS.ByteString -> [BS.ByteString])
188186 -> Middleware
189−detailedMiddleware' cb getAddColor app req = do
187+detailedMiddleware' cb getAddColor app req sendResponse = do
190188 let mlen = lookup "content-length" (requestHeaders req) >>= readInt
191189 (req', body) <-
192190 case mlen of
193191 -- log the request body if it is small
194192 Just len | len <= 2048 -> do
195− body <- requestBody req C.$$ CL.consume
193+ let loop front = do
194+ bs <- requestBody req
195+ if S8.null bs
196+ then return $ front []
197+ else loop $ front . (bs:)
198+ body <- loop id
196199 -- logging the body here consumes it, so fill it back up
197200 -- obviously not efficient, but this is the development logger
198201 --
@@ -204,14 +207,10 @@ detailedMiddleware' cb getAddColor app req = do
204207 -- implementation ensures that each chunk is only returned
205208 -- once.
206209 ichunks <- newIORef body
207− let rbody = do
208− chunks <- readIORef ichunks
210+ let rbody = atomicModifyIORef ichunks $ \chunks ->
209211 case chunks of
210− [] -> return ()
211− x:xs -> do
212− writeIORef ichunks xs
213− C.yield x
214− rbody
212+ [] -> ([], S8.empty)
213+ x:y -> (y, x)
215214 let req' = req { requestBody = rbody }
216215 return (req', body)
217216 _ -> return (req, [])
@@ -237,20 +236,20 @@ detailedMiddleware' cb getAddColor app req = do
237236 , "\n"
238237 ]
239238
240− rsp <- app req'
241−
242− -- log the status of the response
243− -- this is color coordinated with the request logging
244− -- also includes the request path to connect it to the request
245− liftIO $ cb $ mconcat $ map toLogStr $
246− addColor "Status: " ++ statusBS rsp ++
247− [ " "
248− , msgBS rsp
249− , ". "
250− , rawPathInfo req -- if you need help matching the 2 logging statements
251− , "\n"
252− ]
253− return rsp
239+ app req' $ \rsp -> do
240+
241+ -- log the status of the response
242+ -- this is color coordinated with the request logging
243+ -- also includes the request path to connect it to the request
244+ liftIO $ cb $ mconcat $ map toLogStr $
245+ addColor "Status: " ++ statusBS rsp ++
246+ [ " "
247+ , msgBS rsp
248+ , ". "
249+ , rawPathInfo req -- if you need help matching the 2 logging statements
250+ , "\n"
251+ ]
252+ sendResponse rsp
254253 where
255254 paramsToBS prefix params =
256255 if null params then ""
@@ -259,7 +258,13 @@ detailedMiddleware' cb getAddColor app req = do
259258 allPostParams body =
260259 case getRequestBodyType req of
261260 Nothing -> return ([], [])
262− Just rbt -> CL.sourceList body C.$$ sinkRequestBody lbsBackEnd rbt
261+ Just rbt -> do
262+ ichunks <- newIORef body
263+ let rbody = atomicModifyIORef ichunks $ \chunks ->
264+ case chunks of
265+ [] -> ([], S8.empty)
266+ x:y -> (y, x)
267+ sinkRequestBody lbsBackEnd rbt rbody
263268
264269 emptyGetParam :: (BS.ByteString, Maybe BS.ByteString) -> (BS.ByteString, BS.ByteString)
265270 emptyGetParam (k, Just v) = (k,v)
wai-extra/Network/Wai/Parse.hs+48−16
@@ -11,8 +11,10 @@ module Network.Wai.Parse
1111 , parseRequestBody
1212 , RequestBodyType (..)
1313 , getRequestBodyType
14− , sinkRequestBody
14+ , sinkRequestBody -- FIXME bad name
15+ {- FIXME
1516 , conduitRequestBody
17+ -}
1618 , BackEnd
1719 , lbsBackEnd
1820 , tempFileBackEnd
@@ -38,7 +40,7 @@ import qualified Data.ByteString.Char8 as S8
3840import Data.Word (Word8)
3941import Data.Maybe (fromMaybe)
4042import Data.List (sortBy)
41−import Data.Function (on)
43+import Data.Function (on, fix)
4244import System.Directory (removeFile, getTemporaryDirectory)
4345import System.IO (hClose, openBinaryTempFile)
4446import Network.Wai
@@ -49,6 +51,7 @@ import Control.Monad (when, unless)
4951import Control.Monad.Trans.Class (lift)
5052import Control.Monad.Trans.Resource (allocate, release, register, InternalState, runInternalState)
5153import Data.Void (Void)
54+import Data.IORef
5255
5356breakDiscard :: Word8 -> S.ByteString -> (S.ByteString, S.ByteString)
5457breakDiscard w s =
@@ -80,15 +83,22 @@ parseHttpAccept = map fst
8083 _ -> 1.0
8184
8285-- | Store uploaded files in memory
83−lbsBackEnd :: Monad m => ignored1 -> ignored2 -> Sink S.ByteString m L.ByteString
84−lbsBackEnd _ _ = fmap L.fromChunks CL.consume
86+lbsBackEnd :: Monad m => ignored1 -> ignored2 -> m S.ByteString -> m L.ByteString
87+lbsBackEnd _ _ popper =
88+ loop id
89+ where
90+ loop front = do
91+ bs <- popper
92+ if S.null bs
93+ then return $ L.fromChunks $ front []
94+ else loop $ front . (bs:)
8595
8696-- | Save uploaded files on disk as temporary files
8797--
8898-- Note: starting with version 2.0, removal of temp files is registered with
8999-- the provided @InternalState@. It is the responsibility of the caller to
90100-- ensure that this @InternalState@ gets cleaned up.
91−tempFileBackEnd :: InternalState -> ignored1 -> ignored2 -> Sink S.ByteString IO FilePath
101+tempFileBackEnd :: InternalState -> ignored1 -> ignored2 -> IO S.ByteString -> IO FilePath
92102tempFileBackEnd = tempFileBackEndOpts getTemporaryDirectory "webenc.buf"
93103
94104-- | Same as 'tempFileSink', but use configurable temp folders and patterns.
@@ -97,14 +107,19 @@ tempFileBackEndOpts :: IO FilePath -- ^ get temporary directory
97107 -> InternalState
98108 -> ignored1
99109 -> ignored2
100− -> Sink S.ByteString IO FilePath
101−tempFileBackEndOpts getTmpDir pattern internalState _ _ = do
110+ -> IO S.ByteString
111+ -> IO FilePath
112+tempFileBackEndOpts getTmpDir pattern internalState _ _ popper = do
102113 (key, (fp, h)) <- flip runInternalState internalState $ allocate (do
103114 tempDir <- getTmpDir
104115 openBinaryTempFile tempDir pattern) (\(_, h) -> hClose h)
105116 _ <- runInternalState (register $ removeFile fp) internalState
106− CB.sinkHandle h
107− lift $ release key
117+ fix $ \loop -> do
118+ bs <- popper
119+ unless (S.null bs) $ do
120+ S.hPut h bs
121+ loop
122+ release key
108123 return fp
109124
110125-- | Information on an uploaded file.
@@ -121,11 +136,12 @@ type Param = (S.ByteString, S.ByteString)
121136-- | Post parameter name and associated file information.
122137type File y = (S.ByteString, FileInfo y)
123138
124−-- | A file uploading backend. Takes the parameter name, file name, and content
125−-- type, and returns a `Sink` for storing the contents.
139+-- | A file uploading backend. Takes the parameter name, file name, and a
140+-- stream of data.
126141type BackEnd a = S.ByteString -- ^ parameter name
127142 -> FileInfo ()
128− -> Sink S.ByteString IO a
143+ -> IO S.ByteString
144+ -> IO a
129145
130146data RequestBodyType = UrlEncoded | Multipart S.ByteString
131147
@@ -168,16 +184,29 @@ parseRequestBody :: BackEnd y
168184parseRequestBody s r =
169185 case getRequestBodyType r of
170186 Nothing -> return ([], [])
171− Just rbt -> fmap partitionEithers $ requestBody r $$ conduitRequestBody s rbt =$ CL.consume
187+ Just rbt -> sinkRequestBody s rbt (requestBody r)
172188
173189sinkRequestBody :: BackEnd y
174190 -> RequestBodyType
175− -> Sink S.ByteString IO ([Param], [File y])
176−sinkRequestBody s r = fmap partitionEithers $ conduitRequestBody s r =$ CL.consume
191+ -> IO S.ByteString
192+ -> IO ([Param], [File y])
193+sinkRequestBody s r body = do
194+ ref <- newIORef (id, id)
195+ let add x = atomicModifyIORef ref $ \(y, z) ->
196+ case x of
197+ Left y' -> ((y . (y':), z), ())
198+ Right z' -> ((y, z . (z':)), ())
199+ conduitRequestBody s r body add
200+ (x, y) <- readIORef ref
201+ return (x [], y [])
177202
178203conduitRequestBody :: BackEnd y
179204 -> RequestBodyType
180− -> Conduit S.ByteString IO (Either Param (File y))
205+ -> IO S.ByteString
206+ -> (Either Param (File y) -> IO ())
207+ -> IO ()
208+conduitRequestBody = error "conduitRequestBody"
209+{-
181210conduitRequestBody _ UrlEncoded = do
182211 -- NOTE: in general, url-encoded data will be in a single chunk.
183212 -- Therefore, I'm optimizing for the usual case by sticking with
@@ -257,6 +286,7 @@ parsePieces sink bound =
257286 parsePair s =
258287 let (x, y) = breakDiscard 58 s -- colon
259288 in (x, S.dropWhile (== 32) y) -- space
289+-}
260290
261291data Bound = FoundBound S.ByteString S.ByteString
262292 | NoBound
@@ -286,6 +316,7 @@ findBound b bs = handleBreak $ Search.breakOn b bs
286316 | S.index b x == S.index bs y = mismatch xs ys
287317 | otherwise = True
288318
319+{- FIXME
289320sinkTillBound' :: S.ByteString
290321 -> S.ByteString
291322 -> FileInfo ()
@@ -348,6 +379,7 @@ sinkTillBound bound iter seed0 =
348379 ignoreTerm = await' >>= maybe (return ()) (\x -> yield' x >> ignoreTerm)
349380 await' = NeedInput (return . Just) (const $ return Nothing)
350381 yield' = HaveOutput (return ()) (return ())
382+-}
351383
352384parseAttrs :: S.ByteString -> [(S.ByteString, S.ByteString)]
353385parseAttrs = map go . S.split 59 -- semicolon
wai-extra/test/WaiExtraTest.hs+25−22
@@ -30,9 +30,6 @@ import Network.Wai.Middleware.AcceptOverride
3030import Network.Wai.Middleware.RequestLogger
3131import Codec.Compression.GZip (decompress)
3232
33−import qualified Data.Conduit as C
34−import qualified Data.Conduit.List as CL
35−import Data.Conduit.Binary (sourceHandle)
3633import Control.Monad.IO.Class (liftIO)
3734import Data.Maybe (fromMaybe)
3835import Network.HTTP.Types (parseSimpleQuery, status200)
@@ -128,7 +125,13 @@ parseRequestBody' :: BackEnd file
128125parseRequestBody' sink (SRequest req bod) =
129126 case getRequestBodyType req of
130127 Nothing -> return ([], [])
131− Just rbt -> CL.sourceList (L.toChunks bod) C.$$ sinkRequestBody sink rbt
128+ Just rbt -> do
129+ ref <- I.newIORef $ L.toChunks bod
130+ let rb = I.atomicModifyIORef ref $ \chunks ->
131+ case chunks of
132+ [] -> ([], S.empty)
133+ x:y -> (y, x)
134+ sinkRequestBody sink rbt rb
132135
133136caseParseRequestBody :: Assertion
134137caseParseRequestBody =
@@ -295,7 +298,7 @@ caseTakeLine = do
295298-}
296299
297300jsonpApp :: Application
298−jsonpApp = jsonp $ const $ return $ responseLBS
301+jsonpApp = jsonp $ \_ f -> f $ responseLBS
299302 status200
300303 [("Content-Type", "application/json")]
301304 "{\"foo\":\"bar\"}"
@@ -324,7 +327,7 @@ caseJsonp = flip runSession jsonpApp $ do
324327 assertBody "{\"foo\":\"bar\"}" sres3
325328
326329gzipApp :: Application
327−gzipApp = gzip def $ const $ return $ responseLBS status200
330+gzipApp = gzip def $ \_ f -> f $ responseLBS status200
328331 [("Content-Type", "text/plain")]
329332 "test"
330333
@@ -332,7 +335,7 @@ gzipApp = gzip def $ const $ return $ responseLBS status200
332335-- that the compression is skipped based on the presence of
333336-- the Content-Encoding header.
334337gzipPrecompressedApp :: Application
335−gzipPrecompressedApp = gzip def $ const $ return $ responseLBS status200
338+gzipPrecompressedApp = gzip def $ \_ f -> f $ responseLBS status200
336339 [("Content-Type", "text/plain"), ("Content-Encoding", "gzip")]
337340 "test"
338341
@@ -380,8 +383,8 @@ caseGzipBypassPre = flip runSession gzipPrecompressedApp $ do
380383 assertBody "test" sres1 -- the body is not actually compressed
381384
382385vhostApp1, vhostApp2, vhostApp :: Application
383−vhostApp1 = const $ return $ responseLBS status200 [] "app1"
384−vhostApp2 = const $ return $ responseLBS status200 [] "app2"
386+vhostApp1 _ f = f $ responseLBS status200 [] "app1"
387+vhostApp2 _ f = f $ responseLBS status200 [] "app2"
385388vhostApp = vhost
386389 [ ((== Just "foo.com") . lookup "host" . requestHeaders, vhostApp1)
387390 ]
@@ -400,7 +403,7 @@ caseVhost = flip runSession vhostApp $ do
400403 assertBody "app2" sres2
401404
402405autoheadApp :: Application
403−autoheadApp = autohead $ const $ return $ responseLBS status200
406+autoheadApp = autohead $ \_ f -> f $ responseLBS status200
404407 [("Foo", "Bar")] "body"
405408
406409caseAutohead :: Assertion
@@ -418,7 +421,7 @@ caseAutohead = flip runSession autoheadApp $ do
418421 assertBody "" sres2
419422
420423moApp :: Application
421−moApp = methodOverride $ \req -> return $ responseLBS status200
424+moApp = methodOverride $ \req f -> f $ responseLBS status200
422425 [("Method", requestMethod req)] ""
423426
424427caseMethodOverride :: Assertion
@@ -442,7 +445,7 @@ caseMethodOverride = flip runSession moApp $ do
442445 assertHeader "Method" "PUT" sres3
443446
444447mopApp :: Application
445−mopApp = methodOverridePost $ \req -> return $ responseLBS status200 [("Method", requestMethod req)] ""
448+mopApp = methodOverridePost $ \req f -> f $ responseLBS status200 [("Method", requestMethod req)] ""
446449
447450caseMethodOverridePost :: Assertion
448451caseMethodOverridePost = flip runSession mopApp $ do
@@ -468,7 +471,7 @@ caseMethodOverridePost = flip runSession mopApp $ do
468471 assertHeader "Method" "POST" sres4
469472
470473aoApp :: Application
471−aoApp = acceptOverride $ \req -> return $ responseLBS status200
474+aoApp = acceptOverride $ \req f -> f $ responseLBS status200
472475 [("Accept", fromMaybe "" $ lookup "Accept" $ requestHeaders req)] ""
473476
474477caseAcceptOverride :: Assertion
@@ -520,7 +523,7 @@ dalvikHelper includeLength = do
520523 case getRequestBodyType request' of
521524 Nothing -> return ([], [])
522525 Just rbt -> withFile "test/requests/dalvik-request" ReadMode $ \h ->
523− sourceHandle h C.$$ sinkRequestBody lbsBackEnd rbt
526+ sinkRequestBody lbsBackEnd rbt $ S.hGetSome h 2048
524527 lookup "scannedTime" params @?= Just "1.298590056748E9"
525528 lookup "geoLong" params @?= Just "0"
526529 lookup "geoLat" params @?= Just "0"
@@ -548,15 +551,15 @@ caseDebugRequestBody = do
548551 postOutput = T.pack $ "POST / :: \nStatus: 200 OK. /\n"
549552 getOutput params' = T.pack $ "GET /location :: \nGET " ++ show params' ++ "\nStatus: 200 OK. /location\n"
550553
551− debugApp output' req = do
552− iactual <- liftIO $ I.newIORef mempty
553− middleware <- liftIO $ mkRequestLogger def
554+ debugApp output' req send = do
555+ iactual <- I.newIORef mempty
556+ middleware <- mkRequestLogger def
554557 { destination = Callback $ \strs -> I.modifyIORef iactual $ (`mappend` strs)
555558 , outputFormat = Detailed False
556559 }
557− res <- middleware (\_req -> return $ responseLBS status200 [ ] "") req
558− actual <- liftIO $ I.readIORef iactual
559− liftIO $ assertEqual "debug" output $ logToBs actual
560+ res <- middleware (\_req f -> f $ responseLBS status200 [ ] "") req send
561+ actual <- I.readIORef iactual
562+ assertEqual "debug" output $ logToBs actual
560563 return res
561564 where
562565 output = TE.encodeUtf8 $ T.toStrict output'
@@ -578,8 +581,8 @@ urlMapTestApp = mapUrls $
578581
579582 where
580583 trivialApp :: S.ByteString -> Application
581− trivialApp name req =
582− return $
584+ trivialApp name req f =
585+ f $
583586 responseLBS
584587 status200
585588 [ ("content-type", "text/plain")
wai-extra/wai-extra.cabal+1−2
@@ -78,7 +78,6 @@ test-suite tests
7878
7979 build-depends: base >= 4 && < 5
8080 , wai-extra
81− , wai-test >= 1.3
8281 , hspec >= 1.3
8382 , HUnit
8483
@@ -102,7 +101,7 @@ test-suite spec
102101 main-is: Spec.hs
103102 other-modules: Network.Wai.TestSpec
104103 build-depends: base >= 4 && < 5
105− , wai-test
104+ , wai-extra
106105 , wai
107106 , hspec >= 1.3
108107 ghc-options: -Wall -Werror